欢迎访问 AI Skills Video ! 海量优质视频教程,助你提升技能。

利用Playwright Python库实现站点抓取:从基础操作到问题解决

老张 2026年4月23日 11 次阅读
本文将从实操角度出发,详细讲解如何利用Playwright完成前端站点抓取的核心需求,包括页面浏览、DOM导出、屏幕快照、二级页面抓取,并针对常见的人机验证问题提供解决方案,全程附可直接运行的代码示例,帮助开发者快速上手。

一、环境准备:Playwright初始化

在开始抓取前,需先完成Playwright的安装和环境配置。Playwright支持Python、JavaScript/TypeScript等多种语言,本文以Python为例(应用最广泛,上手门槛低)。

1.1 安装依赖

执行以下命令安装Playwright及浏览器驱动(Playwright会自动下载对应浏览器的驱动,无需手动配置):

# 安装Playwright
pip install playwright
# 下载浏览器驱动(Chromium、Firefox、WebKit)
playwright install

1.2 核心概念说明

在编写代码前,需了解Playwright的3个核心对象,这是后续所有操作的基础:

  • Playwright实例:全局入口,用于启动和管理浏览器;

  • Browser(浏览器):可理解为真实的浏览器窗口,支持无头模式(headless=True,不显示界面,适合服务器运行)和有头模式(headless=False,便于调试);

  • Page(页面):浏览器中的一个标签页,所有页面操作(浏览、点击、注入JS等)都通过该对象完成。

二、核心操作:前端站点抓取的4个关键步骤

前端站点抓取的核心需求的是获取页面的“可见内容”和“结构化数据”,结合Playwright的特性,我们可通过以下4个步骤实现完整抓取流程。

2.1 利用Playwright浏览页面

Playwright的优势之一是“自动等待”——无需手动设置sleep等待页面加载,它会自动等待页面元素渲染完成、网络请求结束,避免因页面未加载完成导致的抓取失败。

基础浏览操作示例(以抓取某前端演示站点为例):

from playwright.sync_api import sync_playwright

# 启动Playwright,使用with语句自动释放资源
with sync_playwright() as p:
    # 启动Chromium浏览器,headless=False便于调试
    browser = p.chromium.launch(headless=False)
    # 新建一个页面标签
    page = browser.new_page()
    # 浏览目标页面,自动等待页面加载完成
    page.goto("https://example.com")  # 替换为目标站点URL
    
    # 可选:设置页面视口大小(模拟不同设备)
    page.set_viewport_size({"width": 1920, "height": 1080})
    
    # 可选:模拟用户操作(如滚动页面,加载更多内容)
    page.mouse.wheel(0, 1000)  # 向下滚动1000像素
    page.wait_for_timeout(1000)  # 可选:手动等待1秒(应对极特殊的异步加载)
    
    # 后续操作...(导出DOM、截图等)
    
    # 关闭浏览器
    browser.close()

关键说明:page.goto() 方法会等待页面的load事件完成,对于SPA(单页应用),可使用page.wait_for_load_state("networkidle")等待网络请求完全空闲,确保所有动态内容加载完毕。

2.2 利用JS导出document DOM

抓取页面的核心是获取DOM结构,Playwright支持通过page.evaluate()方法注入JavaScript代码,直接操作页面DOM,导出完整的DOM结构(包括动态渲染的内容)。

导出DOM的两种常用方式(按需选择):

方式1:导出完整DOM字符串

将整个页面的DOM序列化为字符串,可保存为HTML文件,便于后续分析或二次解析:

# 接上面的代码,在page.goto()之后添加
# 注入JS,序列化document.documentElement(整个HTML根节点)
dom_str = page.evaluate("""() => {
    const serializer = new XMLSerializer();
    return serializer.serializeToString(document.documentElement);
}""")

# 将DOM字符串保存为HTML文件
with open("page_dom.html", "w", encoding="utf-8") as f:
    f.write(dom_str)

print("DOM导出完成,已保存至page_dom.html")

原理:通过XMLSerializer将DOM节点转为字符串,相比直接获取document.documentElement.outerHTML,这种方式能更完整地保留DOM结构和属性,尤其适合复杂页面。

方式2:导出指定DOM节点的内容

如果不需要完整DOM,只需抓取页面中特定区域(如列表、详情),可通过JS定位节点并导出内容,减少数据冗余:

# 注入JS,获取指定类名的节点内容(示例:抓取列表项)
list_items = page.evaluate("""() => {
    // 定位所有class为"list-item"的节点
    const items = document.querySelectorAll(".list-item");
    // 提取节点的文本和链接,返回数组
    return Array.from(items).map(item => ({
        text: item.textContent.trim(),
        link: item.querySelector("a")?.href || ""  // 提取链接(可选)
    }));
}""")

# 打印结果
print("抓取的列表项:", list_items)

关键说明:page.evaluate()中注入的JS代码运行在浏览器环境中,可直接使用原生JS的DOM操作方法(如querySelectorAlltextContent),返回的数据会自动序列化并传递到Python环境中。

2.3 利用屏幕截图抓取快照

在某些场景下(如页面内容无法通过DOM解析、需要留存页面视觉证据),需抓取页面快照(截图)。Playwright的截图功能支持全屏截图、区域截图、元素截图,灵活满足不同需求。

截图操作示例(3种常用场景):

# 1. 全屏截图(包含整个页面,自动滚动)
page.screenshot(
    path="full_page_screenshot.png",  # 保存路径
    full_page=True,  # 关键:开启全屏截图
    quality=90  # 图片质量(0-100),仅对jpg有效
)

# 2. 区域截图(指定坐标和大小)
page.screenshot(
    path="area_screenshot.png",
    clip={"x": 100, "y": 100, "width": 800, "height": 600}  # x,y为左上角坐标
)

# 3. 元素截图(抓取指定DOM元素)
# 先定位元素,再截图
target_element = page.locator(".header-banner")  # 定位头部横幅元素
target_element.screenshot(path="element_screenshot.png")

print("截图完成,已保存对应文件")

补充说明:Playwright的截图API支持多种参数,如设置图片格式(type="jpeg")、忽略背景(omit_background=True)等,可根据需求调整,具体参数可参考官方文档。

2.4 管理页面链接列表,抓取二级页面

很多场景下,我们需要先抓取一级页面的所有链接,再批量访问这些链接(二级页面),实现多页面抓取。通过JS代码可轻松管理页面内的链接列表,过滤无效链接后批量处理。

完整示例(一级页面提取链接 → 批量抓取二级页面):

from playwright.sync_api import sync_playwright

def crawl_secondary_pages(base_url):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto(base_url)
        
        # 第一步:提取一级页面的所有有效链接(过滤无效链接)
        links = page.evaluate("""() => {
            const allLinks = Array.from(document.querySelectorAll("a"));
            // 过滤条件:1. 非空 2. 以http开头(排除锚点、javascript链接)3. 包含当前域名(避免跳转到外部站点)
            return allLinks
                .map(link => link.href.trim())
                .filter(href => href && href.startsWith("http") && href.includes(window.location.host));
        }""")
        
        # 去重(避免重复抓取同一链接)
        unique_links = list(set(links))
        print(f"一级页面共提取到 {len(unique_links)} 个有效二级页面链接")
        
        # 第二步:批量抓取二级页面的内容(以提取二级页面标题为例)
        secondary_data = []
        for link in unique_links[:5]:  # 先抓取前5个,避免请求过多被限制
            try:
                # 新建页面打开二级链接(避免影响原页面)
                new_page = browser.new_page()
                new_page.goto(link, timeout=10000)  # 设置超时时间10秒
                
                # 提取二级页面标题和关键内容
                page_info = new_page.evaluate("""() => ({
                    title: document.title,
                    content: document.querySelector(".content")?.textContent?.trim() || "无内容"
                })""")
                page_info["url"] = link
                secondary_data.append(page_info)
                
                new_page.close()  # 关闭当前二级页面,节省资源
                print(f"成功抓取二级页面:{link}")
            except Exception as e:
                print(f"抓取二级页面 {link} 失败:{str(e)}")
        
        # 保存二级页面数据
        with open("secondary_pages_data.json", "w", encoding="utf-8") as f:
            import json
            json.dump(secondary_data, f, ensure_ascii=False, indent=2)
        
        browser.close()
        print("二级页面抓取完成,数据已保存至secondary_pages_data.json")

# 调用函数,替换为目标一级页面URL
crawl_secondary_pages("https://example.com")

关键技巧:

  • 链接过滤:通过href.includes(window.location.host)过滤外部链接,避免抓取无关站点;

  • 去重处理:使用set去重,防止同一链接被多次抓取;

  • 资源控制:每次抓取二级页面后关闭页面(new_page.close()),避免浏览器占用过多内存;

  • 超时设置:goto()设置timeout,防止因页面加载过慢导致程序卡死。

三、常见问题:人机验证的应对方案

在前端抓取过程中,最常见的障碍就是人机验证(如滑块验证、图形验证、reCAPTCHA等)。这类验证的核心目的是区分人类和机器,而Playwright的自动化行为容易被检测到,导致抓取失败。

以下是3种实用的应对方案,从简单到复杂,按需选择:

3.1 方案1:模拟人类行为,降低检测概率

很多站点的人机验证是基于“异常行为”触发的(如页面加载后立即操作、请求频率过快、无鼠标移动等),通过模拟人类操作节奏,可降低触发验证的概率:

# 优化后的浏览代码,模拟人类行为
with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        # 禁用自动化标识,避免被检测
        args=["--disable-blink-features=AutomationControlled"]
    )
    # 创建浏览器上下文,设置用户代理(模拟真实浏览器)
    context = browser.new_context(
        user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        viewport={"width": 1920, "height": 1080}
    )
    page = context.new_page()
    
    # 模拟人类浏览节奏:缓慢加载页面、随机鼠标移动
    page.goto("https://example.com", wait_until="networkidle")
    # 随机等待1-3秒(模拟人类阅读页面)
    import random
    page.wait_for_timeout(random.randint(1000, 3000))
    # 模拟鼠标移动(从页面左上角移动到中间)
    page.mouse.move(100, 100)
    page.wait_for_timeout(500)
    page.mouse.move(500, 500)
    
    # 后续抓取操作...
    context.close()
    browser.close()

关键优化点:

  • 禁用自动化标识:--disable-blink-features=AutomationControlled 可隐藏Playwright的自动化痕迹;

  • 设置真实用户代理:避免使用Playwright默认的用户代理,模拟普通浏览器;

  • 随机等待和鼠标移动:模拟人类的操作节奏,避免机械性操作。

3.2 方案2:复用浏览器上下文,绕过重复验证

如果站点的人机验证是一次性的(登录后或验证一次后,后续请求不再验证),可通过保存浏览器上下文(如Cookie、LocalStorage),复用登录状态或验证状态,避免重复触发验证:

# 第一步:首次运行,手动完成人机验证,保存上下文状态
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://example.com")
    
    # 手动完成人机验证(此时页面已通过验证)
    input("请手动完成人机验证,完成后按Enter继续...")
    
    # 保存上下文状态(包含Cookie、LocalStorage等)到文件
    context.storage_state(path="auth_state.json")
    
    browser.close()

# 第二步:后续抓取,加载已保存的上下文,无需再次验证
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    # 加载保存的上下文状态
    context = browser.new_context(storage_state="auth_state.json")
    page = context.new_page()
    
    # 直接访问目标页面,已通过验证
    page.goto("https://example.com")
    # 后续抓取操作...(无需再次验证)
    
    browser.close()

适用场景:站点的验证状态通过Cookie或LocalStorage保存,一次验证后可长期复用(如论坛、电商站点)。

3.3 方案3:借助第三方服务,自动识别验证

如果上述两种方案无效,可借助第三方验证码识别服务(如2Captcha、超级鹰),自动识别滑块、图形等验证,实现全自动抓取。以2Captcha为例,核心思路是:

  1. 当页面出现验证时,截图验证区域;

  2. 将截图上传到第三方服务,获取验证结果;

  3. 通过Playwright模拟操作,输入验证结果,完成验证。

核心代码示例(简化版):

import requests

# 第三方验证码识别API(以2Captcha为例,需注册获取API_KEY)
API_KEY = "你的2Captcha API_KEY"
SITE_KEY = "目标站点的reCAPTCHA Site Key"  # 从目标页面源码中获取

def solve_captcha(url):
    # 1. 提交验证码任务
    submit_url = f"http://2captcha.com/in.php?key={API_KEY}&method=userrecaptcha&googlekey={SITE_KEY}&pageurl={url}"
    response = requests.get(submit_url)
    if "OK" not in response.text:
        raise Exception("提交验证码任务失败")
    captcha_id = response.text.split("|")[1]
    
    # 2. 等待识别结果
    import time
    while True:
        result_url = f"http://2captcha.com/res.php?key={API_KEY}&action=get&id={captcha_id}"
        result = requests.get(result_url).text
        if "OK|" in result:
            return result.split("|")[1]  # 返回验证结果
        time.sleep(2)  # 每2秒查询一次

# 在Playwright中调用,完成验证
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://example.com")
    
    # 检测到验证框时,调用识别函数
    if page.locator("#captcha-box").is_visible():
        captcha_result = solve_captcha(page.url)
        # 注入JS,提交验证结果(需根据目标站点的验证逻辑调整)
        page.evaluate(f"""() => {{
            document.getElementById("captcha-input").value = "{captcha_result}";
            document.getElementById("captcha-submit").click();
        }}""")
        page.wait_for_load_state("networkidle")
    
    # 后续抓取操作...
    browser.close()

注意事项:第三方服务需要付费(按验证次数计费),且需严格遵守目标站点的 robots.txt 协议,避免违规抓取。

四、总结与注意事项

4.1 核心总结

Playwright作为前端抓取工具,其核心优势在于“原生支持动态页面”和“灵活的JS注入能力”,通过本文的4个核心操作(页面浏览、DOM导出、截图、二级页面抓取),可覆盖绝大多数前端站点的抓取需求;而针对人机验证问题,可根据站点的验证强度,选择“模拟人类行为”“复用上下文”“第三方识别”三种方案逐步突破。

4.2 重要注意事项

  • 合规性:抓取前需查看目标站点的 robots.txt 协议,避免抓取违规内容;不要频繁发送请求,可设置合理的等待时间,防止给服务器造成压力。

  • 稳定性:实际抓取中,建议增加异常捕获(try-except),处理页面加载超时、元素未找到等问题;对于动态加载频繁的页面,可使用page.wait_for_selector()等待目标元素出现。

  • 版本兼容:Playwright版本更新较快,部分API可能会有变化,建议使用稳定版本(如1.40.0),并参考官方文档进行开发。

通过本文的实操教程,相信你已经掌握了Playwright前端抓取的核心技巧。在实际应用中,可根据目标站点的特性,灵活调整代码逻辑,实现高效、稳定的抓取。如果遇到复杂的动态页面或验证场景,可进一步探索Playwright的高级API(如网络请求拦截、模拟手机设备等),解锁更多抓取能力。