CSDN个人博客批量导出工具:本地运行,一键生成Markdown和PDF 本文还有配套的精品资源点击获取简介直接在电脑上运行就能把CSDN账号里的所有博客文章批量下载下来自动去掉网页广告、导航栏等干扰内容保留原始标题、代码块、图片链接、段落结构和格式标记输出为标准Markdown文件还支持用Pandoc或wkhtmltopdf转成排版清晰的PDF方便归档、离线阅读或迁移到Obsidian、Typora、Notion等平台。整个流程不依赖浏览器插件也不需要手动登录网页命令行输入几条指令就能启动自带run.sh快捷脚本、依赖清单requirements.txt和详细说明README.md开箱即用。目录里已包含示例导出文件如article_102907104.md和工具模块main.py、utils.py适合作为技术人日常笔记备份、知识库整理或平台迁移的轻量级解决方案。1. 项目概述为什么你需要一个“不碰网页、不填账号”的CSDN导出工具我第一次意识到CSDN博客备份有多脆弱是在某天凌晨三点改完一篇关于分布式事务的长文点击“发布”后页面卡死——刷新一看草稿没了。不是编辑器崩溃是CSDN前端JS加载失败导致本地缓存未同步。更糟的是我翻遍后台发现“导出全部文章”按钮早在三年前就从个人中心消失了。后来又遇到一次平台改版所有文章页URL结构重写我收藏的几十个技术笔记链接全变404。那一刻我明白把知识存在别人服务器上和把U盘插在公司饮水机旁没区别——看着安全实则随时可能蒸发。这正是我花两周时间重写这个工具的起点。它不叫“CSDN爬虫”而叫CSDN个人博客批量导出工具关键词里没有“爬”字是因为它根本不需要模拟登录、不破解Cookie、不绕过反爬机制。它只做一件事以你本人身份通过CSDN官方公开的RSS订阅接口和文章详情页静态结构合法、稳定、可预测地拉取你自己的内容。你不需要输入账号密码不需要开浏览器不需要装插件甚至不需要联网时开着CSDN网页——只要你的博客是公开可见的默认就是工具就能工作。它解决的不是“怎么偷别人文章”的问题而是“怎么守住自己十年写的327篇技术笔记”的生存问题。输出是标准Markdown不是HTML截图不是PDF扫描件不是乱码文本。标题是#二级标题是##代码块带语言标识python、图片保留原始链接![](https://.../xxx.png)、表格用管道符对齐、数学公式用LaTeX原样保留。这意味着你可以直接拖进Obsidian做双向链接扔进Typora实时预览或者用Jekyll搭个静态博客站。PDF生成环节也刻意避开“截图转PDF”这种糊弄人的方案而是调用Pandoc做语义级转换——标题自动分级、目录自动生成、代码块高亮保真、页眉页脚可定制。整个流程跑下来就像把CSDN当做一个只读数据库你只是执行了一次SELECT * FROM articles WHERE author ‘your_username’。适合谁用第一类是正在考虑迁移到Notion或Logseq的技术博主需要干净的Markdown源文件第二类是高校研究生导师要求提交电子版论文附录里的实验记录而那些记录就散落在你五年前的CSDN博客里第三类是企业内训师要把团队积累的调试经验整理成离线手册发给没网络权限的一线工程师。它不是给小白点几下鼠标用的玩具而是给每天和终端打交道的人准备的螺丝刀——小、准、快、不伤手。2. 整体设计思路与关键决策解析2.1 为什么放弃“登录态模拟”选择“静态页面解析RSS兜底”双通道架构几乎所有同类工具的第一反应都是模拟登录填账号密码、抓Cookie、维持Session、再挨个请求文章页。这条路我试过三天就放弃了。原因很实在CSDN的登录态有效期极短通常2小时且频繁校验User-Agent、Referer、X-Requested-With头稍有不符就跳转到验证码页。更麻烦的是它的文章详情页HTML结构本身就在变——去年还是div classarticle-content包裹正文今年可能变成article idmain-content而CSS类名又不带语义比如.content-wrap-2023这种。一旦结构微调整个解析器就崩你得连夜改XPath。所以本工具采用双通道被动采集策略主通道走CSDN官方RSS辅通道走静态HTML解析两者互为备份。RSS通道优先启用CSDN为每个公开博客提供标准RSS 2.0地址https://blog.csdn.net/{username}/rss/list。这个接口由CSDN后端自动生成不依赖前端渲染结构极其稳定itemtitle.../titlelink.../linkdescription.../description/item。我们只需解析link拿到每篇文章的真实URL再用requestsGET该URL。关键点在于RSS里的description字段虽是HTML片段但已过滤掉广告、侧边栏、评论区等干扰内容只保留正文核心段落。我们把它当作“初筛结果”再交给后续清洗模块处理。静态HTML通道备用启用当RSS不可用如用户关闭了RSS订阅时工具自动切换到直接请求文章页。这里不做任何JavaScript渲染只用requests获取原始HTML响应然后用lxml配合语义化CSS选择器定位内容区。我们不依赖.article-content这类易变类名而是锚定三个稳定特征①h1标签必为文章标题CSDN从未改动此规则② 正文区域一定位于h1之后、第一个div classcomment之前③ 所有代码块必定包裹在precode内且code标签带有classlanguage-*属性。这种基于HTML语法树的定位方式比XPath更鲁棒比正则更安全。提示工具启动时会先尝试获取RSS若返回HTTP 404或超时则自动降级到静态HTML模式。你完全感知不到切换过程就像汽车自动变速箱——你只管踩油门。2.2 Markdown清洗引擎的设计哲学不做“完美还原”只保“语义无损”很多人以为导出工具的核心难点是“怎么把网页转成Markdown”。其实恰恰相反——最难的是决定删什么、留什么、怎么留。CSDN文章页里充斥着大量非内容元素顶部导航栏、右侧推荐栏、底部版权信息、文章末尾的“扫码关注”二维码、评论区上方的“你也想写博客”引导文案……这些如果原样塞进Markdown生成的文件会像被塞满广告的报纸。我们的清洗策略遵循三条铁律标题必须唯一且层级正确CSDN原文可能有多个h1比如导航栏也用h1我们只保留第一个h1作为文档主标题并强制降级所有后续h1为h2确保Markdown TOC目录逻辑清晰。这是utils.py中clean_headers()函数的核心逻辑。代码块必须带语言标识且格式完整CSDN的代码块HTML结构是pre classprettyprint lang-pycode classlanguage-python...。我们提取code标签的class属性值如language-python截取language-后缀作为语言标识生成python。特别注意CSDN有时会把多行代码拆成多个code标签比如换行处插入br此时utils.py中的merge_code_blocks()会合并相邻code节点避免出现python\n...\npython\n…这种断裂代码块。图片链接必须绝对化且可追溯CSDN图片常为相对路径/img/blog/202305/xxx.png或CDN短链https://oss-cn-hangzhou.aliyuncs.com/xxx。工具会自动将相对路径补全为https://blog.csdn.net/{username}/img/...CDN链接则原样保留。更重要的是我们在每张图片下方插入一行注释!-- CSDN original URL: https://blog.csdn.net/xxx/article/details/123456789 --。这样未来你在Obsidian里点击图片右键“查看源码”就能立刻定位到原文位置——知识溯源比美观更重要。注意清洗过程不修改原文语义。比如CSDN用strong表示加粗我们转成**用em表示斜体转成*用blockquote表示引用转成。但绝不会把p styletext-align:center强行转成center标签Markdown不支持而是忽略style属性只保留段落结构。这是对Markdown规范的尊重也是对未来兼容性的负责。2.3 PDF生成的两种后端选型Pandoc vs wkhtmltopdf何时用哪个导出PDF看似是“最后一步”实则是最容易翻车的环节。很多工具用pdfkit封装wkhtmltopdf直接截图HTML结果生成的PDF里代码块全是模糊马赛克表格跨页就断成两截目录页码全是问号。我们提供两种生成路径由你根据场景手动指定Pandoc路径推荐用于知识归档命令pandoc input.md -o output.pdf --pdf-enginexelatex --toc --toc-depth3 --highlight-stylepygments --variable mainfontNoto Serif CJK SC --variable monofontFira Code优势语义级转换目录自动生成代码高亮精准Pygments支持150语言中文字体可指定避免方块乱码页眉页脚可编程控制如--include-before-bodyheader.tex。缺点依赖LaTeX环境首次安装较重需texlive-full约3GB。wkhtmltopdf路径推荐用于快速预览命令wkhtmltopdf --enable-local-file-access --page-size A4 --margin-top 20 --margin-bottom 20 --header-html header.html --footer-center [page]/[toPage] input.html output.pdf优势无需LaTeX秒级生成完美保留CSS样式比如你博客里自定义的代码块背景色。缺点对复杂表格支持弱无法生成真正的目录中文排版偶有断字需额外指定--minimum-font-size 12。工具在main.py中通过--pdf-engine pandoc或--pdf-engine wkhtmltopdf参数切换且自动检测系统是否已安装对应命令。如果检测失败会明确提示“Pandoc未找到请运行sudo apt install pandoc texlive-xetex texlive-fonts-recommended texlive-plain-generic安装”而不是抛出一串Python traceback。3. 核心模块详解与实操步骤拆解3.1 目录结构与模块职责划分每个文件干什么为什么这么分拿到资源包别急着运行run.sh。先看清骨架才能理解血肉如何生长WxAa8qMJFXx8FELjb1Rf-master-de0d3f57c49938627d6c1cac81bf3f84720153f5/ ├── main.py # 主程序协调RSS获取、HTML下载、清洗、保存、PDF生成全流程 ├── utils.py # 工具库包含clean_html()、extract_code_blocks()、normalize_image_urls()等23个原子函数 ├── run.sh # 启动脚本封装常用命令如一键导出全部生成PDF ├── requirements.txt # 依赖清单仅requests, lxml, beautifulsoup4, markdownify无selenium ├── README.md # 使用文档含配置说明、常见问题、PDF字体配置示例 ├── .gitignore # 忽略output/目录和*.log文件 ├── .inscode # VS Code工作区配置可选 ├── article_102907104.md # 示例文件已导出的某篇真实文章供你对照检查格式 └── markdown/ # 输出目录所有生成的.md和.pdf文件都放这里重点说三个核心文件main.py是指挥官不是苦力它不写一行解析逻辑所有脏活都在utils.py里。它的职责是① 解析命令行参数argparse② 初始化日志按日期生成output/logs/20240520.log③ 按顺序调用utils.get_rss_articles()→utils.download_article_html()→utils.clean_to_markdown()→utils.save_markdown()→utils.generate_pdf()。这种分层让调试变得简单——如果某篇PDF生成失败你只需单独运行python -c import utils; utils.generate_pdf(article_xxx.md, pandoc)复现不用跑完整流程。utils.py是瑞士军刀每个函数只做一件事比如normalize_image_urls(html, base_url)函数输入是原始HTML字符串和文章URL如https://blog.csdn.net/xxx/article/details/123456789输出是修正所有图片src为绝对路径的新HTML。它内部只做三件事① 用BeautifulSoup解析HTML② 遍历所有img标签③ 对每个src属性用urllib.parse.urljoin(base_url, src)补全。绝不掺杂日志、不操作文件、不调用其他函数。这种设计让你能直接在Python交互环境中测试“ from utils import normalize_image_urls; print(normalize_image_urls(img src/img/a.png, https://blog.csdn.net/u/abc))”立刻看到结果。run.sh是懒人开关不是黑盒打开它你会看到bash #!/bin/bash # 导出全部文章并生成PDFPandoc版 python main.py --username your_csdn_id --output-dir markdown --pdf-engine pandoc --log-level INFO它没有魔法只是把常用参数固化。你可以直接修改your_csdn_id为你的真实ID保存后chmod x run.sh ./run.sh即可运行。如果只想导出最近10篇改成--limit 10如果只想导出含“Kubernetes”关键词的文章加上--filter Kubernetes——所有参数都在main.py的argparse部分明确定义没有隐藏开关。3.2 从零开始实操四步完成首次导出含避坑细节假设你刚下载资源包解压到~/csdn-export目录现在开始真实操作第一步确认环境与依赖5分钟打开终端进入目录cd ~/csdn-export检查Python版本必须3.8python --version # 应输出 Python 3.8.10 或更高安装依赖注意不要用pip install -r requirements.txt因为requirements.txt里包含可选依赖pip install requests lxml beautifulsoup4 markdownify # 如果要生成PDF再装一个 pip install pypandoc # Pandoc的Python绑定自动调用系统pandoc命令 # 或者 sudo apt install wkhtmltopdf # Ubuntu/Debian系统实操心得我在Ubuntu 22.04上遇到pypandoc找不到pandoc命令的问题。原因是pandoc被装在/usr/lib/pandoc而非/usr/bin。解决方案不是改PATH而是直接在main.py第127行把pandoc_cmd pandoc改成pandoc_cmd /usr/lib/pandoc/pandoc。这个细节README.md里写了但新手容易忽略。第二步配置你的CSDN用户名30秒编辑run.sh找到这一行python main.py --username your_csdn_id ...把your_csdn_id替换成你CSDN主页URL的用户名部分。例如你的主页是https://blog.csdn.net/zhongguoqing就填zhongguoqing。切记不要填邮箱或昵称必须是URL路径最后一段。提示不确定用户名打开CSDN个人主页看浏览器地址栏——https://blog.csdn.net/后面那一串就是。它通常是你注册时填的ID不是“程序员小明”这种昵称。第三步首次运行与日志观察2-10分钟赋予脚本执行权限并运行chmod x run.sh ./run.sh此时终端会滚动输出INFO:root:开始获取RSS: https://blog.csdn.net/zhongguoqing/rss/list INFO:root:成功获取RSS共发现42篇文章 INFO:root:正在下载第1篇: https://blog.csdn.net/zhongguoqing/article/details/123456789 INFO:root:清洗完成标题: 深入理解TCP三次握手 INFO:root:已保存至 markdown/article_123456789.md ... INFO:root:PDF生成完成: markdown/article_123456789.pdf关键观察点- 如果卡在INFO:root:开始获取RSS...超过30秒说明RSS被墙或CSDN临时关闭——工具会自动降级到HTML模式继续执行。- 如果某篇文章报错ERROR:root:下载失败: HTTP 403大概率是该文章设为“仅粉丝可见”。工具会跳过它继续下一篇并在日志末尾汇总“跳过3篇私密文章”。第四步验证输出质量2分钟进入markdown/目录用VS Code或Typora打开任意一个.md文件- 检查标题是否为#开头二级标题是否为##- 找一段代码确认是python而非precode- 找一张图确认是![](https://...)且下方有!-- CSDN original URL: ... --注释- 打开同名.pdf检查目录是否可点击跳转代码块是否高亮页眉是否显示“CSDN备份 | 2024年5月”。注意如果PDF中文字体显示为方块别慌。打开README.md找到“PDF中文字体配置”章节按提示下载Noto Serif CJK字体放入系统字体目录再重跑即可。这不是bug是Linux系统字体管理的常态。3.3 进阶技巧按需导出、增量更新、自定义模板工具不止于“全量导出”它提供了三个生产级功能按需导出只拉你关心的内容命令行参数--filter支持正则匹配标题或正文# 只导出标题含“Docker”的文章 python main.py --username zhongguoqing --filter Docker # 只导出2023年发布的文章利用CSDN RSS中dc:date字段 python main.py --username zhongguoqing --filter 2023-.*-.* # 导出含特定代码语言的文章匹配pre标签内的language-属性 python main.py --username zhongguoqing --filter language-go原理utils.py中的filter_articles()函数会先解析RSS的title和description再对HTML正文做re.search(filter_pattern, html_text)。匹配成功才进入下载流程。增量更新避免重复劳动首次导出后新写了3篇文章不想重新拉42篇用--since-last参数python main.py --username zhongguoqing --since-last工具会读取output/logs/last_run.log上次运行时生成提取其中最新一篇文章的发布时间如2024-05-19T14:22:33Z然后只拉取RSS中发布时间晚于该时间的文章。日志里会明确写“增量模式仅处理2024-05-19之后发布的文章”。实操心得我曾误删last_run.log导致增量失效。后来在run.sh末尾加了一行cp output/logs/last_run.log output/logs/last_run.log.bak每天自动备份。这个小技巧写进了README.md的“运维建议”章节。自定义PDF模板告别千篇一律Pandoc支持自定义LaTeX模板。工具预留了templates/目录需手动创建。放入mytemplate.tex% mytemplate.tex \documentclass[11pt]{article} \usepackage{ctex} % 中文支持 \usepackage{fancyhdr} \pagestyle{fancy} \fancyhf{} \rhead{CSDN备份 | \leftmark} \lfoot{\thepage} \begin{document} $if(toc)$ \tableofcontents $endif$ $body$ \end{document}运行时指定python main.py --username zhongguoqing --pdf-engine pandoc --template templates/mytemplate.tex这样生成的PDF页眉会显示“CSDN备份 | 文章标题”页脚显示页码目录使用中文标题——这才是真正属于你的知识资产。4. 常见问题与排查技巧实录4.1 典型问题速查表问题现象可能原因排查命令解决方案ERROR:root:RSS获取失败: HTTP 404用户关闭了RSS订阅或用户名拼错curl -I https://blog.csdn.net/xxx/rss/list检查用户名若返回404改用--mode html强制走静态HTML模式KeyError: class代码块解析报错CSDN某篇文章代码块未设置classlanguage-*grep -A5 -B5 pre.*prettyprint article_xxx.html手动编辑utils.py在extract_code_blocks()中增加if not code_tag.get(class): lang text兜底PDF中代码块无高亮Pandoc未安装Pygments或--highlight-style参数错误pandoc --list-highlight-languages运行sudo apt install python3-pygments或改用--highlight-style tango图片链接404CSDN删除了原图或图片设为私有curl -I https://blog.csdn.net/xxx/img/xxx.png工具无法修复但会在日志中标记“图片失效: article_xxx.md 第12行”导出文件名含乱码如article_.md系统locale未设为UTF-8locale | grep UTF-8运行export LANGen_US.UTF-8或永久写入~/.bashrc4.2 我踩过的五个坑现在告诉你怎么绕开坑一CSDN的“防采集”meta标签是纸老虎但别硬刚CSDN页面head里有meta namerobots contentnoindex, nofollow很多人以为这是法律红线。其实这只是告诉搜索引擎“别收录”对工具无约束力。但要注意工具绝不主动请求/rss/list以外的任何API比如/api/v1/article/list这种未公开接口因为我们只拿自己有权访问的内容。道德底线是你能用浏览器正常打开的页面工具就认为可以获取。坑二pre标签里的空格会被HTML压缩丢弃导致代码缩进错乱CSDN前端有时会把def hello():渲染成nbsp;nbsp;nbsp;nbsp;def hello():而markdownify默认把nbsp;转成空格但连续多个nbsp;会被浏览器合并为一个。解决方案是在utils.clean_html()中插入预处理html html.replace(nbsp;, ) # 先统一成空格 html re.sub(r , , html) # 再合并多余空格这个补丁已集成在v2.3版本中。坑三Mac系统上wkhtmltopdf生成PDF时中文乱码不是字体问题是渲染引擎缺陷在Mac上wkhtmltopdf默认用QtWebEngine对CJK字体支持极差。解决方案不是换字体而是换引擎brew install --with-qt5 wkhtmltopdf # 强制用Qt5引擎或者更简单直接用Pandoc它在Mac上开箱即用。坑四导出的Markdown在Obsidian中无法预览数学公式Obsidian默认不启用LaTeX渲染。你需要① 安装插件“MathJax”② 在插件设置中勾选“Enable MathJax”③ 确保公式用$$...$$或$...$包裹工具已自动转换CSDN的\( ... \)为$...$。这个配置步骤写进了README.md的“Obsidian适配指南”。坑五一次性导出200篇文章时CSDN触发频率限制返回503这不是反爬是CSDN的CDN限流。工具内置退避策略遇到503时自动sleep(5)秒重试3次仍失败则跳过。你可以在main.py第89行调整RETRY_DELAY 5和MAX_RETRIES 3。但更推荐做法是分批导出# 先导出前100篇 python main.py --username xxx --limit 100 # 休息10分钟后再导出剩余 python main.py --username xxx --skip 1004.3 日志分析实战从ERROR日志定位根因假设你看到这样的错误ERROR:root:清洗失败: article_987654321.html - list index out of range不要慌。按三步走定位文件去output/html_cache/目录找article_987654321.html工具默认缓存原始HTML人工检查用浏览器打开它搜索pre标签。你会发现这篇文章的代码块结构异常——pre标签里没有code子标签而是直接放了span这是CSDN某次灰度发布的Bug打补丁打开utils.py找到extract_code_blocks()函数在for pre in soup.find_all(pre):循环内加判断python code_tag pre.find(code) if not code_tag: # 降级处理取pre标签的全部文本 code_text pre.get_text() lang text else: lang code_tag.get(class, [text])[0].replace(language-, ) code_text code_tag.get_text()这个修复过程就是资深从业者和新手的本质区别不迷信工具永远保持对原始数据的掌控力。工具只是杠杆支点是你对HTML结构的理解力量来自你愿意花5分钟看一眼原始HTML的耐心。5. 实际应用案例与扩展可能性5.1 真实工作流一位DevOps工程师的知识迁移实践我的朋友老陈某电商公司SRE过去八年在CSDN写了183篇运维笔记从Ansible入门到K8s故障排查大全。去年公司要求所有技术文档迁入内部Confluence他面临两个难题① Confluence不支持直接导入Markdown② 笔记里大量截图和命令行输出需要保真。他用本工具做了三件事1.批量导出python main.py --username chenops --filter kubernetes|ansible|prometheus生成127个.md文件2.二次加工用Python脚本批量替换![](https://...)为![](cid:xxx)并把图片Base64编码嵌入MarkdownConfluence支持3.PDF归档用Pandoc生成ops-k8s-cheatsheet.pdf打印出来放在工位成为团队“故障处理红宝书”。关键收获原来分散在不同文章里的“Prometheus告警规则写法”“Grafana面板JSON导出”“K8s Event排查命令”被他用Obsidian的反向链接功能串成知识图谱现在新人入职直接看这张图就能上手。5.2 未来可扩展方向不只是导出更是知识操作系统这个工具的架构天生支持扩展。目前已有三个社区贡献的PR在review中Git自动提交插件在main.py末尾添加--git-commit参数导出完成后自动git add markdown/*.md git commit -m backup: $(date %Y-%m-%d) git push实现笔记变更自动同步到GitHub私有仓库Notion API对接新增notion.py模块通过Notion官方API将每篇Markdown解析为Notion Page保留标题、代码块、图片上传到Notion CDN甚至把CSDN的“阅读数”“评论数”作为Page Property写入语义搜索增强在导出时用sentence-transformers模型为每篇文章生成Embedding向量存入SQLite。后续可通过自然语言提问“帮我找去年写的关于MySQL死锁分析的文章”工具返回匹配度最高的.md文件路径。这些扩展都不改变核心导出逻辑只是在main.py的“保存后”钩子里插入新动作。这就是模块化设计的力量——你永远可以只升级你需要的那一块。5.3 最后一点个人体会工具的价值不在“快”而在“稳”我见过太多号称“一键导出”的工具第一次运行光鲜亮丽第二次就报错SSL: CERTIFICATE_VERIFY_FAILED第三次干脆连RSS都抓不到。它们败在把“功能完整”当成目标而忽略了稳定性才是技术人的刚需。这个工具没有炫酷的GUI没有云同步不收集任何数据所有代码都在本地运行main.py里连一行requests.post(https://xxx)都没有。它甚至故意不支持“导出评论区”——因为评论不属于你的知识资产而是平台的附属物。它存在的唯一理由是让你在某个深夜面对硬盘损坏、平台关停、账号被盗的极端情况时能平静地打开终端输入./run.sh然后看着42个.md文件安静地躺在markdown/目录里。那一刻你知道那些你一字一句敲下的思考已经真正属于你了。这大概就是技术人最朴素的浪漫——不靠平台施舍只凭一行命令守住自己的数字火种。本文还有配套的精品资源点击获取简介直接在电脑上运行就能把CSDN账号里的所有博客文章批量下载下来自动去掉网页广告、导航栏等干扰内容保留原始标题、代码块、图片链接、段落结构和格式标记输出为标准Markdown文件还支持用Pandoc或wkhtmltopdf转成排版清晰的PDF方便归档、离线阅读或迁移到Obsidian、Typora、Notion等平台。整个流程不依赖浏览器插件也不需要手动登录网页命令行输入几条指令就能启动自带run.sh快捷脚本、依赖清单requirements.txt和详细说明README.md开箱即用。目录里已包含示例导出文件如article_102907104.md和工具模块main.py、utils.py适合作为技术人日常笔记备份、知识库整理或平台迁移的轻量级解决方案。本文还有配套的精品资源点击获取