1. 项目背景与核心价值
最近在整理技术文档时发现一个痛点:大多数开源数据库的官方文档虽然内容优质,但缺乏结构化存储方案。每次查阅都需要反复访问官网,既受网络限制又无法实现个性化检索。这个Python爬虫项目就是为了解决这个问题而生——它能自动抓取开源数据库文档,并生成带树形目录结构的JSON文件,最终构建可离线使用的私有知识库。
这个方案特别适合以下场景:
- 技术团队需要建立内部文档中心
- 开发者希望离线查阅高频使用的数据库文档
- 需要对接文档内容进行二次开发(如嵌入帮助系统)
实测抓取MySQL 8.0文档(约3000页HTML)仅需8分钟,生成的JSON文件保留完整目录层级,体积比原始HTML小60%。更妙的是,通过树形结构可以直接定位到任意章节,比在线文档的CTRL+F搜索高效得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 整体架构
采用三层处理流水线:
- 爬取层:基于requests-html处理动态渲染页面
- 解析层:用BeautifulSoup提取文档结构与内容
- 存储层:通过递归算法构建树形JSON
python复制# 核心处理流程示意
def pipeline(url):
html = crawler.fetch(url) # 网页抓取
toc = parser.extract_toc(html) # 目录解析
tree = builder.build_tree(toc) # 树形构建
saver.to_json(tree, 'output.json') # 持久化存储
2.2 关键技术选型
- 动态渲染:相比静态爬取工具(如Scrapy),选用requests-html能更好处理Vue/React编写的现代文档站
- 目录识别:通过XPath定位
<nav>元素,配合CSS类名特征识别(如.sidebar、.toc-wrapper) - 树形构建:递归处理嵌套的
<ul>列表,深度优先遍历生成JSON节点
注意:部分文档站会限制爬虫频率,建议设置
time.sleep(random.uniform(1,3))模拟人工操作
3. 核心实现细节
3.1 智能目录识别算法
通过分析20+主流数据库文档站,总结出目录结构的通用匹配规则:
python复制def detect_toc(html):
# 优先尝试常见CSS选择器
selectors = [
'nav.toc',
'div.sidebar ul',
'#toc .tree',
'aside > ol'
]
for selector in selectors:
if html.find(selector):
return parse_nested_list(html.find(selector))
# 兜底方案:通过<h2>~<h6>标签重建目录
return rebuild_toc_from_headings(html)
3.2 树形JSON构建
关键数据结构设计:
json复制{
"title": "Chapter 1",
"url": "/docs/chapter1",
"children": [
{
"title": "Section 1.1",
"anchor": "#section1.1",
"content": "..."
}
]
}
递归处理逻辑要点:
- 遇到
<li>元素时创建新节点 - 嵌套的
<ul>触发递归调用 - 提取
href和title作为节点属性
3.3 内容去噪优化
文档正文需要特殊处理:
- 移除广告位
div.ad-wrapper - 过滤免责声明
.legal-notice - 保留代码块但压缩空白字符
- 转换Markdown格式的表格为JSON数组
4. 实战案例:MySQL文档抓取
4.1 配置示例
python复制config = {
"entry_url": "https://dev.mysql.com/doc/refman/8.0/en/",
"output_file": "mysql_docs.json",
"max_depth": 4, # 限制目录层级深度
"include_content": True # 是否抓取正文
}
4.2 常见问题处理
-
反爬机制触发:
- 症状:返回403状态码或验证码页面
- 解决方案:轮换User-Agent + 代理IP池
python复制headers = { 'User-Agent': random.choice(user_agents), 'Accept-Language': 'en-US,en;q=0.9' } -
动态加载失败:
- 症状:目录区域显示为空白
- 调试:使用
browser = requests_html.HTMLSession().get(url)渲染JS
-
编码问题:
- 症状:中文内容显示乱码
- 关键代码:
response.encoding = response.apparent_encoding
5. 进阶应用场景
5.1 与RAG技术结合
将生成的JSON文件接入LangChain:
python复制from langchain.document_loaders import JSONLoader
loader = JSONLoader(
file_path='mysql_docs.json',
jq_schema='.children[].content'
)
docs = loader.load()
5.2 自动化更新方案
使用GitHub Actions设置每周自动运行:
yaml复制name: Docs Sync
on:
schedule:
- cron: "0 0 * * 0"
jobs:
crawl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: python crawler.py --config mysql_config.json
- uses: actions/upload-artifact@v3
with:
name: mysql-docs
path: mysql_docs.json
5.3 可视化展示
基于D3.js的树形目录渲染:
javascript复制d3.json("mysql_docs.json").then(data => {
const root = d3.hierarchy(data);
const treeLayout = d3.tree().size([800, 600]);
treeLayout(root);
});
6. 性能优化技巧
-
增量抓取:
- 记录已抓取的URL哈希值
- 二次运行时跳过未修改的页面
-
并行处理:
python复制from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=8) as executor: executor.map(fetch_page, url_list) -
缓存机制:
- 使用DiskCache存储原始HTML
- 配置过期时间(TTL)为7天
7. 避坑指南
-
法律风险规避:
- 优先选择MIT/Apache协议的开源文档
- 避免抓取需登录才能查看的内容
- 在JSON中添加数据来源声明
-
技术陷阱:
- 绝对路径转换:将
href="/doc/..."转为完整URL - 循环引用检测:防止目录节点形成无限循环
- 内存控制:处理大型文档时使用流式JSON生成
- 绝对路径转换:将
-
维护建议:
- 为每个文档站编写独立的解析插件
- 使用Pydantic验证JSON结构
- 添加版本号便于后续更新
这个项目最让我惊喜的是它的扩展性——通过修改配置文件和解析规则,我已经成功适配了PostgreSQL、MongoDB等12种数据库文档。最近正在尝试将其扩展到API文档领域,比如抓取Swagger UI生成的接口文档。如果你有特定文档站的抓取需求,欢迎在评论区交流具体场景。
