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

JavaScript 渲染内容爬取实践:Puppeteer 进阶技巧

进一步探讨如何使用 Puppeteer 进行动态网页爬取,特别是如何等待页面元素加载完成、处理无限滚动加载、单页应用的路由变化以及监听接口等常见场景。

一、等待页面元素加载完成

在爬取动态网页时,确保页面元素完全加载是获取完整数据的关键。Puppeteer 提供了多种等待页面元素加载完成的方法。

1.1 使用 waitForSelector 方法

async function waitForElement() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();await page.goto('https://example.com');await page.waitForSelector('selector-of-element'); // 替换为实际的 CSS 选择器console.log('元素加载完成');// 提取元素内容const content = await page.$eval('selector-of-element', el => el.textContent);console.log('元素内容:', content);await browser.close();
}waitForElement();

1.2 使用 waitForXPath 方法

async function waitForXPathElement() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();await page.goto('https://example.com');await page.waitForXPath('//*[@id="element-id"]'); // 替换为实际的 XPath 表达式console.log('元素加载完成');// 提取元素内容const content = await page.evaluate(() => {return document.evaluate('//*[@id="element-id"]', document).iterateNext().textContent;});console.log('元素内容:', content);await browser.close();
}waitForXPathElement();

二、处理无限滚动加载

无限滚动加载是一种常见的动态网页加载方式,通过监听滚动事件动态加载更多内容。可以使用 Puppeteer 模拟滚动操作,获取所有内容。

async function handleInfiniteScroll() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();await page.goto('https://example.com/infinite-scroll-page');// 设置页面高度,模拟无限滚动const prevPageHeight = await page.evaluate('document.body.scrollHeight');let newPageHeight;while (true) {await page.evaluate(() => {window.scrollTo(0, document.body.scrollHeight);});await page.waitForTimeout(2000); // 等待内容加载newPageHeight = await page.evaluate('document.body.scrollHeight');if (newPageHeight === prevPageHeight) {break; // 如果页面高度不再变化,退出循环}prevPageHeight = newPageHeight;}// 提取内容const contentList = await page.evaluate(() => {return Array.from(document.querySelectorAll('selector-of-content-item')).map(item => item.textContent);});console.log('内容列表:', contentList);await browser.close();
}handleInfiniteScroll();

三、单页应用的路由变化处理

单页应用(SPA)通常通过 JavaScript 动态更新页面内容而不重新加载整个页面。可以使用 Puppeteer 监听路由变化,获取不同路由下的内容。

async function handleSpaRouteChanges() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();await page.goto('https://example.com/spa-page');// 监听路由变化page.on('response', async (response) => {if (response.url().includes('api/new-route')) {console.log('路由变化检测到,获取新内容');// 提取新路由下的内容const content = await page.evaluate(() => {return document.querySelector('selector-of-new-route-content').textContent;});console.log('新路由内容:', content);}});// 触发路由变化的操作await page.click('selector-of-route-link');await browser.close();
}handleSpaRouteChanges();

四、监听接口请求

在某些情况下,我们可能需要监听特定的接口请求,获取接口返回的数据。Puppeteer 提供了监听网络请求的功能。

async function listenApiRequest() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();// 监听网络请求page.on('response', async (response) => {const url = response.url();if (url.includes('api/data')) { // 监听特定接口try {const data = await response.json(); // 获取接口返回的 JSON 数据console.log('接口数据:', data);} catch (error) {console.error('解析接口数据出错:', error);}}});await page.goto('https://example.com/page-with-api-calls');// 执行触发接口请求的操作await page.click('selector-of-api-call-trigger');await browser.close();
}listenApiRequest();

五、完整示例:爬取动态电商网站

以下是一个完整的示例,演示如何使用 Puppeteer 爬取一个动态电商网站的商品列表,该网站使用无限滚动加载商品内容。

async function scrapeEcommerceProducts() {const browser = await puppeteer.launch({ headless: true });const page = await browser.newPage();await page.goto('https://example.com/products', { waitUntil: 'networkidle2' });// 设置页面高度,模拟无限滚动const prevPageHeight = await page.evaluate('document.body.scrollHeight');let newPageHeight;const productSet = new Set(); // 使用集合避免重复while (true) {// 提取当前页面的商品内容const products = await page.evaluate(() => {return Array.from(document.querySelectorAll('.product-item')).map(item => {return {title: item.querySelector('.product-title').textContent,price: item.querySelector('.product-price').textContent};});});// 将商品信息添加到集合products.forEach(product => productSet.add(JSON.stringify(product)));// 滚动页面await page.evaluate(() => {window.scrollTo(0, document.body.scrollHeight);});await page.waitForTimeout(2000); // 等待内容加载newPageHeight = await page.evaluate('document.body.scrollHeight');if (newPageHeight === prevPageHeight) {break; // 如果页面高度不再变化,退出循环}prevPageHeight = newPageHeight;}// 将集合转换为数组并输出const productList = Array.from(productSet).map(item => JSON.parse(item));console.log('商品列表:', productList);await browser.close();
}scrapeEcommerceProducts();

六、总结

通过本文的案例和实践,深入探讨了如何使用 Puppeteer 进行动态网页爬取,包括等待页面元素加载、处理无限滚动加载、单页应用的路由变化以及监听接口请求等常见场景

http://www.xdnf.cn/news/86095.html

相关文章:

  • Qt之moveToThread
  • Spark-Streaming简介 核心编程
  • 【MySQL】索引失效场景大全
  • C++:继承
  • window上 elasticsearch v9.0 与 jmeter5.6.3版本 冲突,造成es 启动失败
  • 使用Autocannon.js进行HTTP压测
  • Vue3 + Vite + TS,使用 ExcelJS导出excel文档,生成水印,添加背景水印,dom转图片,插入图片,全部代码
  • 建造者模式详解及其在自动驾驶场景的应用举例(以C++代码实现)
  • 数据库对象与权限管理-Oracle数据字典详解
  • Linux mmp文件映射补充(自用)
  • 【Linux】虚拟内存——页表与分页
  • 性能测试篇——八股笔记
  • 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
  • IOT项目——物联网 GPS
  • 如何在 Ansys Icepak AEDT 中设置多个流程以加快仿真速度?
  • 第十五讲、Isaaclab中在机器人上添加传感器
  • linux基础14--dns和web+dns
  • 【SAP-CO】生产的成本流转和成本分析
  • Gmail收取POP3邮件总是出错:服务器返回错误“Error in RETR command: Received an empty line”的解决方法
  • 基于国产 FPGA+ 龙芯2K1000处理器+翼辉国产操作系统继电保护装置测试装备解决方案
  • 0基础 | 51单片机 | Proteus仿真
  • 使用Nginx搭建Web服务
  • 黑马商城(六)RabbitMQ
  • 使用达梦官方管理工具SQLark快速生成数据库ER图并导出
  • ProxySQL 在路由层的核心作用
  • 深入理解CSS中的`transform-origin`属性
  • day30 学习笔记
  • 【C到Java的深度跃迁:从指针到对象,从过程到生态】第三模块·面向对象深度进化 —— 第十章 继承:超越C结构体嵌套的维度
  • 实时监测+远程管控:ADW300解锁阳台光伏运维新维度
  • 音视频学习 - MP3格式