返回博客
指南
苏丘·丹2023年4月21日阅读时长:17分钟

2023 年终极剧作家网络抓取和自动化指南

2023 年终极剧作家网络抓取和自动化指南

什么是 Playwright?

Playwright 是由微软开发的一款开源跨平台 Node.js 库,可用于在网页浏览器中自动化执行任务。它设计可靠且易于使用,支持 CSS3 和 JavaScript 等现代网页功能。

Playwright 可用于测试 Web 应用程序、自动化 Web 任务以及 Web 爬取等任务。它基于流行的 Web 测试库 Puppeteer 构建,旨在提供更易于使用且便于维护的 API。

Playwright 支持多种浏览器,包括 Chromium、Firefox 和 WebKit。这意味着您可以利用它在不同的浏览器上自动化任务并抓取数据。Playwright 允许您轻松地在浏览器之间切换,并充分利用它们各自的独特功能。

入门指南

要在本地机器上安装并配置 Playwright,您需要先安装 Node.js 和 npm(Node.js 的包管理器)。如果您尚未安装这些软件,可以从 Node.js官方网站下载并安装。

安装好 Node.js 和 npm 后,您可以按照以下步骤安装 Playwright:

  • 打开终端或命令提示符
  • 创建一个名为 `playwright` 的文件夹,用于存放我们的项目代码
  • 转到新创建的文件夹
  • 运行命令 `npm init` 来初始化项目(并创建 package.json 文件)
  • 使用命令 `npm install playwright` 安装所需的依赖项

使用 Playwright 进行基础网页抓取

创建一个名为 `index.js` 的文件,并将以下代码粘贴进去:

const { chromium } = require('playwright');

(async () => {

    // Launch a Chromium browser

    const browser = await chromium.launch();

    // Create a new page in the browser

    const page = await browser.newPage();

    // Navigate to a website

    await page.goto('https://coinmarketcap.com');

    // Get the page content

    const content = await page.content();

    // Display the content

    console.log(content);

    // Close the browser

    await browser.close();

})();

这段代码将启动一个无界面的 Chromium 浏览器,创建一个新页面,导航至CoinMarketCap主页,获取页面内容,记录该内容,然后关闭浏览器。

您可以通过引入相应的模块,并将“chromium”替换为“firefox”或“webkit”,使用类似的代码来启动其他受支持的浏览器(Firefox 和 WebKit)。

查找用于数据提取的元素

既然我们已经为爬虫打好了基础,接下来就从目标网站中提取一些数据吧。在本例中,我们将提取页面上显示的前10种货币的名称和价格。

要查看 CoinMarketCap 主页的 DOM 树,请在浏览器中打开该网站,右键单击某种币种的名称,然后选择“检查”。这将打开开发者工具并显示 DOM 树。

CoinMarketCap 加密货币价格表,其中比特币行被高亮显示,浏览器开发者工具显示了相应的 HTML 代码

选择合适的选择器本身就是一门艺术。在这种情况下,我们要选择的表格元素就是上下文。该表格有一个名为“cmc-table”的类,这对我们来说非常有用。

要查找货币名称的选择器,我们需要沿着 DOM 树向下遍历。我们要找的元素位于 `.cmc-table tbody tr td:nth-child(3) a .name-area p` 元素内部。

一般而言,在选择元素时应尽可能准确,因为这将最大限度地减少需要处理的错误数量。

使用同样的方法,我们可以找到价格的样式选择器,即 `.cmc-table tbody tr td:nth-child(3) a span`。总结一下,以下是我们的样式选择器列表:

  • 名称:`.cmc-table tbody tr td:nth-child(3) a .name-area p`
  • 价格:`.cmc-table tbody tr td:nth-child(4) a span`

数据提取

利用我们之前确定的选择器,让我们从网页中提取数据并将其存储在一个列表中。$$eval 辅助函数会返回一个数组,其中包含符合选择器条件的元素,这些元素是在页面 DOM 的上下文中进行评估的。

我们将 console.log(content); 这行代码替换为以下内容:

// Extract the currencies data

const currencies = await page.$$eval('.cmc-table tbody tr:nth-child(-n+10)', trs => {

    const data = []

    trs.forEach(tr => {

        data.push({

            name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,

            price: tr.querySelector('td:nth-child(4) a span').innerHTML,

        })

    })

    return data

})

// Display the results

console.log(currencies)

该代码使用 $$eval 函数选取 .cmc-table 中的前 10 个 tr 元素。随后,它遍历这些元素,使用我们之前确定的选择器选取表格数据单元格,并提取其文本内容。最终,数据以对象数组的形式返回。

您可以在官方文档中进一步了解 $$eval 函数。

完整的代码如下:

const { chromium } = require('playwright');

(async () => {

    // Launch a Chromium browser

    const browser = await chromium.launch();

    // Create a new page in the browser

    const page = await browser.newPage();

    // Navigate to a website

    await page.goto('https://coinmarketcap.com');

    // Extract the currencies data

    const currencies = await page.$$eval('.cmc-table tbody tr:nth-child(-n+10)', trs => {

   	 const data = []

   	 trs.forEach(tr => {

   		 data.push({

   			 name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,

   			 price: tr.querySelector('td:nth-child(4) a span').innerHTML,

   		 })

   	 })

   	 return data

    })

    // Display the results

    console.log(currencies)

    // Close the browser

    await browser.close();

})();

处理动态内容

我们只抓取了前10种货币,因为CoinMarketCap在页面初次加载时只显示这10种。若要抓取更多,我们需要执行一项模拟人类操作——即滚动页面。幸运的是,Playwright非常适合完成这项任务。

首先,让我们重构之前使用的 $$eval 函数,并实现分页功能。我们将这个新函数命名为 extractData:

const extractData = async (page, currentPage, perPage = 10) => {

}

我们通过分段选择(即从 0 到 10、从 11 到 21、从 22 到 32 等)来扩展 :nth-child 选择器。我们定义初始选择器(前 10 个元素):

let selector = `:nth-child(-n+${currentPage * perPage})`;

最后但同样重要的是,我们添加了对下一页的支持。代码如下:

if(currentPage > 1) {

    selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;

}

最终函数将如下所示:

const extractData = async (page, currentPage, perPage = 10) => {

    let selector = `:nth-child(-n+${currentPage * perPage})`;

    if(currentPage > 1) {

   	 selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;

    }

    return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {

   	 const data = [];

   	 trs.forEach(tr => {

   		 data.push({

   			 name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,

   			 price: tr.querySelector('td:nth-child(4) a span').innerHTML,

   		 });

   	 });

   	 return data;

    });

};

现在是时候回到我们的爬虫代码中,实现滚动功能,并扩展数据提取功能了。我们将在以下这行代码之后进行所有操作:

await page.goto('https://coinmarketcap.com');

我们重新定义了 currencies 变量:

// Extract the currencies data

let currencies = await extractData(page, 1, 10);

利用 evaluate 函数,我们将页面滚动到视口高度的 1.5 倍处。这将触发表格中的后续元素加载:

// Scroll the page to a little more than the viewport height

await page.evaluate(() => {

    window.scrollTo(0, window.innerHeight * 1.5);

});

这一秒的等待将为界面留出一些时间,以便将从 API 获取的数据填充到表格中:

// Wait for the new elements to load

await page.waitForTimeout(1000);

最后,让我们从第二页提取数据并记录结果:

// Extract the next 10 elements

currencies = [...currencies, ...await extractData(page, 2, 10)]

// Display the results

console.log(currencies)

该爬虫程序的完整代码应如下所示:

const { chromium } = require('playwright');

const extractData = async (page, currentPage, perPage = 10) => {

    let selector = `:nth-child(-n+${currentPage * perPage})`;

    if(currentPage > 1) {

   	 selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;

    }

    return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {

   	 const data = [];

   	 trs.forEach(tr => {

   		 data.push({

   			 name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,

   			 price: tr.querySelector('td:nth-child(4) a span').innerHTML,

   		 });

   	 })

   	 return data;

    })

};

(async () => {

    // Launch a Chromium browser

    const browser = await chromium.launch();

    // Create a new page in the browser

    const page = await browser.newPage();

    // Navigate to a website

    await page.goto('https://coinmarketcap.com');

    // Extract the currencies data

    let currencies = await extractData(page, 1, 10)

    // Scroll the page to a little more than the viewport height

    await page.evaluate(() => {

   	 window.scrollTo(0, window.innerHeight * 1.5);

    });

    // Wait for the new elements to load

    await page.waitForTimeout(1000);

    // Extract the next 10 elements

    currencies = [...currencies, ...await extractData(page, 2, 10)];

    // Display the results

    console.log(currencies);

    // Close the browser

    await browser.close();

})();

简而言之,该爬虫程序将打开CoinMarketCap的首页,提取前10种加密货币的数据,然后滚动页面,提取接下来10种加密货币的数据,并显示结果。

你应该会得到类似于以下的结果:

[

  { name: 'Bitcoin', price: '$16,742.58' },

  { name: 'Ethereum', price: '$1,244.45' },

  { name: 'Tether', price: '$0.9997' },

  { name: 'USD Coin', price: '$1.00' },

  { name: 'BNB', price: '$255.78' },

  { name: 'XRP', price: '$0.335' },

  { name: 'Binance USD', price: '$1.00' },

  { name: 'Dogecoin', price: '$0.07066' },

  { name: 'Cardano', price: '$0.2692' },

  { name: 'Polygon', price: '$0.7762' },

  { name: 'Dai', price: '$0.9994' },

  { name: 'Litecoin', price: '$73.80' },

  { name: 'Polkadot', price: '$4.59' },

  { name: 'Solana', price: '$12.95' },

  { name: 'TRON', price: '$0.0505' },

  { name: 'Shiba Inu', price: '$0.000008234' },

  { name: 'Uniswap', price: '$5.29' },

  { name: 'Avalanche', price: '$11.43' },

  { name: 'UNUS SED LEO', price: '$3.47' },

  { name: 'Wrapped Bitcoin', price: '$16,725.03' },

  { name: 'Cosmos', price: '$9.97' }

]

高级技巧

既然我们已经介绍了使用 Playwright 进行网页抓取的基础知识,例如创建抓取器、查找选择器、提取数据以及实现无限滚动,现在是时候深入探讨 Playwright 提供的某些更高级的功能了。

这些方法包括截屏、填写表单、使用 XPath 代替类选择器,以及使用代理服务器绕过 IP 封禁。

截屏

在进行网页抓取时截屏的一大优势在于,它能让你看到网站或网络应用在不同浏览器和分辨率下的显示效果。

这对开发人员尤其有用,他们可以利用截图来排查布局问题,并测试其 Web 应用程序在不同平台上的显示效果。

要截取整页屏幕截图,可以使用 Page 对象的截图方法。以下是一个示例:

const screenshot = await page.screenshot();

这段代码会截取整个页面的屏幕截图,并将其作为 Buffer 返回。`screenshot` 函数支持传入属性。我们可以指定保存屏幕截图的路径,并决定截图应仅包含视口还是整个页面。

这是完整的代码:

const { chromium } = require('playwright');

(async () => {

    const browser = await chromium.launch();

    const page = await browser.newPage();

    await page.goto('https://coinmarketcap.com');

    // Take a full page screenshot and save the file

    await page.screenshot({

   	 path: "screenshot.png",

   	 fullPage: false

    });

    await browser.close();

})();

你可以将这段代码保存到一个名为 `screenshot.js` 的文件中,并通过命令 `node screenshot.js` 运行该代码。执行后,项目文件夹中将生成一个名为 `screenshot.png` 的文件。

我们可以使用 `clip` 属性截取页面中某个区域的屏幕截图。我们需要定义四个属性:

  • x:相对于左上角的水平偏移量
  • y:相对于左上角的垂直偏移量
  • 宽度:区域宽度
  • 高度:区域高度

设置了 clip 属性的截图功能将呈现如下效果:

// Take a screenshot of a part of the page

await page.screenshot({

    path: "screenshot.png",

    fullPage: false,

    clip: {

        x: 50,

        y: 50,

        width: 320,

        height: 160

    }

});

填写表格

使用 Playwright 的一个优势在于,它允许您访问受保护的页面。通过模拟点击按钮、滚动页面和填写表单等人类操作,Playwright 可以绕过登录要求并访问受限内容。

让我们使用 Playwright 访问我们的 Reddit 账户(如果你还没有 Reddit 账户,请立即注册一个!)。

请访问登录页面,并运用我们在“查找用于数据提取的元素”一节中学习到的技巧,提取用户名和密码输入框的类名。

选择器应如下所示:

  • 用户名输入:`#loginUsername`
  • 密码输入:`#loginPassword`
  • 提交按钮:`button[type="submit"]`

让我们在代码中使用这些选择器。创建一个名为 `login.js` 的文件,并将以下代码粘贴进去:

const { chromium } = require('playwright');

(async () => {

    const browser = await chromium.launch();

    const page = await browser.newPage();

    await page.goto('https://reddit.com/login');

    // Fill up the form

    await page.fill('#loginUsername', "YOUR_REDDIT_USERNAME");

    await page.fill('#loginPassword', "YOUR_REDDIT_PASSWORD");

    // Click the submit button

    await page.click('button[type="submit"]');

    // Wait for the new page to load

    await page.waitForNavigation()

    // Take a screenshot of the new page

    await page.screenshot({

   	 path: "reddit.png",

   	 fullPage: false

    });

    // Close the browser

    await browser.close();

})();

要运行代码并截取受保护页面的屏幕截图,只需在终端中输入 node login.js。几秒钟后,项目目录中就会出现一个名为 reddit.png 的屏幕截图。

很遗憾,我无法把生成的截图分享给你们,因为我的Reddit账号里大部分内容都不适合在工作场所查看。

您可以在官方文档中进一步了解有关 Playwright 身份验证的详细信息

使用 XPath 进行数据提取

对于网页抓取而言,XPath 可能是一个更稳妥的选择,因为它比类名更不容易发生变化。像 Reddit 和 Google 这样的网站经常更新其类名,这可能会给依赖这些类名的抓取工具带来问题。

相比之下,XPath 表达式基于 HTML 文档的结构,因此变更的可能性较小。这意味着即使在经常更新类名的网站上,您也可以通过 XPath 以更可靠、更稳定的方式识别元素。

因此,使用 XPath 可以帮助您构建一个更强大、更具弹性的网页抓取工具,使其在网站更新时不易出现故障。

本文仅对 XPath 的应用进行了浅显的介绍。如需更全面的指南,请参阅这篇文章

让我们回到 Reddit,打开一个子版块,然后打开开发者工具。我将使用/r/webscraping子版块,但你可以选择任何你喜欢的子版块。

Reddit r/webscraping 动态,其中一篇帖子的标题被高亮显示,浏览器开发者工具中显示了选中的标题元素

如果你检查 DOM 树,可能会发现元素的类名似乎是随机生成的。使用这些类名会降低爬虫的可靠性,并且需要持续维护。

右键单击 `h3` 标签,选择“复制”,然后选择 `复制 XPath`。结果应如下所示:

//*[@id="t3_104ocrd"]/div[3]/div[2]/div[1]/a/div/h3

在应用这条规则之前,让我们先移除 @id 部分,这样路径就能涵盖所有文章,而不仅仅是第一篇。

是时候动手实践了。创建一个名为 `xpath.js` 的文件,并将以下代码粘贴进去:

const { chromium } = require('playwright');

(async () => {

    const browser = await chromium.launch();

    const page = await browser.newPage();

    await page.goto('https://reddit.com/r/webscraping');

    const titles = await page.$$eval("//*/div[3]/div[2]/div[1]/a/div/h3", element => {

   	 const data = []

   	 element.forEach(el => {

   		 data.push(el.innerHTML)

   	 })

   	 return data

    })

    console.log(titles)

    // Close the browser

    await browser.close();

})();

要运行该代码,请使用 `node xpath.js` 命令。这将返回你所选子版块中的一系列标题。例如,我的列表如下所示:

[

  “回顾一年来的网络爬虫写作”,

  “LinkedIn评论爬虫——用于从LinkedIn帖子的URL中爬取评论(包括姓名、头像、职位、邮箱(如有)和评论内容)的脚本”,

  “最好的代理列表?”,

  '抱歉如果发错了子版块,但能否从旧的 YouTube 账号中提取邮箱?',

  '寻找出售数据集的人!',

  “Reddit 的分类名称会定期变化吗(那些由随机字母和数字组成的)”,

  “从多个网站抓取视频”,

  “有人愿意帮我写个程序,自动生成杂志订阅的 PDF 文件吗?”,

  “使用 rSelenium 抓取动态网页”,

  “有没有办法将 LinkedIn Sales Navigator 的链接转换为 LinkedIn 的公开链接?”,

  “Gurufocus / 是否已实施新的 Cloudflare 防护措施?”,

  “用于从 Web3 前端收集 APY 数据的抓取工具”

]

您可以在 Playwright 的官方文档中了解更多关于 XPath 的信息。文档链接在此

剧作家 vs 其他人

Playwright、Puppeteer 和 Selenium WebDriver 都是可用于网页抓取和自动化的工具。

由微软开发的Playwright是一款较新的工具,旨在成为网络自动化的“完整”解决方案。它支持多种浏览器(Chromium、Firefox 和 WebKit)以及多种编程语言(JavaScript、TypeScript、Python 和 C#)。

Puppeteer 是由 Google 开发的一款基于 Chrome DevTools 协议的网络爬虫和自动化工具。它主要与 JavaScript 配合使用,具备多种功能,包括截屏、生成 PDF 文件以及与 DOM 进行交互。

Selenium WebDriver 是一款用于 Web 自动化和测试的工具,支持多种编程语言和浏览器。它专为测试而设计,因此可能比其他一些工具需要更多的配置。

性能对比

在选择网页抓取工具时,性能是一个重要的考量因素。在本节中,我们将对比 Playwright、Puppeteer 和 Selenium WebDriver 的性能,以确定哪款工具速度最快、效率最高。

Playwright、Puppeteer 和 Selenium 在速度、文档和浏览器支持方面的对比表

在测试过程中,我们发现Playwright和Puppeteer在执行网页抓取和自动化任务时的响应时间几乎完全相同。然而,与其他两款工具相比,Selenium WebDriver的启动速度明显较慢。

结论

网络爬虫技术能够从网站中提取和处理数据,为企业和个人提供了丰富的信息与机遇。网络爬虫技术的主要优势包括:

  • 节省成本:网络爬虫是一种经济高效的数据采集方式,因为它能帮助您避免购买昂贵的数据库或API。
  • 时间效率:与手动收集相比,抓取大量数据的速度要快得多。
  • 最新数据:通过定期抓取和刷新数据源,网络爬虫可帮助您保持数据的最新状态。

Playwright 是一款功能强大的网络爬虫工具,其丰富的功能使其成为众多用户的首选。使用 Playwright 的优势包括:

  • 与 Chromium 的兼容性:Playwright 基于 Chromium 构建,这是一个为 Google Chrome 提供支持的开源浏览器项目,因此您可以使用最新的 Web 平台功能,并能与各类网站广泛兼容。
  • 跨浏览器支持:Playwright 支持在多个浏览器(包括 Chrome、Firefox 和 Safari)上自动化执行任务。
  • 易用性:Playwright 拥有简单直观的 API,让您能够轻松上手网页抓取和自动化任务。
  • 多功能性:Playwright 可用于多种任务,包括网页抓取、测试和自动化。

总体而言,网络爬取带来的好处以及使用 Playwright 的优势,使其成为任何希望从网络中提取和处理数据的人不可或缺的工具。

如果您正在寻找一种更简便的网络爬虫解决方案,不妨考虑使用 WebScrapingAPI。我们的服务让您能够从任何网站抓取数据,而无需处理配置和维护网络爬虫的复杂流程。

使用 WebScrapingAPI,您只需向我们的 API 发送一个包含目标网站 URL 的 HTTP 请求,我们就会将数据以您指定的格式(JSON、HTML、PNG)返回给您。

我们的 API 会为您处理所有繁重的工作,包括绕过验证码、处理 IP 封禁以及处理动态内容。

既然只需通过几个简单的API调用就能使用WebScrapingAPI获取所需数据,何必浪费时间和资源去开发和维护自己的网页抓取工具呢?快来试用一下, 看看我们如何帮助您简化网页抓取流程。

关于作者
Suciu Dan,WebScrapingAPI 联合创始人
Suciu Dan联合创始人

Suciu Dan 是 WebScrapingAPI 的联合创始人,他撰写了关于 Python 网络爬虫、Ruby 网络爬虫以及代理基础设施的实用指南,这些指南专为开发者而设计。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。