使用 Bright Data 的抓取浏览器升级代理爬取

迁移至 Bright Data 抓取浏览器简化高性能数据采集流程,无需手动管理代理。
5 min read
使用 Bright Data 的抓取浏览器升级代理爬取

进行网络爬取时,通常需要应对反爬机制,使用类似 Puppeteer 的浏览器自动化工具加载动态内容,通过代理轮换避免 IP 阻断,并解决 CAPTCHA。即便使用了这些策略,想要稳定扩展会话仍然很有挑战性。

本文将教你如何从传统的基于代理的配置转向使用 Bright Data 抓取浏览器 进行动态爬取,更加简化的方案。我们会比较两种方法,包括配置、性能、可扩展性以及复杂度等方面。

注意:本文中的示例仅用作教育用途。在爬取任何数据之前,请务必先阅读目标网站的服务条款,并遵守相关法律法规。

先决条件

在开始本教程之前,请确保你已经具备如下条件:

  • 已在本地安装 Node.jsnpm
  • 对 JavaScript 和命令行的基本了解
  • 一个文本编辑器或 IDE,如 Visual Studio CodeWebStorm,用于编写代码
  • 一个免费的 Bright Data 账户,以便使用它们的抓取浏览器

首先,新建一个 Node.js 项目文件夹以存放你的代码。

然后,打开你的终端或 shell,使用以下命令创建新目录:

mkdir scraping-tutorial
cd scraping-tutorial

初始化一个新的 Node.js 项目:

npm init -y

-y 参数会自动回答 yes 给所有问题,使用默认设置创建一个 package.json 文件。

基于代理的网络爬取

在典型的基于代理的方案中,你可以使用 Puppeteer 这样的浏览器自动化工具来与目标域进行交互,加载动态内容并提取数据。同时,通过集成代理来避免 IP 封锁并保持匿名。

下面让我们使用 Puppeteer 快速创建一个爬虫脚本,通过代理从电商网站爬取数据。

使用 Puppeteer 创建爬虫脚本

首先安装 Puppeteer:

npm install puppeteer

然后,在 scraping-tutorial 文件夹中创建一个名为 proxy-scraper.js(文件名可自定)的文件,并添加以下代码:

const puppeteer = require("puppeteer");

(async () => {
  // Launch a headless browser
  const browser = await puppeteer.launch({
    headless: true,
  });
  const page = await browser.newPage();

  const baseUrl = "https://books.toscrape.com/catalogue/page-";
  const books = [];

  for (let i = 1; i <= 5; i++) { // Loop through the first 5 pages
    const url = `${baseUrl}${i}.html`;

    console.log(`Navigating to: ${url}`);

    // Navigate to the page
    await page.goto(url, { waitUntil: "networkidle0" });

    // Extract book data from the current page
    const pageBooks = await page.evaluate(() => {
      let books = [];
      document.querySelectorAll(".product_pod").forEach((item) => {
        let title = item.querySelector("h3 a")?.getAttribute("title") || "";
        let price = item.querySelector(".price_color")?.innerText || "";
        books.push({ title, price });
      });
      return books;
    });

    books.push(...pageBooks); // Append books from this page to the main list
  }

  console.log(books); // Print the collected data

  await browser.close();
})();

该脚本使用 Puppeteer 从 Books to Scrape 网站的前五页抓取书名和价格。它会启动一个无头浏览器,打开新页面,并依次访问每个目录页。

脚本在每一页中,使用 page.evaluate() 内部的 DOM 选择器提取书名和价格,并将数据存入数组。当处理完所有页面后,脚本会在控制台中打印数据,并关闭浏览器。这样就能高效地从分页网站中抓取数据。

使用以下命令测试并运行:

node proxy-scraper.js

输出应类似如下:

Navigating to: https://books.toscrape.com/catalogue/page-1.html
Navigating to: https://books.toscrape.com/catalogue/page-2.html
Navigating to: https://books.toscrape.com/catalogue/page-3.html
Navigating to: https://books.toscrape.com/catalogue/page-4.html
Navigating to: https://books.toscrape.com/catalogue/page-5.html
[
  { title: 'A Light in the Attic', price: '£51.77' },
  { title: 'Tipping the Velvet', price: '£53.74' },
  { title: 'Soumission', price: '£50.10' },
  { title: 'Sharp Objects', price: '£47.82' },
  { title: 'Sapiens: A Brief History of Humankind', price: '£54.23' },
  { title: 'The Requiem Red', price: '£22.65' },
…output omitted…
  {
    title: 'In the Country We Love: My Family Divided',
    price: '£22.00'
  }
]

设置代理

在爬虫配置中,代理常被用来分散请求并使请求难以追踪。常见做法是维护一个代理池并动态轮换。

你可以把代理放在一个数组里,或者存放在单独文件中:

const proxies = [
"proxy1.example.com:port",
"proxy2.example.com:port"
// Add more proxies here
];

实现代理轮换逻辑

让我们添加一些逻辑,在每次启动浏览器时,从代理数组中随机选择代理。更新 proxy-scraper.js,内容如下:

const puppeteer = require("puppeteer");

const proxies = [
"proxy1.example.com:port",
"proxy2.example.com:port"
// Add more proxies here
];

(async () => {
  // Choose a random proxy
  const randomProxy =
    proxies[Math.floor(Math.random() * proxies.length)];

  // Launch Puppeteer with proxy
  const browser = await puppeteer.launch({
    headless: true,
    args: [
      `--proxy-server=http=${randomProxy}`,
      "--no-sandbox",
      "--disable-setuid-sandbox",
      "--ignore-certificate-errors",
    ],
  });

  const page = await browser.newPage();

  const baseUrl = "https://books.toscrape.com/catalogue/page-";
  const books = [];

  for (let i = 1; i <= 5; i++) {
    // Loop through the first 5 pages
    const url = `${baseUrl}${i}.html`;

    console.log(`Navigating to: ${url}`);

    // Navigate to the page
    await page.goto(url, { waitUntil: "networkidle0" });

    // Extract book data from the current page
    const pageBooks = await page.evaluate(() => {
      let books = [];
      document.querySelectorAll(".product_pod").forEach((item) => {
        let title = item.querySelector("h3 a")?.getAttribute("title") || "";
        let price = item.querySelector(".price_color")?.innerText || "";
        books.push({ title, price });
      });
      return books;
    });

    books.push(...pageBooks); // Append books from this page to the main list
  }

  console.log(`Using proxy: ${randomProxy}`);
  console.log(books); // Print the collected data

  await browser.close();
})();

注意:除了手动轮换代理,你也可以使用类似 luminati-proxy 这样的库来自动化该过程。

在这段代码中,从 proxies 列表中随机选择一个代理,通过 --proxy-server=${randomProxy} 参数应用到 Puppeteer。为避免被识别,还可以额外设置随机的用户代理字符串。随后重复爬取逻辑,并记录所使用的代理。

再次运行代码时,输出和之前类似,并会打印使用到的代理:

Navigating to: https://books.toscrape.com/catalogue/page-1.html
Navigating to: https://books.toscrape.com/catalogue/page-2.html
Navigating to: https://books.toscrape.com/catalogue/page-3.html
Navigating to: https://books.toscrape.com/catalogue/page-4.html
Navigating to: https://books.toscrape.com/catalogue/page-5.html
Using proxy: 115.147.63.59:8081
…output omitted…

基于代理爬取的挑战

虽然使用基于代理的方法适用于很多场景,但你可能会遇到如下难题:

  • 频繁被封:如果目标网站反爬措施严格,代理可能被封锁。
  • 性能开销:代理轮换和请求重试会减慢数据采集流程。
  • 可扩展性复杂:为了获取最佳性能和可用性,需要管理和轮换规模庞大的代理池,包括负载均衡、避免过度使用、冷却周期,以及实时处理失效。并发请求数增多时,这个问题会愈发明显,还需要持续监控并更换被拉黑或性能不佳的 IP。
  • 浏览器维护:浏览器的维护相当复杂且消耗资源,需要不断更新和管理其指纹(cookies、headers 以及其他识别信息)来模拟真实用户行为,从而规避高级反爬措施。
  • 云端浏览器开销:云端浏览器的资源需求较高,且基础设施管理更复杂,导致运营成本上升。若要保证扩展时性能稳定,则更是复杂。

使用 Bright Data 抓取浏览器进行动态爬取

为了解决上述难题,你可以使用 Bright Data 抓取浏览器这样的“一站式”API 方案。它能简化你的操作,无需手动代理轮换和繁琐的浏览器配置,并在数据获取的成功率上更具优势。

注册并设置 Bright Data 账户

首先,登录你的 Bright Data 账户,进入 Proxies & Scraping,向下滚动至抓取浏览器并点击 Get Started

Bright Data 仪表板

保持默认配置并点击 Add 即可创建一个新的抓取浏览器实例:

添加抓取浏览器

创建完抓取浏览器实例后,记下 Puppeteer 的 URL,后面会用到:

Scraping Browser 的 URL

修改代码以使用 Bright Data 抓取浏览器

现在,让我们调整代码,让它不再使用代理轮换,而是直接连接到 Bright Data 抓取浏览器的端点。

新建一个名为 brightdata-scraper.js 的文件,添加如下代码:

const puppeteer = require("puppeteer");

(async () => {
  // Choose a random proxy
  const SBR_WS_ENDPOINT ="YOUR_BRIGHT_DATA_WS_ENDPOINT"

  // Launch Puppeteer with proxy
  const browser = await puppeteer.connect({
    browserWSEndpoint: SBR_WS_ENDPOINT,
  });

  const page = await browser.newPage();

  const baseUrl = "https://books.toscrape.com/catalogue/page-";
  const books = [];

  for (let i = 1; i <= 5; i++) {
    // Loop through the first 5 pages
    const url = `${baseUrl}${i}.html`;

    console.log(`Navigating to: ${url}`);

    // Navigate to the page
    await page.goto(url, { waitUntil: "networkidle0" });

    // Extract book data from the current page
    const pageBooks = await page.evaluate(() => {
      let books = [];
      document.querySelectorAll(".product_pod").forEach((item) => {
        let title = item.querySelector("h3 a")?.getAttribute("title") || "";
        let price = item.querySelector(".price_color")?.innerText || "";
        books.push({ title, price });
      });
      return books;
    });

    books.push(...pageBooks); // Append books from this page to the main list
  }

  console.log(books); // Print the collected data

  await browser.close();
})();

请将 YOUR_BRIGHT_DATA_WS_ENDPOINT 替换为你在上一步中拿到的 URL。

该脚本与前文几乎相同,区别就在于不需要依赖多个代理,而是直接连接到 Bright Data 提供的端点。

运行以下命令:

node brightdata-scraper.js

输出结果和之前相同,但现在你不再需要手动代理轮换或配置用户代理。Bright Data 抓取浏览器会自动处理代理轮换、绕过 CAPTCHA 等工作,确保数据采集不中断。

将代码暴露为 Express 接口

如果想把 Bright Data 抓取浏览器融入更大型的应用中,你可以考虑将其封装成 Express 接口。

先安装 Express:

npm install express

创建一个名为 server.js 的文件,并添加如下代码:

const express = require("express");
const puppeteer = require("puppeteer");

const app = express();
const PORT = 3000;

// Needed to parse JSON bodies:
app.use(express.json());

// Your Bright Data Scraping Browser WebSocket endpoint
const SBR_WS_ENDPOINT =
 "wss://brd-customer-hl_264b448a-zone-scraping_browser2:[email protected]:9222";

/**
 * POST /scrape
 * Body example:
 * {
 *   "baseUrl": "https://books.toscrape.com/catalogue/page-"
 * }
 */
app.post("/scrape", async (req, res) => {
  const { baseUrl } = req.body;

  if (!baseUrl) {
    return res.status(400).json({
      success: false,
      error: 'Missing "baseUrl" in request body.',
    });
  }

  try {
    // Connect to the existing Bright Data (Luminati) Scraping Browser
    const browser = await puppeteer.connect({
      browserWSEndpoint: SBR_WS_ENDPOINT,
    });

    const page = await browser.newPage();
    const books = [];

    // Example scraping 5 pages of the base URL
    for (let i = 1; i <= 5; i++) {
      const url = `${baseUrl}${i}.html`;
      console.log(`Navigating to: ${url}`);

      await page.goto(url, { waitUntil: "networkidle0" });

      const pageBooks = await page.evaluate(() => {
        const data = [];
        document.querySelectorAll(".product_pod").forEach((item) => {
          const title = item.querySelector("h3 a")?.getAttribute("title") || "";
          const price = item.querySelector(".price_color")?.innerText || "";
          data.push({ title, price });
        });
        return data;
      });

      books.push(...pageBooks);
    }

    // Close the browser connection
    await browser.close();

    // Return JSON with the scraped data
    return res.json({
      success: true,
      books,
    });
  } catch (error) {
    console.error("Scraping error:", error);
    return res.status(500).json({
      success: false,
      error: error.message,
    });
  }
});

// Start the Express server
app.listen(PORT, () => {
  console.log(`Server is listening on http://localhost:${PORT}`);
});

上述代码中,你初始化了一个 Express 应用,接受 JSON 负载,并定义了一个 POST /scrape 路由。客户端需要在请求体中传入 baseUrl,随后代码将目标 URL 转发给 Bright Data 抓取浏览器端点。

运行你的 Express 服务:

node server.js

测试该接口,可以使用 Postman 这样的工具(或任意其他 REST 客户端),也可以使用 curl 在终端或 shell 中请求:

curl -X POST http://localhost/scrape \
-H 'Content-Type: application/json' \
-d '{"baseUrl": "https://books.toscrape.com/catalogue/page-"}'

输出应类似如下:

{"success":true,"books":[{"title":"A Light in the Attic","price":"£51.77"},{"title":"Tipping the Velvet","price":"£53.74"},{"title":"Soumission","price":"£50.10"},{"title":"Sharp Objects","price":"£47.82"},{"title":"Sapiens: A Brief History of Humankind","price":"£54.23"},{"title":"The Requiem Red","price":"£22.65"},{"title":"The Dirty Little Secrets of Getting Your Dream Job","price":"£33.34"},{"title":"The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull","price":"£17.93"},
… output omitted…
{"title":"Judo: Seven Steps to Black Belt (an Introductory Guide for Beginners)","price":"£53.90"},{"title":"Join","price":"£35.67"},{"title":"In the Country We Love: My Family Divided","price":"£22.00"}]}

下图对比了手动(代理轮换)和 Bright Data 抓取浏览器这两种方式:

手动配置与 Bright Data 抓取浏览器的对比

使用手动轮换代理的方案,需要持续投入精力进行监控和调整,容易出现频繁封禁且难以扩展。

使用 Bright Data 抓取浏览器则可以免去管理代理和请求头的麻烦,通过优化后的基础设施获得更快的响应时间。它自带反爬策略,可提升成功率,减少被封或被标记的风险。

本文所有示例代码都可在此 GitHub 仓库中获取。

计算 ROI

从手动代理的爬取模式转向 Bright Data 抓取浏览器,能显著降低开发时间与成本。

传统方案

假设每天需要爬取新闻网站:

  • 初期开发:约 50 小时(时薪 100 美元,总计 5,000 美元)
  • 持续维护:每月约 10 小时(1,000 美元),用于更新代码、管理基础设施、扩展和代理维护
  • 代理/IP 成本:每月约 250 美元(根据实际需求浮动)

月度成本总计:约 1,250 美元

Bright Data 抓取浏览器方案

  • 开发时间:5–10 小时(约 1,000 美元)
  • 维护:每月约 2–4 小时(200 美元)
  • 无需额外的代理或基础设施管理
  • Bright Data 服务费用:
    • 流量使用:8.40 美元/GB(例如每月 30GB = 252 美元)

月度成本总计:约 450 美元

通过自动化代理管理并借助 Bright Data 抓取浏览器的可扩展能力,不仅能降低初期开发成本,也能减少后续维护支出,让大规模数据采集更加高效经济。

总结

从传统的代理轮换配置迁移到 Bright Data 抓取浏览器,可以免去繁琐的代理轮换和手动防爬配置。

除了获取 HTML 之外,Bright Data 还提供更多工具来简化数据采集流程:

这些方案可让你的爬虫流程更简单、工作量更低,并提升可扩展性。