开发技巧

Bright Data:用 Playwright 抓取必须由 JavaScript 渲染才显示内容的网页,并自动点击“下一页”翻页

用 Playwright 抓取必须由 JavaScript 渲染才显示内容的网页,并自动点击“下一页”翻页。开发时用本机 Chrome 调试,上线时只改一个环境变量,就切换到 Bright Data Browser API 的远程浏览器运行。

BINBHO · 图文教程

用 Playwright 抓取必须由 JavaScript 渲染才显示内容的网页,并自动点击“下一页”翻页。开发时用本机 Chrome 调试,上线时只改一个环境变量,就切换到 Bright Data Browser API 的远程浏览器运行。

先看清成品

成品是 quotes.py:打开练习站 quotes.toscrape.com/js(内容由 JS 生成,直接请求 HTML 拿不到),等待渲染、提取名言、作者和标签,翻页后保存为 quotes.json。本文代码已用本机 Chrome 和 Playwright 1.64.0 实际运行:抓取 3 页共 30 条、内容无重复,第一条作者是 Albert Einstein。本文环境没有 Bright Data 账号,连接远程 Browser API 的部分没有实际运行,连接地址格式按官方示例编写。

注意:采集前先读目标网站的服务条款和 robots.txt,只采集公开、非个人的数据,控制频率,不绕过登录和付费墙,不采集个人信息。本文示例使用专门供练习的网站 toscrape.com。用于商业用途前请咨询法律意见,并遵守所在地关于数据和个人信息保护的法律。

做完之后你会得到什么?

成果 说明
JS 渲染抓取 等元素出现后再提取
自动翻页 点击“Next”直到最后一页或页数上限
双模式 本机 Chrome 调试,Browser API 上线
JSON 输出 中文不转义,方便查看

需要准备哪些环境?

项目 要求 说明
Bright Data 账号 新账号每月有 5,000 免费额度 以官方免费额度页面为准,额度用完会停止,不会自动扣费
Python 3.10 或更高 建议使用虚拟环境
Playwright pip install playwright 本文使用 1.64.0
本机浏览器 已安装 Chrome,或执行 playwright install chromium 本地调试用

官方免费额度页面说明 Browser API 也使用每月免费额度,按流量计费(每 MB 5 个额度)。

第一步:为什么需要浏览器?

先确认页面确实需要渲染:

curl -s https://quotes.toscrape.com/js/ | grep -c 'class="quote"'

输出 0,说明 HTML 里没有内容,名言是浏览器执行 JS 后才生成的。这时 Web Unlocker 这类“拿 HTML”的方式不够用,需要真正的浏览器。

第二步:怎样准备 Browser API?

在控制台添加 Browser API 区域,Overview 里能看到用户名(形如 brd-customer-xxx-zone-yyy)和密码。官方示例的连接地址是:

wss://用户名:密码@brd.superproxy.io:9222

第三步:代码怎么写?

1. 项目结构

js-scraper/
├── quotes.py
└── quotes.json    # 运行后生成

2. quotes.py

"""抓取需要 JavaScript 渲染的页面(练习站 quotes.toscrape.com/js),支持翻页。
配置 BRIGHTDATA_BROWSER_AUTH 时连接 Bright Data Browser API;否则用本机 Chrome 调试。
用法:python quotes.py --pages 3
"""
import argparse
import asyncio
import json
import os

from playwright.async_api import async_playwright

START = "https://quotes.toscrape.com/js/"


async def open_browser(p):
    auth = os.environ.get("BRIGHTDATA_BROWSER_AUTH")  # 形如 brd-customer-xxx-zone-yyy:密码
    if auth:
        return await p.chromium.connect_over_cdp(f"wss://{auth}@brd.superproxy.io:9222")
    return await p.chromium.launch(channel=os.environ.get("LOCAL_CHANNEL", "chrome"), headless=True)


async def scrape(max_pages: int):
    results = []
    async with async_playwright() as p:
        browser = await open_browser(p)
        page = await browser.new_page()
        await page.goto(START, timeout=120_000)
        for n in range(1, max_pages + 1):
            await page.wait_for_selector("div.quote")  # 等 JS 把内容渲染出来
            items = await page.eval_on_selector_all(
                "div.quote",
                """els => els.map(e => ({
                    text: e.querySelector('.text').innerText,
                    author: e.querySelector('.author').innerText,
                    tags: [...e.querySelectorAll('.tag')].map(t => t.innerText)
                }))""",
            )
            results.extend(items)
            print(f"第 {n} 页:{len(items)} 条")
            nxt = page.locator("li.next > a")
            if n == max_pages or await nxt.count() == 0:
                break
            await nxt.click()
            await page.wait_for_load_state("domcontentloaded")
        await browser.close()
    return results


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--pages", type=int, default=2)
    ap.add_argument("--out", default="quotes.json")
    a = ap.parse_args()
    data = asyncio.run(scrape(a.pages))
    with open(a.out, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    print(f"共 {len(data)} 条,已保存到 {a.out}")


if __name__ == "__main__":
    main()
要点 说明
connect_over_cdp 连接远程浏览器,官方示例同样使用这个方法
channel="chrome" 本地直接用已安装的 Chrome
wait_for_selector 不要用固定等待时间,等元素出现最可靠
eval_on_selector_all 在浏览器里一次性提取,减少往返
页数上限 防止意外翻页过多消耗额度

第四步:怎样运行?

1. 本机调试

python3 -m venv .venv && source .venv/bin/activate
pip install playwright
python quotes.py --pages 3

没有安装 Chrome 时,先执行 playwright install chromium,再用 LOCAL_CHANNEL=chromium python quotes.py。

本文实际输出:

第 1 页:10 条
第 2 页:10 条
第 3 页:10 条
共 30 条,已保存到 quotes.json

2. 切换到 Browser API

export BRIGHTDATA_BROWSER_AUTH='brd-customer-xxx-zone-yyy:你的密码'
python quotes.py --pages 3

代码其他部分不用改。

3. 检查结果

检查项 期望
quotes.json 30 条,每条有 text、author、tags
无重复 翻页成功时每页内容不同
中文环境 文件直接可读

出问题时该怎么调?

1. 报 Executable doesn't exist

本机没有 Chrome,Playwright 也没下载浏览器。执行 playwright install chromium 并设置 LOCAL_CHANNEL=chromium。

2. wait_for_selector 超时

原因 解决方法
选择器写错 用浏览器开发者工具确认
页面加载慢 加大超时,例如 timeout=60000
被拦截显示验证页 截图看看:await page.screenshot(path="debug.png")

3. 远程连接失败

检查用户名密码是否是 Browser API 区域的(不是 Web Unlocker 区域),以及网络是否允许连接 9222 端口。

常见问题怎么处理?

现象 原因 解决方法
每页数据相同 点了下一页但没等新内容 等待 URL 或元素变化
额度消耗快 加载了图片和视频 只抓需要的页面,控制页数
本地正常远程失败 远程会话有时长限制 拆成多个较短任务
输出为空列表 选择器不匹配 先在本地有界面模式(headless=False)观察
需要登录才能看 涉及账号 不要用于绕过登录,遵守网站条款

项目资源

用途 地址
Browser API 介绍 https://docs.brightdata.com/products/scraping-browser/introduction
Browser API 代码示例 https://docs.brightdata.com/products/scraping-browser/code-examples
Browser API 错误码 https://docs.brightdata.com/products/scraping-browser/error-codes
免费额度 https://docs.brightdata.com/general/account/billing-and-pricing/free-tier
Playwright Python https://playwright.dev/python/docs/intro
练习站 Quotes to Scrape(JS 版) https://quotes.toscrape.com/js/

建议先在本机跑 python quotes.py --pages 1,确认能拿到 10 条,再加大页数。

本地完全跑通后,再设置 BRIGHTDATA_BROWSER_AUTH 用远程浏览器跑一页,对比结果一致后再正式使用。