ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CSDN文章转PDF归档:浏览器打印与Playwright批量导出

2026/9/17 11:44:31 拓冰建站 浏览量
CSDN文章转PDF归档:浏览器打印与Playwright批量导出 硬盘里那个叫 tech-archive 的目录是我这几年陆陆续续攒下来的技术资料库里面躺着八百多个 PDF。最早的几份是几年前从 CSDN 博客上存下来的单片机配置笔记去年我想回头查其中一个寄存器地址点开原链接——文章已经被作者删了评论区只剩一句内容已迁移。从那天起把 CSDN 文章转成 PDF 归档就成了我的固定动作找到一篇值得留下的技术文章把它变成一份不依赖网络、不怕删改、能随时翻、能直接打印做标注的 PDF 文件。这篇内容就是把这套流程完整拆开讲清楚包括什么时候该转、页面里有哪些坑、浏览器打印的参数怎么填、批量导出用什么脚本、转完怎么验收质量。不管你是刚开始收集资料的新手还是已经存了几百份文件想升级工作流的人都能直接照着做。1. 先把值不值得转这件事想明白我见过太多人一上来就研究工具结果存了三百个 PDF 再也没打开过。转 PDF 这个动作本身不贵但管理和检索的成本会随着数量线性增长所以第一步不是动手是筛选。1.1 三类真正值得落成本地 PDF 的文章我自己的判断标准很粗暴只有满足下面任意一条才会转含可复现的操作步骤。比如某个环境的完整安装流程、某个报错的处理链路、某个参数的调优过程。这类内容一旦原文改动或者配图挂掉价值直接归零必须本地留一份。含配置清单、参数表、命令集合。这类内容我经常要在没有外网的机器上对照着敲PDF 是最省事的形式不用来回切窗口。成系列的教程。单篇可以靠收藏夹但一个十几篇的系列教程如果分散在各个收藏里半年后你根本找不到第三篇在哪。合成一份 PDF 反而形成了天然的目录。反过来纯资讯类、观点评论类、以及那些每年都会被新版本推翻的某版本新特性速览我基本不存。这类内容生命周期太短存下来只会污染你的资料库。1.2 一个筛选清单三十秒决定去留为了不在这件事上纠结我给自己定了一张表看完文章扫一眼就能定判断维度值得转不值得转内容类型教程、排查记录、参数手册资讯、活动公告、观点随笔时效性原理类、通用方法版本强相关且更新频繁是否含代码有可运行片段只有概念描述阅读场景需要离线/反复查阅一次看完即弃图文依赖大量截图和流程图纯文字提示如果一篇文章你犹豫超过十秒那基本就是不需要。真正有用的文章你看到一半就会想这个得存下来。1.3 转 PDF 之外的第一个备选方案顺便说一句不是所有情况都要转 PDF。如果文章只有几百字、纯粹是个命令备忘直接复制到本地笔记里比转 PDF 快十倍。PDF 的优势在于保留原始排版、图文位置和代码高亮当你需要看到作者原本长什么样的时候它才不可替代。我一般是这样分工的短备忘进笔记长教程和图文混排的进 PDF。2. 转换之前得先摸清页面到底是怎么渲染的很多人打印出来发现缺图、缺代码、排版错乱八成不是工具的问题是没搞清楚页面结构就下手了。这一步花五分钟能省掉后面反复重来的时间。2.1 服务端直出和前端动态渲染的区别技术博客的正文渲染大致分两类。一类是打开页面时 HTML 里就已经带着完整正文浏览器右键查看网页源代码就能看到全部文字另一类是先给个空壳正文靠脚本在浏览器里动态拼出来。这两类在处理手法上完全不同前者简单浏览器打印、命令行工具、简单脚本都能拿到完整内容。后者麻烦如果你用不带浏览器内核的工具直接抓拿到的往往是个空白页或者只有标题。判断方法很直接打开开发者工具禁用 JavaScript 刷新一次页面。如果正文还在就是第一类如果正文消失只剩导航就是第二类必须用带渲染引擎的方案。2.2 懒加载图片和折叠代码块这是最容易吃亏的两个地方。图片懒加载意味着你停留在页面顶部时下面的图还没加载出来这时候直接按打印下半部分的插图会是空框。解决办法是先手动滚到页面底部等所有图都渲染完再回到顶部打印。折叠代码块更隐蔽。有些文章的代码默认只显示几行要点击展开才显示全部。打印前必须把所有折叠项点开否则你存下来的 PDF 里最关键的那段代码刚好被省略号截掉了。批量处理时这一步尤其要注意脚本里必须加一步点击所有展开按钮。2.3 关于需要登录才能查看的内容有些文章或专栏需要登录后、甚至是订阅后才能看全文。这里的态度要明确正常登录自己的账号把你有权访问的内容导出来自用这是合理的个人归档行为但如果内容本身需要付费授权那就老老实实付费不要试图绕过任何访问控制。另外导出后只在个人设备上阅读使用不要二次分发或者上传到公开渠道这是基本的分寸。3. 浏览器打印这条路参数怎么填才不出废页浏览器自带的打印成 PDF 是所有方案里门槛最低的Windows 上一般对应Microsoft Print to PDF这个虚拟打印机Mac 和 Linux 上就是系统自带的存储为 PDF。但默认参数打出来的东西基本没法看左右各一条侧边栏底部一堆推荐阅读代码块被拦腰砍断。下面是我摸索出来的一套固定流程。3.1 打印预览打开后先做三项清理按 CtrlP 或者 CmdP 之后别急着点保存先处理这三件事砍掉非正文区域。最省事的办法是开启浏览器的阅读模式地址栏右侧那个像书本的图标它会把导航、侧栏、评论区、广告全部剥掉只留正文。如果阅读模式识别失败就退回手动方案在开发者工具里选中要隐藏的区块按 Delete 键临时删掉注意这个操作只在当前页面会话有效刷新就恢复所以删完立刻打印。展开全部折叠内容。回到页面上把所有展开全文显示更多的按钮点一遍。触发所有懒加载图片。从顶部匀速滚到底部再滚回来或者在开发者工具的 Console 里执行一次滚动脚本确保图片全部加载。3.2 页边距、缩放和背景图形这三个参数打印对话框里那几个不起眼的选项直接决定了成品质量。我用下来最稳的一组配置是参数推荐值理由纸张A4通用性最好打印和屏幕阅读都合适边距默认或最小自定义边距容易让代码块超宽被裁缩放80% 到 90%技术文章常有宽表格和长代码行缩一点能避免横向截断背景图形必须勾选不勾的话代码高亮底色全丢只剩黑白文字页眉页脚取消勾选否则每页顶部带 URL、底部带页码和日期双面打印视情况只是电子存档的话无所谓注意缩放低于 75% 之后正文会明显变小屏幕上读起来很累。如果 90% 仍然有横向截断问题不在缩放而在代码块本身不支持换行得用下一节的 CSS 方案解决。3.3 代码块被拦腰截断的修复手法这是最让人抓狂的问题一段五十行的代码打印出来第一页尾巴断在第十二行第二页开头接着第十三行中间还会插进一个页眉。修复思路是往页面里注入一段打印专用样式告诉浏览器代码块和图片不要跨页断开。具体做法是打开开发者工具的 Console粘贴执行下面这段const style document.createElement(style); style.textContent media print { pre, code, blockquote, table, figure, img { break-inside: avoid !important; page-break-inside: avoid !important; } pre { white-space: pre-wrap !important; word-break: break-all !important; font-size: 11px !important; line-height: 1.4 !important; } table { font-size: 11px !important; } h1, h2, h3 { break-after: avoid !important; } body { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; } } ; document.head.appendChild(style);其中break-inside: avoid是核心它让代码块、图片、表格尽量保持在同一页内white-space: pre-wrap让超长代码行自动折行避免横向被裁最后一行print-color-adjust: exact是强制保留背景色某些浏览器里光勾选背景图形还不够得靠它兜底。执行完这段再按 CtrlP会发现断裂问题基本消失代价只是页数多一点这对电子存档完全值得。3.4 关于页码浏览器原生确实没有浏览器打印出来的 PDF 默认不带页码长文档翻起来不方便。两个补救办法一是装一个能生成页码的打印辅助扩展在保存时自动在页脚叠加二是先用浏览器导出 PDF再用任意一款 PDF 编辑器批量添加页码。我一般用第二种因为可以顺手在封面加个标题和归档日期做成一份像样的文档。这一步不算刚需但如果你打算长期归档加上页码和标题页会让检索体验好很多。4. 从单篇到整栏目批量导出的实现路径单篇靠浏览器打印够了但当你想把一个系列、一个专栏甚至一个作者的全部教程一次性存下来时手动操作就完全不现实了。这时候就得上自动化。4.1 三种批量方案的实际取舍我把能想到的路子都试了一遍结论如下方案上手难度批量能力适用场景浏览器扩展极低中等一次导十几篇偶尔用命令行工具中等强需要稳定复现、定期跑自写脚本较高最强有定制需求比如加封面、合并目录我的建议是如果你只是偶尔存几篇装个支持批量打印当前标签页的扩展就够了把所有要存的文章在标签页里打开一次性导出。如果你要定期归档或者需要处理上百篇那就直接上脚本一次投入换来长期省事。4.2 用无头浏览器跑批量导出下面这份脚本是我自己一直在用的基于 Python 加 Playwright核心逻辑是逐个打开文章链接等页面渲染完成展开所有折叠块滚到底部触发懒加载最后调用浏览器的原生打印接口输出 PDF。import asyncio from pathlib import Path from playwright.async_api import async_playwright URLS_FILE urls.txt OUT_DIR Path(pdf_out) OUT_DIR.mkdir(exist_okTrue) PRINT_CSS media print { pre, code, table, figure, img { break-inside: avoid !important; } pre { white-space: pre-wrap !important; word-break: break-all !important; } body { -webkit-print-color-adjust: exact !important; } } async def save_one(page, url, idx): await page.goto(url, wait_untilnetworkidle, timeout60000) # 展开所有折叠代码块选择器需按实际页面结构调整 for sel in [.show-more, .read-more, .code-expand]: for btn in await page.query_selector_all(sel): try: await btn.click() except Exception: pass # 滚到底部触发懒加载 await page.evaluate( async () { for (let y 0; y document.body.scrollHeight; y 600) { window.scrollTo(0, y); await new Promise(r setTimeout(r, 150)); } window.scrollTo(0, 0); } ) await page.add_style_tag(contentPRINT_CSS) title (await page.title())[:60].replace(/, _).replace(\\, _) out OUT_DIR / f{idx:03d}_{title}.pdf await page.pdf( pathstr(out), formatA4, print_backgroundTrue, margin{top: 12mm, bottom: 14mm, left: 10mm, right: 10mm}, scale0.9, ) print(fdone - {out}) async def main(): urls [u.strip() for u in Path(URLS_FILE).read_text(encodingutf-8).splitlines() if u.strip()] async with async_playwright() as p: browser await p.chromium.launch() page await browser.new_page(viewport{width: 1280, height: 900}) for i, url in enumerate(urls, 1): try: await save_one(page, url, i) except Exception as e: print(ffail {url}: {e}) await asyncio.sleep(2) await browser.close() asyncio.run(main())几个关键参数值得单独说。wait_untilnetworkidle是等网络请求静默下来这一步比单纯等几秒可靠得多因为不同文章的图片数量差别很大。print_backgroundTrue对应打印对话框里的背景图形少了它代码高亮就没了。scale0.9是全局缩放和前面手动打印时的建议一致。两个sleep是我踩坑之后加的连续快速请求同一站点很容易被限流间隔两秒能显著降低失败率。至于那几个折叠按钮的选择器我写的是常见命名实际用之前你必须打开开发者工具确认一遍因为这类类名会不定期调整。4.3 文件命名和目录分层别等到五百个文件才后悔文件名混乱是归档最大的敌人。我的规则是序号_标题关键词_日期比如012_环境配置与依赖安装_20240411.pdf。序号保证目录里的顺序稳定标题关键词保证肉眼可见日期保证你以后能看出这份资料的新旧。目录按主题/子主题两层分就够了超过三层你会懒得点进去。比如tech-archive/ embedded/ mcu-config/ peripheral-driver/ backend/ database/ deployment/ tooling/ build-system/提示别用中文特殊符号做文件名跨设备同步和某些命令行工具会出问题。空格也尽量换成下划线。5. 导出之后的验收六个必须过一遍的检查点很多人转完就直接扔进文件夹了等到真正需要用的那天才发现缺图或者文字选不中。我现在的习惯是每转一批抽几份过一遍验收清单。5.1 六个检查项的具体标准检查项合格标准常见不合格表现图片完整性全部插图正常显示大面积空框、灰色占位代码高亮底色和语法着色保留全黑白或底色变纯黑代码完整性无省略号截断中间出现展开更多或断行横向内容无右侧裁切表格右边一列消失文字可选中光标能选中正文整页是图片选不中文件体积单篇 1MB 到 5MB超过 30MB 或者只有几十 KB最后一项特别值得说。只有几十 KB往往意味着图全丢了超过 30MB则通常是某张未压缩的大图被原样嵌入。我遇到过一篇带四十多张截图的教程导出后 68MB翻页卡顿后来用 PDF 编辑器的图片压缩功能降到 4MB屏幕上完全看不出差别。5.2 文字图层为什么有的 PDF 搜不了如果 PDF 是用浏览器原生打印生成的正文一定是矢量文字可以选中、可以搜索、可以复制。但如果你走的是一些截图拼接成 PDF的野路子整份文件就是一堆位图搜索功能直接废掉体积还会暴涨。这也是我一直推荐浏览器原生打印而不是截图方案的原因——同样是存档能不能搜索是质变。顺带说一个细节代码里的连字符和引号有时会被排版规则换成全角字符导致你复制出来粘贴运行会报错。验收时随手复制两行代码到编辑器里跑一下能提前发现这个问题。5.3 公式、图表和宽表格的还原度技术文章里的数学公式和架构图是最容易被破坏的部分。公式如果原本是用脚本渲染的打印时可能变成一堆乱码或者位置错位宽表格和横向流程图在 A4 宽度下几乎必然被裁。对付宽表格除了前面提到的缩放和pre-wrap还有一个办法把纸张方向临时改成横向只对这一页生效。浏览器打印支持按页设置虽然操作稍麻烦但对那几张确实很宽的表是唯一不丢内容的选择。至于架构图如果导出后线条丢失或者文字重叠我的做法是单独把那张图另存为 PNG和 PDF 放在同一个目录里文件名保持一致。看起来不够优雅但实用。6. 存下来之后让这些 PDF 真正能被用起来归档不是终点如果五百份 PDF 躺在硬盘里从来打不开那前面的工作全是白费。这几年我摸索出几个让资料库保持活着的习惯分享给你。6.1 给资料库建一份索引比什么都重要我用的是一份 Markdown 索引文件每次新增 PDF 就补一行记录文件名、主题、来源和一句话备注。听起来很笨但它解决了最关键的问题三个月后你根本不记得自己存过什么。有了索引用编辑器全局搜索就能找到比在文件管理器里翻快得多。索引格式大概是这样- [012] 环境配置与依赖安装 | embedded/mcu-config | 2024-04-11 | 记录了交叉编译工具链的坑 - [013] 串口通信调试记录 | embedded/peripheral-driver | 2024-04-13 | 含示波器波形图6.2 和笔记工具联动的两种做法第一种是把 PDF 当成附件挂进笔记笔记里只写这篇解决了什么问题正文留在 PDF 里。第二种是把 PDF 里最关键的几段代码和结论摘抄到笔记PDF 作为原始出处备查。我两种都在用原理性的内容走第一种操作性内容走第二种因为操作步骤我迟早要复制出来用提前摘一遍省事。如果你用的是支持 PDF 批注的阅读器还有个额外收益读的时候顺手高亮和写批注几个月后回看那些标记就是你自己写的第二版笔记比原文章有用得多。6.3 自用归档的边界感最后必须提一句。把公开的技术文章转成 PDF 放在自己电脑上阅读、做笔记、打印这是正常的学习行为。但如果要转发给别人、发到群里、或者传到公开网盘就涉及作者的权利了尤其是有明确版权声明或者付费专栏的内容。我的原则很简单只存自己看得不在任何公开渠道二次分发引用的时候注明出处和原文链接。这个分寸守住了归档这件事才能做得长久。对了还有一个我用了很久的小技巧每份 PDF 存下来之后我会在文件的元数据里补上原文标题和链接。浏览器导出的 PDF 元数据基本是空白的用任意 PDF 编辑器填一下以后就算文件名被改乱了点开属性还能顺藤摸瓜找回原文。这一步只花十秒钟但在我身上至少救回过七八份不知道从哪来的文件。