如何依次发起请求关联ID与图片,爬取认证XHR加载的资源
解决Puppeteer循环请求重复输出相同数据的问题
我有一组ID,需要关联对应图片。平时用Node.js的Axios+Cheerio串行发起请求,遇到需要认证的XHR/Fetch接口时,会用Puppeteer。目标是逐个处理每个ID,每行输出ID及对应的图片信息,期望输出格式如下:
030175020 null 800077125 <img src="//farmacialoreto.it/image/data/quarz-d30-weleda-polvere-20g.jpg" alt="Quarz D30 Weleda 20g"> <span class="sale">-1%</span>
但当前的Puppeteer代码在第一次输出正确结果后,后续循环都会输出相同数据,原代码如下:
const puppeteer = require("puppeteer"); const baseUrl = 'https://farmacialoreto.it/#/dfclassic/query=' const arr = ["030175020","800075590",..."983757737"] async function relateCodeToImg() { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() for (const id of arr) { await page.goto(`${baseUrl}${id}`) if (await page.$eval('span[data-role="total"]', e => e.innerText) === "1") { console.log(id + " " + await page.$eval('figure[class="df-card__image image"]', e => e.innerHTML)) } else { console.log(id + " " + null); } } await browser.close() } relateCodeToImg()
问题原因
核心问题是页面跳转后的等待机制不足,加上单页应用的路由切换可能存在缓存或DOM未完全更新的情况:
page.goto()默认只等待页面基本加载完成,但动态渲染的商品数据可能还没更新- 复用同一个页面时,旧的DOM内容或JS上下文没有被彻底重置,导致后续读取的是之前页面的缓存
解决方案
方案1:完善等待逻辑,确保内容完全更新
修改循环内的代码,增加网络空闲等待和元素加载等待,确保读取的是当前ID对应的最新内容:
const puppeteer = require("puppeteer"); const baseUrl = 'https://farmacialoreto.it/#/dfclassic/query=' const arr = ["030175020","800075590",..."983757737"] async function relateCodeToImg() { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() for (const id of arr) { // 等待网络空闲,确保所有请求完成 await page.goto(`${baseUrl}${id}`, { waitUntil: 'networkidle2' }); // 等待关键元素出现,避免读取不存在的内容 await page.waitForSelector('span[data-role="total"]'); const totalText = await page.$eval('span[data-role="total"]', e => e.innerText); if (totalText === "1") { // 等待图片元素加载完成 await page.waitForSelector('figure[class="df-card__image image"]'); const imgHtml = await page.$eval('figure[class="df-card__image image"]', e => e.innerHTML); console.log(`${id} ${imgHtml}`); } else { console.log(`${id} null`); } } await browser.close() } relateCodeToImg()
方案2:每次循环新建页面,彻底隔离上下文
如果单页应用的缓存问题无法通过等待解决,每次循环新建一个页面,用完即关闭,彻底避免上下文污染:
const puppeteer = require("puppeteer"); const baseUrl = 'https://farmacialoreto.it/#/dfclassic/query=' const arr = ["030175020","800075590",..."983757737"] async function relateCodeToImg() { const browser = await puppeteer.launch({ headless: true }) for (const id of arr) { // 每次循环新建页面 const page = await browser.newPage(); await page.goto(`${baseUrl}${id}`, { waitUntil: 'networkidle2' }); const totalText = await page.$eval('span[data-role="total"]', e => e.innerText); if (totalText === "1") { const imgHtml = await page.$eval('figure[class="df-card__image image"]', e => e.innerHTML); console.log(`${id} ${imgHtml}`); } else { console.log(`${id} null`); } // 关闭当前页面,释放资源 await page.close(); } await browser.close() } relateCodeToImg()
关键优化点
{ waitUntil: 'networkidle2' }:等待网络请求基本结束,确保动态数据加载完毕page.waitForSelector():确保目标元素渲染完成后再读取内容,避免空值或旧数据- 新建页面:彻底隔离每个请求的上下文,解决单页应用的缓存问题
内容的提问来源于stack exchange,提问作者Ax_
相关产品推荐
相关产品推荐

