如何用React Crawling爬取JS生成的网页
在动态网站接管网络之前,抓取相对简单。几乎所有网站都依赖于客户端脚本,蜘蛛可以轻松地从静态 HTML 代码中提取数据。然而,今天却是另一番景象。大多数页面使用 React、Vue 或 Angular 来动态加载内容。
React 是一个流行的 JavaScript 库,用于构建交互式 UI 和单页应用程序 (SPA)。使用它的网站通常依赖 JS 来提供部分或全部内容。您已经知道常规库不足以抓取 JavaScript 生成的网页。那么,让我们看看这是为什么以及如何克服它。
您关于如何爬取 React 网站的教育现在开始!
为什么常规库不足以进行 React 爬取?
当机器人访问页面时,它会查看 HTML 文件并解析其内容以提取目标数据。由于React 网站使用服务器端代码来呈现最终的 HTML,因此获取的源代码不包含整个页面数据。
浏览器最初呈现静态内容,同时下载并运行用必要信息填充 HTML 所需的 JavaScript。
后台 JS 执行经常更改或添加元素到整个内容。但是,标准库会在 JavaScript 触发之前获取 HTML。因此,您在爬虫中收到的数据不完整。
什么是替代方案?
在 React 爬取中,你需要运行页面上的所有代码并渲染内容。如果网站使用 API 向服务器请求数据,您可以通过在爬虫中模拟 API 调用来查看此数据。但是常规库无法呈现 JavaScript。因此,这里的替代方案是使用无头浏览器或 DOM 引擎使用 React 构建网络爬虫。
您可以使用的无头浏览器包括:
- Selenium:一种开源浏览器自动化框架,支持 Node.js、Python、Java 和许多其他语言。
- Puppeteer:一个基于 Chromium 的 Node 库,它使用高级 API 来自动执行浏览器操作。
什么是 React 爬虫?
React 爬虫是一种从 React 网站中提取完整 HTML 数据的工具。这样的解决方案可以在获取 HTML 内容和检索目标信息之前渲染 React 组件。
通常,常规爬虫会检查 URL 列表(也称为种子列表)并发现它需要的那些。但是,它在遇到 React 网站时不会返回准确的数据,因为它无法访问其最终 HTML。
Web 抓取工具使用这种爬行结构来查找要从中提取数据的 URL。让我们用一个例子来澄清它:
假设您有目标 React 网站,但没有要抓取的页面的 URL。在这种情况下,React 爬虫将获取您网站的 URL 并有选择地输出您需要的 URL。
请记住,某些网页及其 URL 比其他网页更有价值。因此,爬虫将根据它们的优先级来组织它们。这称为索引。虽然有些人可能会互换使用爬网和索引这两个术语,但实际上,它们是两个不同的过程。
爬行和索引之间有什么区别?
抓取是 URL 的发现。另一方面,索引是指收集、分析和组织它们。
React 爬虫的基本注意事项
在我们深入研究 React 爬行的世界之前,让我们看一下要记住的关键事项的概述:
- 使用无头浏览器进行爬行往往很慢且性能密集。因此,您希望尽可能避免使用它。
- 并不是所有的 React 网站都是一样的。例如,一些在插入 React 以显示动态内容之前仅呈现静态 HTML。这意味着您可以通过简单地运行 HTTP 请求来访问您想要的数据,而无需无头。
因此,在您进行任何爬行之前仔细阅读以下清单是必不可少的:
🤔 JavaScript 呈现的数据是否是页面源的一部分,并且可以通过 DOM 中某处的脚本标记访问?如果是,我们可以很容易地从 JS 对象中解析出字符串。下面是一个示例:以房地产网站Redfin为例。如果您检查它的 HTML 元素,您会发现以下带有 JSON 内容的脚本标记。
网站是通过JavaScript API调用渲染数据的吗?如果为真,我们可以通过在 HTTP React 爬虫的帮助下向端点发送直接请求来模拟调用。这通常会返回一个 JSON,我们稍后可以轻松解析它。
但是,如果两者的答案是否定的,那么摆在您面前的唯一可行选择就是使用无头浏览器。
先决条件
以下是学习本教程所需的内容:
对于本 React 爬虫教程,我们将使用 Node.js 和 Puppeteer。因此,您的系统上必须有Node(或nvm,如果您愿意)、npm 和 Puppeteer。请记住,有些预装了前者。
npm install puppeteer
如何使用 Headless Chrome 和 Puppeteer 创建 React Web 爬虫?
首先,初始化你的项目:
mkdir react-crawler cd react-crawler npm init -y
react-crawler.js
这会在您的项目目录中创建一个新文件。在您喜欢的代码编辑器中打开它。然后,将 Puppeteer 库导入到您的脚本中以运行它。
const puppeteer = require('puppeteer');
请记住,Puppeteer 库是基于承诺的:它在幕后对 Chromium 进行异步 API 调用。因此,我们必须在函数内编写代码async()
。
假设您要从React 店面检索数据。
在 Node.js 的帮助下,我们可以执行 React 爬取并使用以下脚本抓取呈现的数据。我们可以看到页面的 HTML 内容明显太大,无法使用console.log()
。因此,我们将把它存储在一个文件中,如下面的代码所示。
请注意,我们必须导入 Node.js fs 模块才能使用该fs.writeFile()
函数。
const puppeteer = require('puppeteer'); const fs = require('fs').promises; (async () => { //initiate the browser const browser = await puppeteer.launch(); //create a new in headless chrome const page = await browser.newPage(); //go to target website await page.goto('https://reactstorefront.vercel.app/default-channel/en-US/', { //wait for content to load waitUntil: 'networkidle0', }); //get full page html const html = await page.content(); //store html content in the reactstorefront file await fs.writeFile('reactstorefront.html', html); //close headless chrome await browser.close(); })();
结果如下所示:
你抓取了你的第一个 React 网站。
使用 Puppeteer 选择节点
大多数数据提取项目并不旨在下载整个站点的内容。因此,我们必须只选择我们的目标元素并访问它们的 URL。
查看Puppeteer Page API,了解可用于访问网页的所有不同方法。或者探索我们关于使用 Puppeteer 进行网络抓取的深入指南,以获取有关如何提取所需数据所需的所有信息。
您可以使用相应的选择器来获取链接并将它们添加到列表中。
让我们学习如何抓取产品的 URL:
以找到产品的父级div
并映射锚标记以获得href
我们想要的属性:
这是您需要执行此操作的代码:
const puppeteer = require('puppeteer'); (async () => { //initiate the browser const browser = await puppeteer.launch(); //create a new in headless chrome const page = await browser.newPage(); //go to target website await page.goto('https://reactstorefront.vercel.app/default-channel/en-US/', { //wait for content to load waitUntil: 'networkidle0', }); //get product urls const imgUrl = await page.evaluate(() => Array.from(document.querySelectorAll('ul.grid > li a')).map(a => a.href) ); console.log(imgUrl); await browser.close(); })();
这就是结果:
[ 'https://reactstorefront.vercel.app/default-channel/en-US/products/abba-abba-1970-1982/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/abba-abba-abba-again/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/abba-abba-abba-mp3/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/abba-abba-aniversario-los-10-anos-de-abba/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/adele-adele-3-25/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/adele-adele-3-30/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/adele-adele-3-chasing-pavements/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/adele-adele-3-easy-on-me/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/acdc-acdc-a-giant-dose-of-rock-and-roll/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/acdc-acdc-detroit-rock-city/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/acdc-acdc-high-voltage-rock-n-roll/', 'https://reactstorefront.vercel.app/default-channel/en-US/products/acdc-acdc-hot-seams-wet-dreams-rock-in-rio-1985/' ]
根据您的项目目标,React 爬取过程可能是无穷无尽的。只需按照上述步骤抓取新链接即可。
现在,您可能想知道:是吗?
是的!React 爬行并没有那么复杂。10行左右的代码,就可以爬取JS生成的页面。幸运的是,这样做时您可能不会遇到任何挑战。也就是说,直到您遇到阻止您访问的 URL。
不幸的是,如今反机器人保护几乎无处不在。网络爬虫的另一个主要挑战。我们不会在这里进一步详细介绍,因为这超出了我们文章的范围,但一如既往,我们已经为您准备好了。
探索我们关于如何绕过 Akamai 等反机器人解决方案的分步教程以了解更多信息。
同时,我们将优化 Puppeteer 的性能。
如何优化 Puppeteer 的性能
您可能想知道为什么使用无头浏览器进行爬网比其他方法慢得多。好吧,React 爬行会显着影响您的基础架构,这已经不是什么秘密了。因此,目标是简化无头浏览器的工作。
他们可能在做一些额外的事情:
- 加载图片
- 触发 XHR 请求
- 应用 CSS 规则
但是,额外的工作仅与您的项目目标相关。例如,如果那是截取几页,您可能不会考虑额外加载图像。
也就是说,有不同的方法来优化 Puppeteer 脚本。
让我们深入研究一下:
以缓存优先策略为例。当我们启动一个新的无头浏览器实例时,Puppeteer 会创建一个临时目录来存储 cookie 和缓存等数据。一旦关闭浏览器,这一切都会消失。
以下代码强制库将这些资产保存在自定义路径中。因此它可以在我们每次启动新实例时重用它们:
const browser = await puppeteer.launch({ userDataDir: './data', });
这会显着提升性能,因为 Chrome 不需要持续下载这些资源。
结论
在本 React 爬取教程中,您了解了为什么我们必须使用无头浏览器来爬取 JavaScript 生成的网站。
以下是这些步骤的快速回顾:
- 安装并运行 Puppeteer。
- 抓取您的目标网址。
- 使用选择器提取链接。
- 抓取新链接。
- 返回步骤#2。