在当今的数字世界中,网页抓取与自动化密不可分。从比价网站到数据驱动型企业,从网络中提取和处理数据的能力已成为许多在线业务不可或缺的一部分。
Playwright应运而生:这款工具能助您轻松实现网页抓取和测试任务的自动化。通过控制无头浏览器,Playwright让您能够以快速高效的方式抓取数据并与网站进行交互。
本文将深入探讨 Playwright 在自动化和爬取方面的功能。我将向您展示如何安装和配置 Playwright,提供常见任务的代码示例,并探讨处理登录表单、截屏等高级技术。

在当今的数字世界中,网页抓取与自动化密不可分。从比价网站到数据驱动型企业,从网络中提取和处理数据的能力已成为许多在线业务不可或缺的一部分。
Playwright应运而生:这款工具能助您轻松实现网页抓取和测试任务的自动化。通过控制无头浏览器,Playwright让您能够以快速高效的方式抓取数据并与网站进行交互。
本文将深入探讨 Playwright 在自动化和爬取方面的功能。我将向您展示如何安装和配置 Playwright,提供常见任务的代码示例,并探讨处理登录表单、截屏等高级技术。
Playwright 是由微软开发的一款开源跨平台 Node.js 库,可用于自动化浏览器中的任务。它设计可靠且易于使用,支持 CSS3 和 JavaScript 等现代 Web 特性。
Playwright 可用于测试 Web 应用程序、自动化 Web 任务以及 Web 爬取等场景。它基于流行的 Web 测试库 Puppeteer 构建,旨在提供更友好且易于维护的 API。
Playwright 支持多种浏览器,包括 Chromium、Firefox 和 WebKit。这意味着您可以利用它在不同浏览器上自动化任务并抓取数据。Playwright 允许您轻松地在浏览器之间切换,并充分利用它们各自的独特功能。
要在本地机器上安装并配置 Playwright,您需要先安装 Node.js 和 npm(Node.js 的包管理器)。如果您尚未安装,可以从官方 Node.js 网站下载并安装。
安装好 Node.js 和 npm 后,请按照以下步骤安装 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 树。
选择正确的选择器本身就是一门艺术。在此情况下,我们要选择的表格元素是上下文。该表格有一个名为“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`。总结一下,以下是我们的选择器列表:
利用之前确定的选择器,让我们从网页中提取数据并将其存储在列表中。$$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 在页面初次加载时显示的数量。若要抓取更多内容,我们需要执行一个模拟人类操作的动作——滚动页面。幸运的是,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 datalet 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);
});一秒的等待将为 UI 留出时间,使其能将从 API 获取的数据填充到表格中:
// Wait for the new elements to loadawait page.waitForTimeout(1000);
最后,让我们从第二页提取数据并记录结果:
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)]
// Display the resultsconsole.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 应用在不同浏览器和分辨率下的显示效果。
这对开发人员特别有用,他们可以利用截图来调试布局问题,并测试其 Web 应用程序在不同平台上的外观。
要截取全页面截图,可以使用 Page 对象的 screenshot 方法。以下是一个示例:
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` 属性截取页面中的特定区域。我们需要定义四个属性:
设置了 `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 账户,请立即注册一个!)。
访问登录页面,并运用我们在“查找数据提取元素”章节中学到的技巧,提取用户名和密码输入框的类名。
选择器应如下所示:
现在让我们在代码中使用这些选择器。创建一个 `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 账号内容大多不适合在工作场所查看。
对于网页抓取而言,XPath 可能是一个更稳定的选择,因为它比类名更不容易发生变化。像 Reddit 和 Google 这样的网站经常更新其类名,这可能会给依赖类名的抓取工具带来问题。
相比之下,XPath 表达式基于 HTML 文档的结构,因此变更的可能性较小。这意味着即使在频繁更新类名的网站上,您也可以通过 XPath 以更可靠、更稳定的方式识别元素。
因此,使用 XPath 有助于构建更健壮且具有更高容错能力的网页抓取工具,即使网站更新,该工具也不易出现故障。
本文仅浅尝辄止地介绍了 XPath 的部分应用。如需更全面的指南,请参阅这篇文章。
让我们回到 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` 命令。这将返回你所选子版块的标题列表。例如,我的列表如下所示:
[
'A Year of Writing about Web Scraping in Review',
'Linkedin Comments Scraper - Script to scrape comments (including name, profile picture, designation, email(if present), and comment) from a LinkedIn post from the URL of the post.',
'Best Proxy Lists?',
'Sorry if this is the wrong sub, but is it possible to extract an email from an old youtube account?',
'Looking for people with datasets for sale!',
"Are reddit's classes names change periodically (those with random letters and numbers and what not)",
'Scrape Videos from multiple websites',
'Anyone want to write me a program that can automate generating a pdf of a magazine subscription?',
'Scraping a Dynamic Webpage with rSelenium',
'Is there a way to convert LinkedIn Sales Navigator URLs to a LinkedIn public URL?',
'Gurufocus / Implemented new cloudflare protection?',
'Scraper to collect APY data from Web3 frontends'
]您可以在 Playwright 官方文档中阅读更多关于 XPath 的内容。文档链接在此。
Playwright、Puppeteer 和 Selenium WebDriver 都是可用于网页抓取和自动化的工具。
由微软开发的 Playwright 是一款较新的工具,旨在成为网页自动化的“完整”解决方案。它支持多种浏览器(Chromium、Firefox 和 WebKit)以及多种编程语言(JavaScript、TypeScript、Python 和 C#)。
Puppeteer 是由 Google 开发的一款基于 Chrome DevTools 协议的网页抓取和自动化工具。它主要配合 JavaScript 使用,并具备截图、生成 PDF 以及与 DOM 交互等众多功能。
Selenium WebDriver 是一款支持多种编程语言和浏览器的网页自动化与测试工具。它侧重于测试功能,因此可能比其他一些工具需要更多的配置。
性能是选择网页抓取工具时的重要考量因素。在本节中,我们将对比 Playwright、Puppeteer 和 Selenium WebDriver 的性能,以确定哪款工具速度最快、效率最高。
在测试过程中,我们发现 Playwright 和 Puppeteer 在执行网页抓取和自动化任务时的响应时间几乎相同。然而,与其他两款工具相比,Selenium WebDriver 的启动速度明显较慢。
网络爬虫技术能够从网站中提取并处理数据,为企业和个人提供了丰富的信息与机遇。网络爬虫的主要优势包括:
Playwright 是一款功能强大的网络爬虫工具,其丰富的特性使其成为众多用户的首选。使用 Playwright 的优势包括:
总体而言,网页抓取的益处与使用 Playwright 的优势,使其成为任何希望从网络中提取和处理数据的人不可或缺的工具。
如果您正在寻找更简便的网页抓取解决方案,不妨考虑使用 WebScrapingAPI。我们的服务让您无需处理配置和维护网页抓取工具的复杂流程,即可从任何网站抓取数据。
使用 WebScrapingAPI,您只需向我们的 API 发送包含目标网站 URL 的 HTTP 请求,我们便会以您偏好的格式(JSON、HTML、PNG)将数据返回给您。
我们的API将为您处理所有繁重的工作,包括绕过验证码、应对IP封禁以及处理动态内容。
既然只需通过几个简单的API请求即可使用WebScrapingAPI获取所需数据,何必浪费时间和资源去构建和维护自己的网页抓取工具呢?快来试用一下,看看我们如何帮助您简化网页抓取流程。

Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。