在 Apify 上使用 Bright Data 的抓取浏览器进行网页抓取

探索如何将 Bright Data 的 Scraping Browser 集成到 Apify 中,以提升网页抓取效率、降低成本,并成功应对反抓取挑战。
7 min read
抓取浏览器与 Apify

在这篇博文中,你将学到:

  • 什么是 Apify
  • 为什么在 Apify 上使用 Scraping Browser 能实现双赢
  • 如何在 Apify 的 Python 脚本中集成 Bright Data 的 Scraping Browser
  • 如何在 Apify 上使用 Bright Data 的代理

让我们开始吧!

什么是 Apify?

Apify 是一个完整的网页抓取和数据提取平台。它允许你在云端创建并运行自定义的网络抓取工具——也就是 Actors。这些 Actors 会自动化与数据收集、处理及自动化相关的任务。

在 Apify 上,你可以通过将脚本公开给其他用户来为你的抓取脚本变现。无论你是私下使用还是对外公开,Bright Data 的抓取解决方案都能帮助让你的抓取器更加可靠且更有效。

为什么要在 Apify 上使用 Bright Data 的 Scraping Browser

要理解 Bright Data 的 Scraping Browser 的价值,首先必须了解这款工具是什么以及它所提供的功能。

传统浏览器自动化工具最大的限制往往不是它们的 API,而是它们所控制的浏览器本身。而 Scraping Browser 是专为网页抓取设计的下一代浏览器。它特别具备以下关键功能:

  • 可靠的 TLS 指纹来避免被检测
  • 可以无限扩展的大规模数据提取能力
  • 由 7200 万 IP 代理网络支持的内置 IP 轮换
  • 自动重试以处理失败的请求
  • 能够处理 CAPTCHA

Scraping Browser 兼容所有主流的浏览器自动化框架——包括 Playwright、Puppeteer 和 Selenium。因此,你无需学习新的 API 或安装第三方依赖,只需直接把它集成到现有的浏览器自动化抓取脚本中即可。

现在,在 Apify 上使用 Scraping Browser 可以带来更多好处,包括:

  • 降低云端成本:浏览器会消耗大量资源,导致更高的 CPU 和内存使用率。Scraping Browser 在云端托管并保证无限扩展,可在 Apify 的 Actor 运行时降低云端成本。由于Apify 按服务器使用情况收费,即使将 Scraping Browser 的费用考虑在内,这种设置也可能节省整体成本。
  • 一体化的反爬工具:Scraping Browser 能应对 IP 封锁、CAPTCHA 验证、浏览器指纹问题以及其他反抓取障碍。这让你的抓取流程更加高效,并且不易中断。
  • 内置代理集成:Scraping Browser 内置了代理管理,因此你无需再关心维护和手动轮换代理
  • Apify 的优势:在 Apify 的云端 Actor(而非通用脚本)上使用 Scraping Browser,还能提供额外好处,比如:
    • 轻松部署
    • 通过 API 以编程方式访问数据
    • 简单的数据导出
    • 轻松配置输入参数
    • 可为大型项目扩展

Bright Data + Apify 的集成不仅能简化你的抓取流程,还能提升可靠性,同时降低让抓取机器人上线所需的时间和精力。

如何在 Apify 上集成 Bright Data 的 Scraping Browser:分步指南

在本节中,我们将以 Amazon 为目标站点。该平台信息丰富,但对机器人有严格的限制措施。如果没有合适的工具,你极有可能遇到Amazon CAPTCHA,从而阻止你的抓取:

阻挡脚本的 Amazon CAPTCHA

这里,我们将构建一个利用 Bright Data 的 Scraping Browser 来抓取 Amazon 通用产品搜索结果页面的 Actor:

目标亚马逊产品搜索页面

注意:本文示例使用 Python 编写 Actor,但Apify 也支持 JavaScript

按照以下步骤,学习如何在 Apify 中集成 Bright Data 的抓取工具!

准备工作

要完成本教程,你需要满足以下前提:

步骤 #1:项目设置

使用 Apify CLI 是最简单的新建 Apify Actor 项目方式。首先,在 Node.js 环境下全局安装它:

npm install -g apify-cli

然后,执行以下命令创建一个新的 Apify 项目:

npx apify-cli create

接下来,你会被要求回答几个问题。请按如下方式回答:

✔ Name of your new Actor: amazon-scraper
✔ Choose the programming language of your new Actor: Python
✔ Choose a template for your new Actor. Detailed information about the template will be shown in the next step.
Playwright + Chrome

这样,Apify CLI 就会在名为 amazon-scraper 的文件夹里创建一个 Python Actor,并使用 “Playwright + Chrome” 模板。如果你不熟悉这些工具,可以阅读我们关于 Playwright 网页抓取的指南。

注意:Selenium 或 Puppeteer 模板也同样支持,因为 Bright Data 的 Scraping Browser 能与任何浏览器自动化工具集成。

你的 Apify Actor 项目结构大致如下:

amazon-scraper
│── .dockerignore
│── .gitignore
│── README.md
│── requirements.txt
│
├── .venv/
│   └── ...
│
├── .actor/
│   │── actor.json
│   │── Dockerfile
│   └── input_schema.json
│
├── src/
│   │── main.py
│   │── __init__.py
│   │── __main__.py
│   └── py.typed
│
└── storage/
    └── ...

amazon-scraper 文件夹用你喜欢的 Python IDE 打开,比如 Visual Studio Code(安装 Python 插件)PyCharm Community Edition

现在,记得要在本地运行 Actor,需要安装 Playwright 的浏览器。首先在项目文件夹中激活虚拟环境(.venv)。如果你使用的是 Windows,执行:

.venv/Scripts/activate

若使用 Linux/macOS,执行:

source .venv/bin/activate

然后,安装 Playwright 所需的依赖:

playwright install --with-deps

太好了!现在你可以通过以下命令本地运行你的 Actor:

apify run

至此,你的 Apify 项目已完成初步设置,可以开始集成 Bright Data 的 Scraping Browser!

步骤 #2:连接到目标页面

如果你查看亚马逊搜索结果页面的 URL,会发现如下格式:

https://www.amazon.com/search/s?k=<keyword>

例如:

注意目标页面的 URL

你的脚本目标 URL 需要使用这个格式,其中 <keyword> 可以用 Apify 的输入参数 动态替换。定义 Actor 能接收的输入参数需要在 .actor 目录下的 input_schema.json 文件中进行。

定义 keyword 参数使脚本更具灵活性,便于用户自定义搜索词。要定义这个参数,可以将 input_schema.json 的内容替换为:

{
    "title": "Amazon Scraper",
    "type": "object",
    "schemaVersion": 1,
    "properties": {
        "keyword": {
            "title": "Search keyword",
            "type": "string",
            "description": "The keyword used to search products on Amazon",
            "editor": "textfield"
        }
    },
    "required": ["keyword"]
}

这个配置定义了一个必需的字符串类型 keyword 参数。

为了在本地运行 Actor 时设置搜索词,修改 storage/key_value_stores/default 目录下的 INPUT.json 文件:

{
    "keyword": "laptop"
}

这样,Actor 会读取 keyword 作为输入参数,并将其赋值为 "laptop"

当你将 Actor 部署到 Apify 平台后,会在启动时看到一个可供自定义此参数的输入字段:

在 Apify 控制台中配置的搜索词文本框

请注意,Actor 的入口文件为 src 文件夹下的 main.py。打开该文件并进行以下修改:

  1. 从输入参数中读取 keyword
  2. 构建目标的亚马逊搜索页面 URL
  3. 使用 Playwright 访问该页面

完成这个步骤后,你的 main.py 可能如下所示:

from apify import Actor
from playwright.async_api import async_playwright


async def main() -> None:
    # Enter the context of the Actor
    async with Actor:
        # Retrieve the Actor input, and use default values if not provided
        actor_input = await Actor.get_input() or {}
        # Reading the "keyword" argument from the input data, assigning it the
        # value "laptop" as a default value
        keyword = actor_input.get("keyword")

        # Building the target url
        target_url = f"https://www.amazon.com/search/s?k={keyword}"

        # Launch Playwright and open a new browser context
        async with async_playwright() as playwright:
            # Configure the browser to launch in headless mode as per Actor configuration
            browser = await playwright.chromium.launch(
                headless=Actor.config.headless,
                args=["--disable-gpu"],
            )
            context = await browser.new_context()

            try:
                # Open a new page in the browser context and navigate to the URL
                page = await context.new_page()
                await page.goto(target_url)

                # Scraping logic...

            except Exception:
                Actor.log.exception(f"Cannot extract data from {target_url}")

            finally:
                await page.close()

上面的代码:

  1. 初始化一个 Apify Actor 来管理脚本生命周期
  2. 通过 Actor.get_input() 获取输入参数
  3. 从输入数据中提取 keyword 参数
  4. 使用 Python 的 f-string 构建目标 URL
  5. 启动 Playwright,开启无头模式的 Chromium,并禁用 GPU
  6. 创建一个新的浏览器上下文,打开页面,并通过 page.goto() 跳转到目标 URL
  7. Actor.log.exception() 记录任何错误
  8. 确保执行结束后关闭 Playwright 页面

很好!你的 Apify Actor 已经准备好利用 Bright Data 的 Scraping Browser 进行高效的网页抓取。

步骤 #3:集成 Bright Data 的 Scraping Browser

现在,你可以先用 Playwright 的 API 在连接到目标页面后截图:

await page.screenshot(path="screenshot.png")

运行本地 Actor 后,会在项目文件夹中生成名为 screenshot.png 的文件。打开后,你可能会看到:

Amazon CAPTCHA 阻拦了你的脚本

或者出现下面的亚马逊错误页面:

亚马逊错误页面

如你所见,你的脚本已被亚马逊的反爬机制所阻拦。这只是对抗流行网站时可能遇到的诸多挑战中的一个。

使用 Bright Data 的 Scraping Browser,就无需再担心这些挑战——它是一个云端抓取解决方案,提供无限扩展、自动 IP 轮换、CAPTCHA 解决和反爬绕过等功能。

首先,如果你还没有账户,请注册 Bright Data。然后登录平台。在 “User Dashboard” 部分,点击 “Get proxy products” 按钮:

点击 “Get proxy products” 按钮

在 “Proxies & Scraping Infrastructure” 页面下的 “My Zones” 表格中,选择 “scraping_browser” 这一行:

选择 Scraping Browser 产品

通过切换开关启用该产品:

启用 Scraping Browser

然后,在 “Configuration” 选项卡下,确保 “Premium domains” 和 “CAPTCHA Solver” 功能被启用,以便达到最佳效果:

启用 “Premium domains” 和 “CAPTCHA Solver” 选项

接着,在 “Overview” 选项卡下,复制 Playwright Scraping Browser 的连接字符串:

将 Puppeteer / Playwright Scraping Browser 连接字符串复制到剪贴板

将该连接字符串添加到 main.py 文件里并作为常量使用:

SBR_WS_CDP = "<YOUR_PLAYWRIGHT_SCRAPING_BROWSER_CONNECTION_STRING>"

请用你刚才复制的实际连接字符串来替换 <YOUR_PLAYWRIGHT_SCRAPING_BROWSER_CONNECTION_STRING>

注意:如果你计划将 Actor 公之于众,你应该将 SBR_WS_CDP 定义为一个 Apify Actor 输入参数,以便使用你 Actor 的其他人也能集成他们自己的 Scraping Browser 连接字符串。

最后,修改 main.pybrowser 的定义,让它在 Playwright 中使用 Scraping Browser:

browser = await playwright.chromium.connect_over_cdp(SBR_WS_CDP, timeout=120000)

请注意,这里连接的超时时间应设得比平时更高,因为代理 IP 轮换和 CAPTCHA 解决都需要一定时间。

完成!你已经在 Apify 的 Playwright 环境里成功集成了 Scraping Browser。

步骤 #4:准备抓取所有商品列表

要抓取亚马逊的商品列表,需要先查看页面的 HTML 结构。右击其中一个商品元素,选择 “检查 (Inspect)”。下面是开发者工具的示例:

检查商品列表 HTML 元素

你会看到,每个商品元素都可以用这个 CSS 选择器进行选择:

[data-component-type=\"s-search-result\"]

利用自定义的 data-* 属性来定位元素通常很好,因为这些属性往往用于测试或监控,通常比较稳定。

在 Playwright 中,你可以通过定位器 (locator)找到所有商品元素:

product_elements = page.locator("[data-component-type=\"s-search-result\"]")

然后遍历这些就绪的商品元素,为后续数据提取做准备:

for product_element in await product_elements.all():
    # Data extraction logic...

很好!接下来我们来实现从 Amazon 抓取数据的逻辑。

步骤 #5:实现抓取逻辑

先来检查单个商品元素:

商品图片元素

从这一部分,你可以从 .s-image 元素的 src 属性获取商品图片:

image_element = product_element.locator(".s-image").nth(0)
image = await image_element.get_attribute("src")

注意,这里用了 nth(0) 来获取第一个匹配该定位器的元素。

接下来查看商品标题:

商品标题元素

你可以在 [data-cy="title-recipe"] 元素的 <a><h2> 标签内,分别获取商品 URL 和标题:

title_header_element = product_element.locator("[data-cy=\"title-recipe\"]").nth(0)

link_element = title_header_element.locator("a").nth(0)
url = None if url_text == "javascript:void(0)" else "https://amazon.com" + url_text

title_element = title_header_element.locator("h2").nth(0)
title = await title_element.get_attribute("aria-label")

这里的逻辑用于忽略一些 “javascript:void(0)”(例如广告位)链接,并把相对链接转为绝对地址。

然后查看评论部分:

商品评论元素

[data-cy="reviews-block"] 找到的 <a> 元素里,你可以从 aria-label 获取评论星级:

rating_element =  product_element.locator("[data-cy=\"reviews-block\"] a").nth(0)
rating_text = await rating_element.get_attribute("aria-label")
rating_match = re.search(r"(\d+(\.\d+)?) out of 5 stars", rating_text)
if rating_match:
    rating = rating_match.group(1)
else:
    rating = None

因为 aria-label 中的文本是类似 “X out of 5 stars” 的格式,所以可以用正则直接提取评分值 X。查看 如何使用正则进行网页抓取

别忘了先导入 re 标准库:

import re

然后查看评论数:

商品评论数元素

[data-component-type="s-client-side-analytics"]<a> 元素里即可获取评论总数:

review_count_element = product_element.locator("[data-component-type=\"s-client-side-analytics\"] a").nth(0)
review_count_text = await review_count_element.text_content()
review_count = int(review_count_text.replace(",", ""))

注意这里通过替换逗号,把诸如 “2,539” 形式的字符串转换成数字。

最后查看商品价格:

商品价格元素

[data-cy="price-recipe"] 下的 .a-offscreen 元素可以获取到对应数字:

price_element_locator = product_element.locator("[data-cy=\"price-recipe\"] .a-offscreen")
# If the price element is on the product element
if await price_element_locator.count() > 0:
    price = await price_element_locator.nth(0).text_content()
else:
    price = None

不是所有商品都有价格元素,因此要先检查是否存在价格节点再获取。

为了让脚本正常工作,你需要在代码里更新对 Playwright 的导入:

from playwright.async_api import async_playwright, TimeoutError

好了!这样我们就完成了亚马逊产品数据的抓取逻辑。

需要注意的是,本文的重点不在深入探讨如何抓取亚马逊,因此更详细的可参见如何用 Python 抓取亚马逊产品数据的教程。

步骤 #6:收集抓取到的数据

for 循环的末尾,通过一个 product 字典收集已抓取的信息:

product = {
    "image": image,
    "url": url,
    "title": title,
    "rating": rating,
    "review_count": review_count,
    "price": price
}

然后把它推送到 Apify dataset 中:

await Actor.push_data(product)

push_data() 会确保这些抓取到的数据被记录到 Apify 上,让你可以通过 API 访问,或以 CSV、JSON、Excel、JSONL 等多种格式导出。

步骤 #7:汇总所有步骤

最终,在 Apify + Bright Data 环境中,你的 Actor main.py 可能如下所示:

from apify import Actor
from playwright.async_api import async_playwright, TimeoutError
import re

async def main() -> None:
    # Enter the context of the Actor
    async with Actor:
        # Retrieve the Actor input, and use default values if not provided
        actor_input = await Actor.get_input() or {}
        # Reading the "keyword" argument from the input data, assigning it the
        # value "laptop" as a default value
        keyword = actor_input.get("keyword")

        # Building the target url
        target_url = f"https://www.amazon.com/search/s?k={keyword}"

        # Launch Playwright and open a new browser context
        async with async_playwright() as playwright:
            # Your Bright Data Scraping API connection string
            SBR_WS_CDP = "wss://brd-customer-hl_4bcb8ada-zone-scraping_browser:[email protected]:9222"

            # Configure Playwright to connect to Scraping Browser and open a new context
            browser = await playwright.chromium.connect_over_cdp(SBR_WS_CDP, timeout=120000)
            context = await browser.new_context()

            try:
                # Open a new page in the browser context and navigate to the URL
                page = await context.new_page()
                await page.goto(target_url)

                # Use a locator to select all product elements
                product_elements = page.locator("[data-component-type=\"s-search-result\"]")

                # Iterate over all product elements and scrape data from them
                for product_element in await product_elements.all():
                    # Product scraping logic
                    image_element = product_element.locator(".s-image").nth(0)
                    image = await image_element.get_attribute("src")

                    title_header_element = product_element.locator("[data-cy=\"title-recipe\"]").nth(0)

                    link_element = title_header_element.locator("a").nth(0)
                    url_text = await link_element.get_attribute("href")
                    url = None if url_text == "javascript:void(0)" else "https://amazon.com" + url_text

                    title_element = title_header_element.locator("h2").nth(0)
                    title = await title_element.get_attribute("aria-label")

                    rating_element =  product_element.locator("[data-cy=\"reviews-block\"] a").nth(0)
                    rating_text = await rating_element.get_attribute("aria-label")
                    rating_match = re.search(r"(\d+(\.\d+)?) out of 5 stars", rating_text)
                    if rating_match:
                        rating = rating_match.group(1)
                    else:
                        rating = None

                    review_count_element = product_element.locator("[data-component-type=\"s-client-side-analytics\"] a").nth(0)
                    review_count_text = await review_count_element.text_content()
                    review_count = int(review_count_text.replace(",", ""))

                    price_element_locator = product_element.locator("[data-cy=\"price-recipe\"] .a-offscreen")
                    # If the price element is on the product element
                    if await price_element_locator.count() > 0:
                        price = await price_element_locator.nth(0).text_content()
                    else:
                        price = None

                    # Populate a new dictionary with the scraped data
                    product = {
                        "image": image,
                        "url": url,
                        "title": title,
                        "rating": rating,
                        "review_count": review_count,
                        "price": price
                    }
                    # Add it to the Actor dataset
                    await Actor.push_data(product)
            except Exception:
                Actor.log.exception(f"Cannot extract data from {target_url}")

            finally:
                await page.close()

可以看出,在 Apify “Playwright + Chrome” 模板下集成 Bright Data 的 Scraping Browser 只需少量代码改动。

步骤 #8:部署到 Apify 并运行 Actor

如果要将本地 Actor 部署到 Apify,请在项目文件夹下执行:

apify push

如果尚未登录,就会提示你通过 Apify CLI 验证身份。

部署完成后,你会看到类似:

✔ Do you want to open the Actor detail in your browser?

输入 “Y” 或 “yes”,就会在浏览器中打开你在 Apify 控制台的 Actor 详情页面:

你的 Actor 在 Apify 控制台页面

如果你愿意,可以手动登录 Apify 后:

  1. 进入控制台
  2. 打开 “Actor” 页面

点击 “Start Actor” 按钮来启动你的亚马逊抓取 Actor。届时,系统会要求你输入关键词。可以试试 “gaming chair”:

配置关键词输入

然后点击 “Save & Start” 以抓取 “gaming chair” 相关的亚马逊产品列表。

当抓取完成后,你能在 “Output” 栏里看到成功获取的数据:

以表格形式展示的抓取数据

如果想导出数据,请前往 “Storage” 标签页,选择 “CSV” 并点击 “Download” 按钮:

点击 “Download” 按钮

下载后的 CSV 文件会如下所示:

CSV 格式展示的抓取数据

瞧!Bright Data 的 Scraping Browser + Apify 的集成非常顺畅,无需再为 Amazon 或任何其他网站的验证码或封禁而发愁。

[额外补充] 在 Apify 上使用 Bright Data 代理

在 Apify 上直接使用 Scraping Browser 或 Web Unlocker 等抓取产品非常简单。

与此同时,如果你已经有一个在 Apify 上运行的 Actor,而只想为它增强代理功能(例如避免 IP 封禁),也可以直接将 Bright Data 代理集成到 Apify Actor 中。具体可参见 官方文档集成指南

结论

通过本教程,你学习了如何构建一个结合 Scraping Browser 与 Playwright 来抓取亚马逊数据的 Apify Actor。我们从零开始,演示了如何用本地抓取脚本一步步部署到 Apify。

现在,你就能理解在 Apify 云端抓取时,使用 Scraping Browser 这样专业抓取工具的优势。采用类似方法,Apify 同样支持 Bright Data 的其他产品:

  • 代理服务:4 种不同类型的代理网络,包含 7200 万+ 住宅 IP,助你突破地理限制
  • 网络抓取 API:针对 100 多个常见网站提供专门的端点,实现持续获取结构化数据
  • 搜索引擎结果页 API:自动处理各类 SERP 解锁与解析,轻松获取搜索结果页面

立即注册 Bright Data,免费试用这些高度可组合的代理和抓取产品吧!