Python爬虫实战:数字时光机 - 基于 Playwright 的网页全貌归档系统(HTML + 截图)(附CSV导出 + SQLite持久化存储)!

㊗️本期内容已收录至专栏《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) 三种格式的存档文件。

读完这篇,你能获得:

  1. 🖼️ 能够对任意复杂的动态网页进行"长截图"和"源码备份"。
  2. 🕵️ 掌握如何处理懒加载(Lazy-load)图片,确保截图不留白。
  3. 🗂️ 建立一套按时间戳自动归档的文件管理系统,适用于法律取证或竞品监测。

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)🔧

  1. 截图里的字体全是方块/乱码?

    • 原因:Linux 服务器(如 Docker/Ubuntu)上通常没有中文字体。

    • 解决 :在系统级安装字体 sudo apt-get install fonts-noto-cjk,或者在 Playwright 启动时加载本地字体文件。

  2. Cookie 弹窗挡住了网页内容?

    • 原因:欧盟 GDPR 或各种"接受 Cookie"的遮罩层。

    • 解决:在截图前,使用选择器定位"Accept"或"关闭"按钮并点击。

      python 复制代码
      try:
          page.click("text=Accept All Cookies", timeout=3000)
      except:
          pass # 没找到就算了
  3. 截图只有首下面全是黑的?

    • 原因 :滚动逻辑失效,或者页面是 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爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
@陈小鱼1 小时前
基于 Savitzky-Golay滤波器的超声图像运动分析方法
python·计算机视觉·matlab·信号处理
七夜zippoe1 小时前
属性测试革命:Hypothesis框架深度实战指南
python·性能测试·数据驱动·hypothesis·状态机测试
艾醒(AiXing-w)2 小时前
技术速递——通义千问 3.5 深度横评:纸面超越 GPT‑5.2,实测差距在哪?
人工智能·python·语言模型
喵手2 小时前
Python爬虫实战:自动化构建 arXiv 本地知识库 - 从 PDF 下载到元数据索引!
爬虫·python·自动化·arxiv·本地知识库·pdf下载·元数据索引
百锦再2 小时前
Java InputStream和OutputStream实现类完全指南
java·开发语言·spring boot·python·struts·spring cloud·kafka
闲人编程2 小时前
Celery分布式任务队列
redis·分布式·python·celery·任务队列·异步化
deephub2 小时前
深入RAG架构:分块策略、混合检索与重排序的工程实现
人工智能·python·大语言模型·rag
danyang_Q2 小时前
vscode python-u问题
开发语言·vscode·python
忘忧记3 小时前
python QT sqlsite版本 图书管理系统
开发语言·python·qt