Python爬虫实战:手把手教你如何搭建文档站点快照与长图归档器!
㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐⭐⭐ (进阶)福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现净化与长图截取 (Snap Purify)7️⃣ 核心实现哈希校验与增量检测 (Diff Hash)8️⃣ 数据存储与统一归档 (Manifest Zipping)9️⃣ 运行方式与结果展示必写 常见问题与排错极度硬核1️⃣1️⃣ 进阶优化视觉像素级比对 (Image Diff)1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface一句话说明本文将带你使用 Python 与 Playwright打造一个能够自动巡航开源文档、剥离干扰元素截取完美长图、保存 HTML 源码并通过哈希对比Hash Diff实现增量归档的极客工具。读完能获得什么掌握使用 Playwright 截取“无瑕疵超长网页图”的前端 DOM 操控技巧。学会利用 SHA-256 哈希算法避免重复抓取未变动的文档节省海量硬盘空间。建立一套带有版本控制与 ZIP 压缩功能的自动化文档追踪体系。1️⃣ 摘要Abstract一句话说明通过无头浏览器加载文档页执行自定义 JS 脚本清理悬浮组件同步保存渲染后的完整 HTML 与 PNG 格式全屏截图并基于文件哈希生成 JSON 格式的变更追踪清单Manifest。读完能获得什么一套开箱即用的、专注于文档体系备份的自动化脚本。从“单次执行”向“长期定时监控Cron”演进的 DevOps 经验。2️⃣ 背景与需求Why为什么要归档文档开源界有一句名言“代码是骨骼文档是灵魂”。但文档的变动往往比代码更频繁且不可追溯。利用该工具我们可以构建团队私有的 API 知识图谱甚至在断网或内网环境下依然拥有最原汁原味的图文参考资料。目标字段清单Schema Definitiontarget_url文档原始链接page_title页面标题提取自titlecrawl_time快照生成的 UTC 时间html_path本地归档的.html相对路径screenshot_path本地归档的.png相对路径content_hashHTML 核心正文的 SHA-256 哈希值用于判断页面是否更新3️⃣ 合规与注意事项必写尊重托管平台大多文档托管在 GitHub Pages、Vercel 或 Netlify 上。截取全页和加载全量静态资源相对消耗带宽请务必设置合理的并发限制和请求延时。夜间巡检如果是进行全站级别的遍历归档强烈建议通过 Cron 定时任务放在凌晨执行。版权声明归档的数据仅用于学习、内网参考或历史回溯若要将截图公开二次发布请遵循该开源项目原有的许可证如 MIT 或 Apache 2.0。4️⃣ 技术选型与整体流程What/How核心引擎Playwright。对于“截图”这个强需求requests无能为力Selenium配置笨重。Playwright凭借其原生的异步支持和极强的跨浏览器截图能力是当前唯一的真神。增量存储思想Diff Detection文档如果不更新天天截图不仅浪费时间还占磁盘。我们在截图前先提取正文计算 Hash与上一版的 Hash 对比。如果一致直接跳过截图和存储阶段整体流水线 (Archival Pipeline)5️⃣ 环境准备与依赖安装可复现来吧搭建我们的档案室Python 版本推荐 3.9。依赖安装pipinstallplaywright pandas playwrightinstallchromium工程目录结构doc_archiver/ ├── config/ │ └── target_docs.txt # 需要监控的 URL 列表 ├── core/ │ ├── archiver.py # 核心截图与存储逻辑 │ └── diff_checker.py # 哈希比对模块 ├── vaults/ # 历史快照存放舱 │ ├── 20231027_v1/ # 按日期/版本划分的归档夹 │ └── doc_manifest.json # 增量追踪总表 └── run_scheduler.py # 定时启动入口6️⃣ 核心实现净化与长图截取 (Snap Purify)由于我们要保存完美的“长图”必须干掉那些随着鼠标滚动一直飘在屏幕上的元素。# core/archiver.pyimportasynciofromplaywright.async_apiimportasync_playwrightimportosasyncdefpurify_and_snapshot(page,url,save_dir,file_prefix): 访问页面 - 净化浮动元素 - 保存 HTML - 全页截图 print(f Navigating to:{url})# waitUntilnetworkidle 确保文档里的图片和图表加载完毕awaitpage.goto(url,wait_untilnetworkidle,timeout30000)# 获取页面标题清洗特殊字符titleawaitpage.title()safe_title.join([cforcintitleifc.isalpha()orc.isdigit()orc ]).rstrip()# --- 极客操作注入 CSS 净化页面 ---# 将所有的悬浮栏fixed/sticky转为正常流absolute/relative/static# 这样在截长图时导航栏就只会乖乖待在最顶部不会遮挡正文awaitpage.add_style_tag(content * { position: static !important; animation: none !important; transition: none !important; } /* 可选针对某些框架隐藏右侧滚动追踪目录防止干扰 */ .table-of-contents, .sidebar { display: block; } )# --------------------------------------# 生成路径html_filenamef{file_prefix}_{safe_title}.htmlpng_filenamef{file_prefix}_{safe_title}.pnghtml_pathos.path.join(save_dir,html_filename)png_pathos.path.join(save_dir,png_filename)# 1. 获取并保存渲染后的 HTML (用于后续文字复制和搜索)html_contentawaitpage.content()withopen(html_path,w,encodingutf-8)asf:f.write(html_content)# 2. 执行全页长图截图awaitpage.screenshot(pathpng_path,full_pageTrue)returntitle,html_path,png_path,html_content7️⃣ 核心实现哈希校验与增量检测 (Diff Hash)如果文档一个月都没更新我们为什么要重复截图呢我们需要一个聪明的diff_checker.py。# core/diff_checker.pyimporthashlibfrombs4importBeautifulSoupdefcalculate_content_hash(html_content): 仅提取页面的纯文本来计算 Hash。 避免因为时间戳、随机广告或无关 DOM 变动导致的误判。 soupBeautifulSoup(html_content,html.parser)# 移除脚本和样式forscriptinsoup([script,style,nav,footer]):script.extract()textsoup.get_text(separator ,stripTrue)# 生成 SHA-256hash_objhashlib.sha256()hash_obj.update(text.encode(utf-8))returnhash_obj.hexdigest()defis_page_changed(url,current_hash,manifest_data):通过对比 Manifest 历史记录判断是否需要重新归档# 假设 manifest 结构是以 url 为 key 的字典ifurlinmanifest_data:last_hashmanifest_data[url].get(content_hash)iflast_hashcurrent_hash:returnFalse# 没有变化returnTrue# 新增或已发生变化8️⃣ 数据存储与统一归档 (Manifest Zipping)我们将所有操作打包并且每次运行完毕后输出一份清晰的 Manifest 记录表方便我们查阅“哪天更新了什么”。importjsonimporttimefromdatetimeimportdatetimeimportshutilclassArchiveManager:def__init__(self,vault_dirvaults):self.vault_dirvault_dir self.manifest_pathos.path.join(vault_dir,doc_archive_manifest.json)self.manifest_dataself._load_manifest()def_load_manifest(self):ifos.path.exists(self.manifest_path):withopen(self.manifest_path,r,encodingutf-8)asf:returnjson.load(f)return{}defsave_manifest(self):withopen(self.manifest_path,w,encodingutf-8)asf:json.dump(self.manifest_data,f,indent4)defrecord_update(self,url,title,html_path,png_path,content_hash):更新总账本self.manifest_data[url]{page_title:title,latest_crawl_time:datetime.utcnow().isoformat()Z,html_path:html_path,screenshot_path:png_path,content_hash:content_hash}self.save_manifest()defcompress_vault(self,target_folder):将当天的归档文件夹打包为 ZIP节约空间shutil.make_archive(target_folder,zip,target_folder)print(f Compressed{target_folder}into ZIP format.)9️⃣ 运行方式与结果展示必写让我们把浏览器引擎和管理模块融合在run_scheduler.py中执行这一场华丽的归档启动主程序 (run_scheduler.py)importasynciofromplaywright.async_apiimportasync_playwrightimportosfromdatetimeimportdatetimefromcore.archiverimportpurify_and_snapshotfromcore.diff_checkerimportcalculate_content_hash,is_page_changedfromcore.archive_managerimportArchiveManagerasyncdefmain():target_urls[https://react.dev/reference/react,https://fastapi.tiangolo.com/tutorial/first-steps/]managerArchiveManager()today_strdatetime.now().strftime(%Y%m%d)version_diros.path.join(vaults,farchive_{today_str})os.makedirs(version_dir,exist_okTrue)print(\n Starting Documentation Archival Routine...\n)asyncwithasync_playwright()asp:browserawaitp.chromium.launch(headlessTrue)# 设置较大的 Viewport 宽度保证代码块不换行文档排版美观contextawaitbrowser.new_context(viewport{width:1920,height:1080})pageawaitcontext.new_page()foridx,urlinenumerate(target_urls):# 1. 抓取并获取基础内容try:awaitpage.goto(url,wait_untildomcontentloaded)temp_htmlawaitpage.content()current_hashcalculate_content_hash(temp_html)# 2. Diff 检测ifnotis_page_changed(url,current_hash,manager.manifest_data):print(f⏩ Skipped [No Changes]:{url})continue# 3. 如果变化了执行深度净化与截图file_prefixfdoc_{idx:03d}title,html_path,png_path,_awaitpurify_and_snapshot(page,url,version_dir,file_prefix)# 4. 更新账本manager.record_update(url,title,html_path,png_path,current_hash)print(f✅ Archived [Updated]:{title})exceptExceptionase:print(f❌ Failed to archive{url}:{e})awaitbrowser.close()# 5. 打包今日的归档数据manager.compress_vault(version_dir)print(\n Routine completed! Vault secured.)if__name____main__:asyncio.run(main())运行日志示例 Starting Documentation Archival Routine... Navigating to: https://react.dev/reference/react ✅ Archived [Updated]: Built-in React Hooks ⏩ Skipped [No Changes]: https://fastapi.tiangolo.com/tutorial/first-steps/ Compressed vaults/archive_20231027 into ZIP format. Routine completed! Vault secured.Manifest (JSON) 输出示例{https://react.dev/reference/react:{page_title:Built-in React Hooks,latest_crawl_time:2023-10-27T10:00:00Z,html_path:vaults/archive_20231027/doc_000_BuiltinReactHooks.html,screenshot_path:vaults/archive_20231027/doc_000_BuiltinReactHooks.png,content_hash:a1b2c3d4e5f6...}} 常见问题与排错极度硬核截图里有大片空白或者图片没加载出来原因现代文档很多使用了lazy-load懒加载图片技术。如果不往下滚动底部的图片根本不请求。终极对策在page.screenshot之前注入一段 JS 让浏览器自动滚到底部再滚回顶部。awaitpage.evaluate(() window.scrollTo(0, document.body.scrollHeight))awaitpage.wait_for_timeout(2000)# 等待图片加载awaitpage.evaluate(() window.scrollTo(0, 0))黑夜模式 (Dark Mode) 和白天模式来回横跳对策不同页面的默认主题可能不同。在browser.new_context()中强制固定系统配色方案color_schemelight或dark保证你整个归档库视觉风格高度统一部分代码块 (Code Snippets) 在长图里被截断了原因页面的 Viewport 太窄导致代码水平溢出。对策我们在上面的代码中设置了宽达1920的视口。如果遇到极端的长代码可以在净化 CSS 中加入.code-block { white-space: pre-wrap !important; }强制代码换行展示。1️⃣1️⃣ 进阶优化视觉像素级比对 (Image Diff)文字 Diff 查重虽然快但无法捕捉“UI 样式的改变”。如果你是一个追求完美的极客接入 Pixelmatch你可以利用 Python 的图像比对库如Pillow或OpenCV计算 SSIM 结构相似度。如果今天截的图和昨天的长图在像素结构上差异超过 1%再触发告警并归档。这才是真正的视觉级版本监控接入 GitHub Actions把这个 Python 脚本传到 GitHub。设置一个.github/workflows/cron.yml每天运行一次。利用 Actions 的 Artifacts 功能直接把生成的 ZIP 存入云端连本地硬盘都不用占了☁️1️⃣2️⃣ 总结与延伸阅读极客感悟今天我们不仅仅是在写一个爬虫我们是在对抗“数字世界的熵增”。通过这套自动化框架我们把易逝的 HTML 网页变成了坚如磐石、随时可查的离线知识金库。延伸从归档到检索既然你有了完美的 HTML 备份你完全可以将它与咱们之前聊过的“本地搜索引擎Whoosh”结合起来。自己建一个只属于你的、永远不会 404 的私有开发者文档检索引擎 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2434471.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!