Python爬虫实战:构建私有知识库 - 一键抓取开源数据库文档并 JSON 化树形目录!

Python爬虫实战:构建私有知识库 - 一键抓取开源数据库文档并 JSON 化树形目录! ㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐ (入门级)福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错排雷指南1️⃣1️⃣ 进阶优化打造企业级 RAG 语料1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface 大家好今天我们要挑战一个极其有趣且实用的项目使用 Python 的requests和BeautifulSoup深入某个开源数据库如类 Redis/PostgreSQL 的在线文档的侧边栏导航把它的“层级目录树”完整扒下来并自动提取每个章节的摘要最终产出一份完美的结构化 JSON 文件。 读完这篇你能获得掌握**递归解析 HTML 嵌套列表ul/li**的高级技巧轻松构建树形数据结构。学会如何处理父子页面的关系精准提取跨页面的连贯信息。获得一份可以直接喂给 LLM大语言模型作为 RAG 知识库索引的高质量数据集。1️⃣ 摘要Abstract本文将演示如何针对由 Sphinx/MkDocs/Docusaurus 等框架生成的静态数据库文档站点开发一个层级目录爬虫。我们将优先抓取全局侧边栏Sidebar获取章节的名称、层级与链接随后并发访问各详情页提取首段简介最终输出为标准的db_docs_tree.json文件。2️⃣ 背景与需求Why 为什么要爬文档树在学习复杂的开源数据库时官方文档往往庞大且碎片化。通过爬虫将目录“树状 JSON 化”不仅可以生成离线版的高清知识导图更是目前构建 AI 智能客服让 AI 知道哪个知识点在哪个章节的基石。 目标字段清单Chapter_Name(章节名)如 “1.2 安装指南”Path(路径)用于记录它的父级关系如 “入门 / 安装”Level(层级)树的深度1级、2级、3级目录…Link(链接)该章节的绝对 URLSummary(简介)进入该链接后正文部分的第一段有效文本p3️⃣ 合规与注意事项必写在开源世界遨游规矩不能忘‍♂️尊重 robots.txt官方文档通常是完全开放索引的但礼貌性检查robots.txt依然是专业爬虫工程师的素养。温和的并发频率控制文档站点多由开源社区用爱发电有些部署在 GitHub Pages 或 ReadTheDocs请务必加入延时time.sleep不要用高并发把人家服务器打宕机了。遵守开源协议抓取的数据仅限个人学习或内部知识库使用若要公开展示请遵循目标站点的 CC-BY-SA 或 MIT 等开源授权协议中立表达合法合规。4️⃣ 技术选型与整体流程What/How️ 技术选型静态解析为主绝大多数文档网站如 ReadTheDocs首次加载时侧边栏 HTML 已经存在于源码中。因此我们不需要沉重的无头浏览器。选型方案Requests (请求) BeautifulSoup (DOM解析) 递归算法 (组装树)。 整体执行流程[Fetch] 请求文档主页 HTML ⬇️ [Parse] 定位 Sidebar(侧边栏)递归解析嵌套的 ul 和 li ⬇️ [Build] 构建包含层级 (Level) 和路径 (Path) 的扁平字典列表 ⬇️ [Fetch Parse] 遍历列表访问每个 Link提取正文首段作为 Summary ⬇️ [Storage] 将丰富后的数据持久化为嵌套或扁平的 JSON 文件5️⃣ 环境准备与依赖安装可复现推荐使用Python 3.9我们需要非常纯粹的基础库组合。 依赖安装pipinstallrequests beautifulsoup4 项目结构db_docs_crawler/ ├── spider.py # 核心代码 ├── utils.py # 辅助工具如清洗 HTML 标签 └── output/ # 存放最终导出的树形文件 └── db_docs_tree.json6️⃣ 核心实现请求层Fetcher文档站点通常没有严格的反爬但由于可能会一次性请求几百个页面**网络稳定性重试机制**至关重要。importrequestsfromrequests.adaptersimportHTTPAdapterfromurllib3.util.retryimportRetrydefget_session():配置具有重试机制的会话sessionrequests.Session()session.headers.update({User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36,Accept:text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8})# 遇到 429(限流) 和 5xx 错误时自动进行指数退避重试retriesRetry(total3,backoff_factor1.5,status_forcelist[429,500,502,503,504])session.mount(http://,HTTPAdapter(max_retriesretries))session.mount(https://,HTTPAdapter(max_retriesretries))returnsession clientget_session()deffetch_page(url):try:resclient.get(url,timeout10)res.raise_for_status()res.encodingutf-8returnres.textexceptExceptionase:print(f❌ 请求失败 [{url}]:{e})returnNone7️⃣ 核心实现解析层Parser这是本项目的灵魂所在文档的侧边栏通常是多重嵌套的ul列表我们需要写一个递归函数来遍历它。(注以下代码基于典型的 Sphinx/MkDocs 侧边栏结构编写实战需视情况微调 DOM 选择器)frombs4importBeautifulSoupfromurllib.parseimporturljoin BASE_DOC_URLhttps://docs.mock-database.com/en/latest/defparse_navigation_tree(html,base_url):解析左侧导航树返回带有层级关系的列表soupBeautifulSoup(html,html.parser)# 假设侧边栏被包裹在 classwy-menu-vertical 或 nav classsidebar 中nav_containersoup.select_one(.sidebar-nav)orsoup.select_one(nav)ifnotnav_container:return[]chapters[]deftraverse_ul(ul_element,current_level1,parent_path):递归遍历 UL 标签ifnotul_element:return# 遍历当前 UL 下的所有 LIforliinul_element.find_all(li,recursiveFalse):# 只找第一层儿子防止重复a_tagli.find(a,recursiveFalse)ifnota_tag:continuechapter_namea_tag.text.strip()# 拼接相对路径为绝对 URLlinkurljoin(base_url,a_tag.get(href,))# 构建当前的完整路径 (如 Home / Setup / Installation)current_pathf{parent_path}/{chapter_name}ifparent_pathelsechapter_name# 记录当前节点# 注意这里的 link 可能带有锚点 #比如 page.html#section我们保留它chapters.append({Chapter_Name:chapter_name,Path:current_path,Level:current_level,Link:link,Summary:# 占位稍后填充})# 如果当前 LI 内部还有 UL说明有子章节递归深入nested_ulli.find(ul,recursiveFalse)ifnested_ul:traverse_ul(nested_ul,current_level1,current_path)# 寻找第一层入口 UL 开始递归root_ulnav_container.find(ul)traverse_ul(root_ul)returnchaptersdefextract_summary(html):提取正文页面的第一段作为简介ifnothtml:return无法获取页面soupBeautifulSoup(html,html.parser)# 典型文档的正文区包裹在 classdocument 或 main 中content_areasoup.select_one(.document)orsoup.select_one(main)ifcontent_area:# 找到第一个有效的非空段落forpincontent_area.find_all(p):textp.text.strip()iflen(text)20:# 过滤掉极短的无意义文本returntext[:200]...# 截取前 200 字return无有效简介8️⃣ 数据存储与导出Storage我们将爬取到的扁平化但带有层级Level标识的数据结构存储为 JSON 文件。这比 CSV 更适合表达树形关系。importjsonimportosdefsave_to_json(data,filenamedb_docs_tree.json):ifnotdata:print( 数据为空取消保存)returnos.makedirs(output,exist_okTrue)file_pathos.path.join(output,filename)# 简单的去重有些文档锚点不同但指向同一页按需决定是否去重unique_data{item[Path]:itemforitemindata}.values()withopen(file_path,w,encodingutf-8)asf:# ensure_asciiFalse 保证中文正常显示indent4 美化输出json.dump(list(unique_data),f,ensure_asciiFalse,indent4)print(f 文档目录树已成功生成并保存至:{file_path})9️⃣ 运行方式与结果展示必写这是主程序它将“拉取树”和“拉取摘要”两大步骤完美串联。importtimedefmain():print( 数据库文档章节爬虫启动)# 步骤 1: 抓取主页获取全局导航树print(f 正在解析全局导航树:{BASE_DOC_URL})home_htmlfetch_page(BASE_DOC_URL)chaptersparse_navigation_tree(home_html,BASE_DOC_URL)print(f✅ 导航解析完成共发现{len(chapters)}个章节节点。)# 步骤 2: 遍历节点抓取详情页摘要 (如果节点过多实战中可按需截断)fori,chapterinenumerate(chapters):linkchapter[Link]# 如果带有 # 锚点说明页面内容跟父级是同一页可避免重复请求if#inlink.split(/)[-1]:chapter[Summary]见父级页面continueprint(f ├── [{i1}/{len(chapters)}] 正在提取摘要: Level{chapter[Level]}-{chapter[Chapter_Name]})page_htmlfetch_page(link)chapter[Summary]extract_summary(page_html)time.sleep(1)# 频率控制做个乖宝宝 ☕# 步骤 3: 存储save_to_json(chapters)if__name____main__:# main() # 取消注释即可运行pass 预期输出的 JSON 文件示例 (db_docs_tree.json)[{Chapter_Name:Getting Started,Path:Getting Started,Level:1,Link:https://docs.mock-database.com/en/latest/start.html,Summary:Welcome to the MockDB documentation. This guide will help you install and configure the database...},{Chapter_Name:Installation,Path:Getting Started / Installation,Level:2,Link:https://docs.mock-database.com/en/latest/install.html,Summary:MockDB can be installed on Linux, macOS, and Windows. We highly recommend using Docker for the quickest setup...}] 常见问题与排错排雷指南在实际爬取不同框架构建的文档时你极有可能遇到这些坑相对路径地狱现象获取到的 Link 是../setup/install.html直接访问报 404。解法永远使用urllib.parse.urljoin(current_page_url, extracted_href)这是处理相对路径的银弹。动态渲染的文档站如 Docusaurus v2 某些模式现象网页源码里没有ul只有div id__docusaurus/div空壳。解法此时不必死磕 HTML。按 F12 打开 Network寻找类似search-index.json或route.js的接口很多现代前端框架会在底层打包一份完整的站点 JSON直接抓那个更香循环嵌套导致递归死循环现象代码跑着跑着报RecursionError栈溢出。解法警惕网页中存在的互相引用比如顶部导航栏和侧边栏互相指向。通过控制recursiveFalse(只找子节点) 或维护一个visited_urls集合来避免死循环。1️⃣1️⃣ 进阶优化打造企业级 RAG 语料如果你的终极目的是做知识库还可以这样秀操作✨Markdown 还原利用html2text库把 HTML 正文直接转换为 Markdown 格式存储这是 LLM 最喜欢的阅读格式。并发加速 (Asyncio / ThreadPool)如果有上千个章节可以用concurrent.futures开启 10 个线程并行抓取摘要速度提升 10 倍以上。接入向量数据库爬完之后直接调用 OpenAI 的 Embedding API将Summary转成向量存入 Milvus 或 Pinecone立刻拥有一个属于你的智能文档问答助手1️⃣2️⃣ 总结与延伸阅读 搞定在这个项目中我们不仅仅是在写爬虫更是在解析结构、理解逻辑。利用递归算法将 DOM 树无损转换为 JSON 树这种“降维打击”的能力在数据工程中非常核心。下一步你可以尝试把抓取到的db_docs_tree.json导入到 Elasticsearch 中自己手撸一个比官方更好用的文档全文搜索工具 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。