㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写)⚠️](#3️⃣ 合规与注意事项(必写)⚠️)
- [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
- [5️⃣ 环境准备与依赖安装(可复现)🛠️](#5️⃣ 环境准备与依赖安装(可复现)🛠️)
- [6️⃣ 核心实现:浏览器交互层(Fetcher)🌐](#6️⃣ 核心实现:浏览器交互层(Fetcher)🌐)
- [7️⃣ 核心实现:懒加载处理与解析(Action/Parser)🖱️](#7️⃣ 核心实现:懒加载处理与解析(Action/Parser)🖱️)
- [8️⃣ 数据存储与导出(Storage)💾](#8️⃣ 数据存储与导出(Storage)💾)
- [9️⃣ 运行方式与结果展示(必写)🚀](#9️⃣ 运行方式与结果展示(必写)🚀)
- [🔟 常见问题与排错(Troubleshooting)🔧](#🔟 常见问题与排错(Troubleshooting)🔧)
- [1️⃣1️⃣ 进阶优化(可选但加分)⚡](#1️⃣1️⃣ 进阶优化(可选但加分)⚡)
- [1️⃣2️⃣ 总结与延伸阅读 📝](#1️⃣2️⃣ 总结与延伸阅读 📝)
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
1️⃣ 摘要(Abstract)
本文将弃用传统的 HTTP 库,转而使用微软开源的浏览器自动化神器 Playwright 。我们将构建一个自动化脚本,能够模拟真实用户访问,处理动态渲染(JavaScript),并同时产出全屏长截图(PNG) 、原始结构(HTML)以及打印版(PDF) 三种格式的存档文件。
读完这篇,你能获得:
- 🖼️ 能够对任意复杂的动态网页进行"长截图"和"源码备份"。
- 🕵️ 掌握如何处理懒加载(Lazy-load)图片,确保截图不留白。
- 🗂️ 建立一套按时间戳自动归档的文件管理系统,适用于法律取证或竞品监测。
2️⃣ 背景与需求(Why)
为什么要爬(归档)?
文本数据往往丢失了上下文。在以下场景中,"视觉证据"比"文本数据"更重要:
- 法律与合规留证:证明某网站在"某年某月某日"展示了虚假广告或侵权内容。
- 竞品监测:记录竞争对手的首页 Banner 设计、活动排版变化。
- 历史回溯:由于互联网信息的易逝性(404 Not Found),我们需要将重要的技术文档或新闻永久保存在本地。
目标产出:
- Snapshot.png:像素级还原的全网页长图。
- Source.html:渲染后的 DOM 结构(包含 JS 修改后的内容)。
- Report.pdf:可搜索文本的 PDF 版本(可选)。
3️⃣ 合规与注意事项(必写)⚠️
- 版权红线 :你可以将截图保存在本地作为个人研究或证据,但严禁将他人的网页截图发布在公开场合(可能侵犯版式设计权或著作权)。
- 服务器压力:Playwright 启动的是真实浏览器内核(Chromium/Webkit),资源消耗极大。请务必控制并发数(建议单机不超过 3-5 个并发),不要把对方服务器挤爆。
- 隐私保护:在截图时,如果页面包含你的个人登录信息(如头像、账号),请在代码中添加"遮罩层"逻辑进行打码处理。
4️⃣ 技术选型与整体流程(What/How)
技术栈选择 :Playwright (Python版)
-
为什么不用 Selenium?
- Selenium 配置繁琐(需对应 WebDriver 版本)。
- Selenium 截取"全屏长图"非常痛苦,需要拼接,容易错位。
- Playwright 原生支持
fullPage: true,速度比 Selenium 快 3-5 倍,且能更好地处理 Headless(无头)模式下的渲染问题。
流程设计:
启动浏览器引擎 ➡️ 新建上下文 (Context) ➡️ 访问页面 (Goto) ➡️ 模拟滚动 (解决懒加载) ➡️ 等待网络空闲 ➡️ 截图/存HTML ➡️ 清理资源
5️⃣ 环境准备与依赖安装(可复现)🛠️
Python 版本:3.8+
1. 安装 Python 库:
bash
pip install playwright
2安装浏览器内核:
这是 Playwright 最爽的一点,一行命令自动配置环境,不用到处找驱动。
bash
playwright install chromium
(注:如果下载慢,请确保你的网络环境通,或者指定国内源)
项目目录:
text
archive_project/
├── archive/ # 存档结果目录
├── main.py # 主程序
└── utils.py # 工具函数(滚动、命名等)
6️⃣ 核心实现:浏览器交互层(Fetcher)🌐
我们要编写一个具有"拟人化"特征的浏览器启动器。
python
from playwright.sync_api import sync_playwright
import time
def fetch_and_archive(url, output_dir):
"""
核心归档函数:启动浏览器 -> 加载 -> 截图 -> 保存
"""
with sync_playwright() as p:
# 1. 启动 Chromium 浏览器
# headless=True 表示无头模式(不弹出窗口),服务器端运行必备
# slow_mo=50 表示每个操作减慢 50ms,模拟人类反应,防止触发反爬
browser = p.chromium.launch(headless=True, slow_mo=50)
# 2. 创建上下文(相当于一个独立的浏览器窗口,隔离 Cookie)
# viewport 设置分辨率,模拟 1080P 屏幕,确保布局正常
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36"
)
page = context.new_page()
print(f"🌍 正在访问: {url}")
try:
# 3. 跳转并等待
# wait_until='networkidle' 是关键!表示直到网络连接变为空闲(没有新请求)才算加载完
# timeout=60000 设置 60秒超时,防止网页太慢导致报错
page.goto(url, wait_until='networkidle', timeout=60000)
# --- 关键步骤:处理懒加载 ---
auto_scroll(page)
# 再次等待,确保滚动后的图片加载完成
page.wait_for_timeout(2000)
# 4. 执行保存操作(详见下一节)
save_artifacts(page, url, output_dir)
except Exception as e:
print(f"❌ 归档失败 {url}: {e}")
finally:
browser.close()
详细解析:
waittil='networkidle':这是静态爬虫没有的。很多现代网页(如 SPA 单页应用)加载完 HTML 后,还会发一堆 API 请求去拿数据。如果不加这个参数,你截出来的图可能是空的骨架屏。viewport:如果不设置,默认视口可能很小(800x600),导致网页变成移动端布局或菜单折叠。为了证据的完整性,一定要设为桌面级分辨率。
7️⃣ 核心实现:懒加载处理与解析(Action/Parser)🖱️
很多网站的图片是"滑到哪里加载到哪里"(Lazy Load)。如果直接截图,底部全是灰色方块。我们需要注入 JavaScript 控制浏览器自动滚到底。
python
def auto_scroll(page):
"""
模拟人工滚动到底部,触发懒加载图片
"""
print("⬇️ 开始模拟滚动...")
# page.evaluate 允许我们在浏览器上下文中执行原生 JS
page.evaluate("""
async () => {
await new Promise((resolve, reject) => {
var totalHeight = 0;
var distance = 300; // 每次滚动的距离
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
// 如果滚到底了,或者超过了极其夸张的高度(防止死循环),就停止
if(totalHeight >= scrollHeight || totalHeight > 30000){
clearInterval(timer);
resolve();
}
}, 100); // 每 100ms 滚一次
});
}
""")
print("✅ 滚动完成")
详细:
- 这段代码的精髓在于
page.evaluate。我们不是用 Python 去控制滚动,而是把一段 JS 脚本"注射"到网页里运行。 - 逻辑是:每隔 100 毫秒向下滚 300 像素,直到滚不动为止。这样能确保所有
<img>标签进入视口,触发src加载。
8️⃣ 数据存储与导出(Storage)💾
这里我们要规划文件的命名规范,确保文件多了之后也能找得到。
python
import os
from datetime import datetime
from urllib.parse import urlparse
def save_artifacts(page, url, output_dir):
# 1. 生成基于域名的文件夹
domain = urlparse(url).netloc.replace('www.', '')
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
# 最终目录:./archive/example.com/20260204_120000/
save_path = os.path.join(output_dir, domain, timestamp)
os.makedirs(save_path, exist_ok=True)
# 2. 保存全屏截图 (PNG)
# full_page=True 是 Playwright 的杀手锏,自动拼接长图
screenshot_path = os.path.join(save_path, "screenshot.png")
page.screenshot(path=screenshot_path, full_page=True)
print(f"📸 截图已保存: {screenshot_path}")
# 3. 保存 PDF (可选)
# format='A4' 自动分页,print_background=True 保留背景色
pdf_path = os.path.join(save_path, "print_version.pdf")
page.pdf(path=pdf_path, format='A4', print_background=True)
# 4. 保存 HTML (源码)
# 此时拿到的 content 是经过 JS 修改后的最终 DOM
html_path = os.path.join(save_path, "source.html")
with open(html_path, 'w', encoding='utf-8') as f:
f.write(page.content())
print(f"💾 归档完成,文件位于: {save_path}")
9️⃣ 运行方式与结果展示(必写)🚀
创建 main.py 将上述代码串联。
python
# main.py
from scraper import fetch_and_archive # 假设上面的函数在 scraper.py
if __name__ == "__main__":
targets = [
"https://www.apple.com/cn/", # 测试复杂的视觉布局
"https://news.ycombinator.com/", # 测试纯文本结构
]
print("🚀 启动数字档案馆...")
for url in targets:
fetch_and_archive(url, output_dir="./archive_data")
time.sleep(2) # 缓冲一下
print("\n🎉 所有任务执行完毕!")
运行命令 :
pythonmain.py
结果示例 :
打开文件夹,你会看到如下结构:
text
archive_data/
└── apple.com/
└── 20260204_083500/
├── screenshot.png (一张 1920x8000+ 的超长高清图)
├── print_version.pdf
└── source.html
🔟 常见问题与排错(Troubleshooting)🔧
-
截图里的字体全是方块/乱码?
-
原因:Linux 服务器(如 Docker/Ubuntu)上通常没有中文字体。
-
解决 :在系统级安装字体
sudo apt-get install fonts-noto-cjk,或者在 Playwright 启动时加载本地字体文件。
-
-
Cookie 弹窗挡住了网页内容?
-
原因:欧盟 GDPR 或各种"接受 Cookie"的遮罩层。
-
解决:在截图前,使用选择器定位"Accept"或"关闭"按钮并点击。
pythontry: page.click("text=Accept All Cookies", timeout=3000) except: pass # 没找到就算了
-
-
截图只有首下面全是黑的?
- 原因 :滚动逻辑失效,或者页面是
overflow: hidden的特殊布局。 - 解决 :检查 CSS 样式,或者直接给
body设置高度。
- 原因 :滚动逻辑失效,或者页面是
1️⃣1️⃣ 进阶优化(可选但加分)⚡
-
广告拦截(AdBlock) :
截图里全是广告很难看。Playwright 可以通过拦截请求来屏蔽广告:
python# 屏蔽包含 ads 的请求 page.route("**/*", lambda route: route.abort() if "ads" in route.request.url else route.continue_()) -
图像对比(Diff) :
利用
Pillow库,对比今天和昨天的截图。如果像素差异超过 10%,则发送邮件报警:"竞品首页改版了!" -
Docker 化部署 :
将脚本打包进 Docker,配合 Cron 定时任务,每天凌晨 2 点自动对目标列表进行"巡检"。
1️⃣2️⃣ 总结与延伸阅读 📝
这篇教程带你完成了一个工业级的网页归档工具。不同于简单的爬虫,我们利用 Playwright 完美复刻了人类浏览网页的视觉体验,并将其永久冻结在硬盘里。
复盘核心点:
- Requests 只能抓皮(HTML),Playwright 才能抓肉(渲染后的视觉)。
- 处理懒加载(Auto-scroll)是截图完整的关键。
- 文件结构化存储让数据有了检索价值。
下一步可以做什么?
你可以尝试给这个工具加一个 Web UI(用 Streamlit),让不懂代码的同事输入 URL,就能一键生成并下载截图包。这绝对是办公室里的效率神器!🛠️
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
