当前位置: 首页 > news >正文

Browserless 网页抓取:Playwright 中的 NodeJS

什么是 Playwright?

Playwright 是一个用于 Web 测试和自动化的开源框架。基于 Node.js,由 Microsoft 开发,它通过单一 API 支持 Chromium、Firefox 和 WebKit。它可以在 Windows、Linux 和 macOS 上运行,并且兼容 TypeScript、JavaScript、Python、.NET 和 Java。

在 Playwright 中使用 NodeJS 进行 Web 爬取的优势是什么?

Playwright 不仅仅是一个工具,而是一个全面的 Web 爬取解决方案,结合了在 Node.js 中的强大功能、灵活性和效率,以满足现代 Web 自动化最苛刻的要求。

  • 强大的页面交互处理
  • 自动处理动态内容
  • 抗反爬技术的韧性

在 Node.js 中,Playwright 的无头模式是一个巨大的优势。它允许你在没有可视界面的情况下运行浏览器,加快了爬取过程,非常适合大规模任务。

Playwright 能够很好地处理复杂的 Web 交互,例如加载动态内容、管理用户输入和处理异步操作。这使得它非常适合从依赖 JavaScript 的现代网站中爬取数据。网络请求拦截功能是另一个强项。它让你可以控制请求和响应,帮助绕过反爬措施并优化你的爬取过程。

Playwright 的另一个优点是它与 Node.js 项目的集成非常简单。你可以轻松地将其融入现有的工作流程中,并与其他 JavaScript 或 TypeScript 库一起使用。此外,由于它可以跨 Windows、Linux 和 macOS 运行,你可以在不同的平台上部署爬取脚本而无需任何麻烦。

什么是 Browserless?

Browserless 是由 Nstbrowser 设计的基于云的无头浏览器服务,用于执行没有图形界面的 Web 操作和自动化脚本。

Browserless 的一个关键特性是它能够通过集成的 Anti-detect、Web Unblocker 和智能代理系统绕过常见的障碍,如 CAPTCHAs 和 IP 封锁。这些工具确保你的自动化脚本即使在具有严格安全措施的网站上也能顺利运行。

Browserless 支持云容器集群,使你能够轻松扩展操作。无论你是在 Windows、Linux 还是 macOS 上运行,我们的平台都能为你的 Web 自动化需求提供一致的企业级解决方案。它设计得非常适合与你现有的工作流程无缝集成,提供一个强大且可靠的高性能 Web 操作环境。

对 Web 爬取和 Browserless 有任何精彩的想法和疑问吗?
让我们看看其他开发者在 Discord 和 Telegram 上分享了什么!

如何使用 Playwright 和 NodeJS 进行 Web 爬取?

步骤1:安装和设置 PlaywrightPlaywright

首先,请确保你的系统上已安装 Node.js。如果没有,下载并安装 LTS 版本。之后,使用 Node.js 的包管理器 npm 安装 Playwright。

npm init playwright@latest

执行安装命令并进行以下选择以开始:

  • 在 TypeScript 和 JavaScript 之间选择(默认是 TypeScript)。
  • 命名你的测试文件夹(如果已经存在测试文件夹,则默认为 test 或 e2e)。
  • 选择是否添加 GitHub Actions 工作流,以便在 CI 上运行测试。
  • 确定是否立即安装 Playwright 浏览器(默认情况下为真,但你也可以稍后手动使用 npx playwright install 安装)。

步骤2:使用 Playwright 启动浏览器

像普通的浏览器一样,Playwright 也可以渲染 JavaScript、图像等,因此它可以提取动态加载的数据。由于它模拟正常的用户行为,脚本更难以检测和阻止。

import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: false }); // 打开浏览器并创建一个新标签
const page = await browser.newPage();
await page.goto('https://developer.chrome.com/'); // 访问指定的 URL
await page.setViewportSize({ width: 1080, height: 1024 }); // 设置视口大小
console.log('[浏览器已打开!]');

默认情况下,Playwright 在无头模式下运行。在提供的代码中,我们通过设置 headless: false 来禁用无头模式,允许你可视化测试脚本。由于在 package.json 中启用了 "type": "module" 设置,允许 ES 模块执行,因此示例和以下代码不需要异步函数。

步骤3:开始爬取

我们将从一个基本的 Web 爬取任务开始,让你快速熟悉 Playwright。让我们提取 Amazon 上 Apple AirPods Pro 的评论!

有效的 Web 爬取始于 分析页面。你需要确定你想要提取的数据的位置。浏览器的调试控制台对我们非常有帮助:

打开网页后,你需要通过按 Ctrl + Shift + I(Windows/Linux)或 Cmd + Option + I(Mac)打开调试控制台。

  1. 在控制台的左上角选择元素选择器。
  2. 将鼠标悬停在你想要爬取的元素上,控制台中会高亮显示对应的 HTML 代码。

Playwright 支持各种选择元素的方法,但使用简单的 CSS 选择器通常是入门的最简单方式。之前使用的 .devsite-search-field 就是 CSS 选择器的一个例子。

对于复杂的 CSS 结构,调试控制台可以直接复制 CSS 选择器。右键点击你想抓取的元素 HTML,并选择 打开菜单 > 复制 > 复制选择器

现在选择器已经确定,我们可以使用 Playwright 尝试获取我上面选择的用户名。

import { chromium } from 'playwright';  // 你也可以使用 'firefox' 或 'webkit'
// 启动一个新的浏览器实例
const browser = await chromium.launch();
// 创建一个新页面
const page = await browser.newPage();
// 访问指定的 URL
await page.goto('https://www.amazon.com/Apple-Generation-Cancelling-Transparency-Personalized/product-reviews/B0CHWRXH8B/ref=cm_cr_dp_d_show_all_btm?ie=UTF8&reviewerType=all_reviews');
// 等待节点加载,最多等待10秒
await page.waitForSelector('div[data-hook="genome-widget"] .a-profile-name', { timeout: 10000 });
// 提取用户名
const username = await page.$eval('div[data-hook="genome-widget"] .a-profile-name', node => node.textContent);
console.log('[用户名]===>', username);
// 关闭浏览器
await browser.close();

你可以调整代码,不仅提取单个用户名,还可以提取完整的评论列表:

  • 使用 page.waitForSelector 确保评论元素完全加载。
  • 使用 page.$$ 选择所有匹配指定选择器的元素。

然后,循环遍历评论元素列表,并从每个元素中提取必要的信息。提供的代码将捕获标题、评分、用户名、文本内容和头像的 data-src 属性(包括头像的 URL)。

正如所展示的那样,代码:

  • page.goto 用于导航到所需页面。
  • waitForSelector 用于等待目标节点正确显示。
  • page.$eval 用于获取第一个匹配的元素,并通过回调函数提取特定属性。
await page.waitForSelector('div[data-hook="review"]');
const reviewList = await page.$$('div[data-hook="review"]');

接下来,我们需要遍历评论元素的列表并从每个评论元素中获取所需信息。

在以下代码中,我们可以获取内容的标题、评分、用户名和文本内容,以及头像元素节点的 data-src 属性值,这就是头像的 URL 地址。

for (const review of reviewList) {const title = await review.$eval('a[data-hook="review-title"] > span:nth-of-type(2)',node => node.textContent,);const rate = await review.$eval('i[data-hook="review-star-rating"] .a-icon-alt',node => node.textContent,);const username = await review.$eval('div[data-hook="genome-widget"] .a-profile-name',node => node.textContent,);const avatar = await review.$eval('div[data-hook="genome-widget"] .a-profile-avatar img',node => node.getAttribute('data-src'),);const content = await review.$eval('span[data-hook="review-body"] span',node => node.textContent,);console.log('[log]===>', { title, rate, username, avatar, content });
}

第四步:导出数据

运行上述代码后,您应该能够在终端中看到日志信息的输出。

如果您想进一步保存这些数据,可以使用基本的 Node.js 模块 fs 将数据写入 JSON 文件,以便后续数据分析。以下是一个简单的工具函数:

import fs from 'fs';// 保存为 JSON 文件
function saveObjectToJson(obj, filename) {const jsonString = JSON.stringify(obj, null, 2);fs.writeFile(filename, jsonString, 'utf8', (err) => {err ? console.error(err) : console.log(`文件保存成功:${filename}`);});
}

完整代码如下。运行后,您可以在当前脚本执行路径中找到 amazon_reviews_log.json 文件,记录了所有爬取的结果!

import { chromium } from 'playwright';
import fs from 'fs';
// 启动浏览器
const browser = await chromium.launch();
const page = await browser.newPage();
// 访问页面
await page.goto(`https://www.amazon.com/Apple-Generation-Cancelling-Transparency-Personalized/product-reviews/B0CHWRXH8B/ref=cm_cr_dp_d_show_all_btm?ie=UTF8&reviewerType=all_reviews`
);// 等待评论元素加载
await page.waitForSelector('div[data-hook="review"]');
// 获取所有评论元素
const reviewList = await page.$$('div[data-hook="review"]');
const reviewLog = [];// 遍历评论元素列表
for (const review of reviewList) {const title = await review.$eval('a[data-hook="review-title"] > span:nth-of-type(2)',node => node.textContent.trim());const rate = await review.$eval('i[data-hook="review-star-rating"] .a-icon-alt',node => node.textContent.trim());const username = await review.$eval('div[data-hook="genome-widget"] .a-profile-name',node => node.textContent.trim());const avatar = await review.$eval('div[data-hook="genome-widget"] .a-profile-avatar img',node => node.getAttribute('data-src'));const content = await review.$eval('span[data-hook="review-body"] span',node => node.textContent.trim());console.log('[log]===>', { title, rate, username, avatar, content });reviewLog.push({ title, rate, username, avatar, content });
}// 保存为 JSON 文件
function saveObjectToJson(obj, filename) {const jsonString = JSON.stringify(obj, null, 2);fs.writeFile(filename, jsonString, 'utf8', (err) => {err ? console.error(err) : console.log(`文件保存成功:${filename}`);});
}saveObjectToJson(reviewLog, 'amazon_reviews_log.json');
await browser.close();

使用 Playwright 进行其他网页操作

点击按钮

使用 page.click 点击按钮,并设置延迟以使操作更像人类操作。以下是一个简单的示例。

import { chromium } from 'playwright'
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/'); // 访问指定的 URL
await page.waitForTimeout(3000)
await page.click('p > a', { delay: 200 })

滚动页面

在 page.evaluate 上,可以通过调用 Window API 设置滚动条的位置。page.evaluate 是一个非常有用的 API:

import { chromium } from 'playwright'
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://www.nstbrowser.io/'); // 访问指定的 URL// 滚动到页面底部
await page.evaluate(() => {window.scrollTo(0, document.documentElement.scrollHeight);
});

获取元素列表

要获取多个元素,我们可以使用 page.$$eval,它可以获取所有匹配指定选择器的元素,最后在回调函数中遍历这些元素以获取特定属性。

import { chromium } from 'playwright'
// 启动浏览器
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com/');await page.waitForSelector('.titleline > a')
// 获取文章标题
const titles = await page.$$eval('.titleline > a', elements =>elements.map(el => el.innerText)
);
// 输出捕获的标题
console.log('文章标题:');
titles.forEach((title, index) => console.log(`${index + 1}: ${title}`));

拦截 HTTP 请求

page.route 方法用于拦截页面上的请求。'**/*' 是一个通配符,表示匹配所有请求。然后可以在回调函数中处理所有请求。参数 route 用于设置请求是正常执行还是被中断,也可以用于重写响应。

import { chromium } from 'playwright'
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();await page.route('**/*', (route) => {const request = route.request();// 拦截并阻止字体请求if (request.url().includes('https://fonts.gstatic.com/')) {route.fulfill({status: 404,contentType: 'image/x-icon',body: ''});console.log('图标请求已阻止');// 拦截并阻止样式表请求} else if (request.resourceType() === 'stylesheet') {route.abort();console.log('样式表请求已阻止');// 拦截并阻止图像请求} else if (request.resourceType() === 'image') {route.abort();console.log('图像请求已阻止');// 允许其他请求继续} else {route.continue();}
});await page.goto('https://www.youtube.com/');

截图

Playwright 提供了开箱即用的截图 API,这是一个非常实用的功能。您可以通过 quality 控制截图文件的质量,并通过 clip 裁剪图像。如果您对截图比例有要求,可以设置 viewport 来实现。

import { chromium } from 'playwright';const browser = await chromium.launch({ viewport: { width: 1920, height: 1080 } });
const page = await browser.newPage();
await page.goto('https://www.youtube.com/');// 捕获整个页面
await page.screenshot({ path: 'screenshot1.png' });
// 捕获 JPEG 图像,将质量设置为 50
await page.screenshot({ path: 'screenshot2.jpeg', quality: 50 });
// 裁剪图像,指定裁剪区域
await page.screenshot({ path: 'screenshot3.jpeg', clip: { x: 0, y: 0, width: 150, height: 150 } });
console.log('截图已保存');
await browser.close();

Playwright vs. Puppeteer vs. Selenium

标准PlaywrightPuppeteerSelenium
浏览器支持Chromium, Firefox 和 WebKit基于 Chromium 的浏览器Chrome, Firefox, Safari, Internet Explorer 和 Edge
语言支持TypeScript, JavaScript, Python, .NET 和 JavaNode.jsJava, Python, C#, JavaScript, Ruby, Perl, PHP, TypeScript
操作系统Windows, macOS, LinuxWindows 和 OS XWindows, macOS, Linux, Solaris
社区和生态系统不断增长的社区,Microsoft 的强大支持和持续开发由 Google 支持的强大社区,特别适合关注 Chromium 的用户成立时间久,有庞大活跃的社区和广泛的生态系统,包括插件和集成
CI/CD 集成支持
录制和回放支持在 Chrome DevTools 的 Sources 面板中Playwright CodeGenSelenium IDE
网页抓取差异具有现代功能和速度,适合不同浏览器的抓取在 Chrome 中表现优秀,速度快多功能的网页抓取工具,但可能较慢
无头模式支持所有支持的浏览器的无头模式支持无头模式,但仅限于基于 Chromium 的浏览器支持无头模式,但实现因浏览器驱动而异
截图PDF 和图像捕获,尤其是在 Chromium 中PDF 和图像捕获,易于使用不支持内置 PDF 捕获

最终想法

Playwright 非常强大!

它提供了一个强大而多功能的网页抓取工具包。此外,Playwright 的出色文档和不断增长的社区为您的自动化任务带来了极大的便利。

在这篇博客中,您对 Playwright 有了更深入的了解:

  • Playwright 网页抓取的优势。
  • 在 Playwright 中高效使用 NodeJS。
  • 使用 Playwright 完成其他操作。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 第2章 C语言基础知识
  • Windows C++控制台菜单库开发与源码展示
  • Java 调整字符串,验证码生成
  • Qt第十七章 多线程
  • ElfBoard技术贴|如何在ELF 1开发板上搭建流媒体服务器
  • 高性能web服务器nginx
  • Mac文件需要分卷压缩怎么办 Mac上怎么解压分卷压缩的文件
  • 二、什么是Vue中的响应式?Vue的响应式原理
  • Linux驱动学习之点灯(四,linux2.6)
  • 电子电气架构 --- 软件定义汽车需要怎么样的EE架构
  • Excel“取消工作表保护”忘记密码并恢复原始密码
  • 【数学建模】优化模型——两辆平板车装货问题
  • linux应用编程--网络编程(socket编程基础)
  • 企业为什么需要安装加密软件
  • PostgreSQL的postgres主进程
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • Android Volley源码解析
  • Asm.js的简单介绍
  • AWS实战 - 利用IAM对S3做访问控制
  • java概述
  • spring学习第二天
  • Vue官网教程学习过程中值得记录的一些事情
  • 笨办法学C 练习34:动态数组
  • 闭包--闭包作用之保存(一)
  • 编写符合Python风格的对象
  • 从输入URL到页面加载发生了什么
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 构建二叉树进行数值数组的去重及优化
  • 跨域
  • 前端知识点整理(待续)
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 优秀架构师必须掌握的架构思维
  • 带你开发类似Pokemon Go的AR游戏
  • 第二十章:异步和文件I/O.(二十三)
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​14:00面试,14:06就出来了,问的问题有点变态。。。
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​MySQL主从复制一致性检测
  • #HarmonyOS:Web组件的使用
  • #pragam once 和 #ifndef 预编译头
  • #vue3 实现前端下载excel文件模板功能
  • %@ page import=%的用法
  • (23)Linux的软硬连接
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (四)linux文件内容查看
  • (算法)N皇后问题
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (一)VirtualBox安装增强功能
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (译)2019年前端性能优化清单 — 下篇