批量下载自己的随笔
文档说明:只记录关键地方; 2023-04-15
缘由和意义:
东西都在自己手里,平台倒闭还可以迁移
数据永远在自己手里,发什么都有自主权
可以避免人为刀俎,我为鱼肉的情况
无广告、自主可控、打造自己的品牌、高度定制化
知道为啥大家自己买硬盘了吧 (网盘行业大家有目共睹)
为啥写: 汲取了知识,同时也需要回馈,相互促进的关系
破除知识优越感 !
破除知识诅咒 !
copyleft !
下载自己随笔的脚本
都是使用markdown 写的,下载就方便了很多
办法一: 控制台注入javascript 直接就可以下载
办法二:浏览器扩展注入javascript 直接就可以下载
办法三: python3 直接抓取
办法四: python3 注入javascript
办法五: python3 浏览器扩展注入javascript
办法六: 上述办法混合使用
实操原理图
实操
准备数据源
打开地址: https://i.cnblogs.com/posts
把每页显示随笔数 设置为最大值
打开web控制台,输入如下代码
浏览器右上角 允许下载随笔索引文件
通过查看控制台: 发现还有更简单的 https://i.cnblogs.com/api/posts/list?p=1&cid=&t=1&cfg=0&search=&orderBy=&s=80&scid=
{
function createJSONFile(content, filename) {
let blob = new Blob([JSON.stringify(content)], {type: "application/json"});
let url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = filename;
a.click();
setTimeout(function () {
window.URL.revokeObjectURL(url);
}, 3000);
}
let list = []
let pattern = /^\/posts\/edit;postId=(\d+)$/
let download_url_prefix = 'https://www.cnblogs.com/jingjingxyk/p/';
document.querySelectorAll('.cnb-table table tbody tr').forEach((value, key, parent) => {
let title_link = value.querySelector('.post-title-appendix')
let title_content = value.querySelector('.post-title-content span')
let public_date = value.querySelector('.post-title-suffix-date span')
let editor_operator = value.querySelector('td:nth-child(6) a')
let public_state = value.querySelector('td:nth-child(3)')
let post_id = parseInt(pattern.exec(editor_operator.getAttribute('href'))[1]);
let data = {
"title": title_content.innerText.trim(),
'url': location.protocol + title_link.getAttribute('href'),
'public_date': public_date.getAttribute('title'),
"post_id": post_id,
'public_state': public_state.innerText,
'public_state_code': public_state.innerText === '已发布' ? 1 : 0,
'download_md_link': download_url_prefix + post_id + '.md'
}
list.push(data)
})
let current_page = document.querySelector('.cnb-pager-position .indexes .current')
let current_page_number = parseInt(current_page.innerText)
createJSONFile(list, "i-cnblogs-com-" + current_page_number + ".json")
}
浏览器启用远程调试功能
以chromium 内核浏览器为例
chromium --remote-debugging-port=9221
准备python 运行环境
Pipfile 文件
pipenv update --pypi-mirror https://pypi.tuna.tsinghua.edu.cn/simple
[[source]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
verify_ssl = true
name = "pip_conf_index_global"
[packages]
requests = "*"
playwright = "*"
[dev-packages]
[requires]
python_version = "3.10"
python_full_version = "3.10.6"
准备 python3 脚本
进入 运行环境
pipenv shell
执行
python3 main.py
import os
import json
import glob
import optparse
import datetime
from playwright.async_api import async_playwright
import asyncio
import re
'''
下载 随笔 markdown 文档
原理: 借助浏览器 远程调试 实现
--remote-debugging-port=9221
前提: 用自己的帐号先登录,这样 跳过了登录环节
'''
def get_cnblogs_index(filename):
with open(filename, mode='r', encoding='utf-8') as f:
content = f.read()
result = json.loads(content)
return result
def init():
wildcard = project_dir + '/i-cnblogs-com-*.json'
data_origin_index = list()
for file in glob.glob(wildcard):
res = get_cnblogs_index(file)
for cell in res:
data_origin_index.append(cell)
return data_origin_index
async def get_content(element, SEMAPHORE, browser):
async with SEMAPHORE:
# 文件名中的空格替换为下划线
pattern = r'\s+'
title = re.sub(pattern, '_', element['title'])
filename = project_dir + '/cnblogs/' + title + '.md'
if not os.path.isfile(filename):
context = browser.contexts[0]
page = await context.new_page()
print(element)
await page.goto(element['url'])
content = await page.evaluate(
'''
async (url) => {
//使用 ajax 原始文本
let response = await fetch(url)
let content= await response.text()
return content
}
'''
, element['download_md_link'])
print("{}".format(content))
with open(filename, "w+") as f:
f.write(content)
await asyncio.sleep(3)
await page.close()
async def run(playwright):
data_origin_index = init()
browser = await playwright.chromium.connect_over_cdp("http://localhost:9222")
tasks = [asyncio.create_task(get_content(element, SEMAPHORE, browser)) for element in data_origin_index]
result = await asyncio.gather(*tasks)
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
if __name__ == '__main__':
parser = optparse.OptionParser('usage: %prog [OPTIONS]', description=__doc__)
parser.add_option('--id', help='随笔 ID ', type=int, default=None)
parser.add_option('--max_parallel', help='最大并发数', type=int, default=os.cpu_count())
args = parser.parse_args() # 暂不启用传递命令行参数
project_dir = os.path.abspath(os.path.dirname(__file__) + '/')
if not os.path.isdir(project_dir + '/cnblogs'):
os.mkdir(project_dir + '/cnblogs')
start_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 控制并发数 默认为3 ;不要轻易改大,改大以后,影响服务器性能,可能会惹上麻烦
SEMAPHORE = asyncio.Semaphore(3)
asyncio.run(main())
end_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print("start_time: {} \nend_time: {}".format(start_time, end_time))
小结:
此方法不仅仅可以备份 博客园 随笔文件,更多应用。。。。。
其实就是各种爬虫的玩法 (甚至可以操纵自动登录)
爬虫写的好,牢饭吃到老 爬取个人隐私身份信息是违法的