为何Puppeteer异步函数scrapeEbay内的数组无法在外部访问?
问题原因与解决方案
核心原因
没错,问题完全是异步函数的执行顺序导致的。scrapeEbay是异步函数,调用它时如果不添加await,JavaScript会直接继续执行后续代码,不会等待这个函数里的所有异步操作(比如页面跳转、数据抓取、数组填充)完成。
你的main函数里,for循环只是触发了两次scrapeEbay调用,但没有等待它们执行完毕,所以循环结束后立刻执行的console.log会在scrapeEbay完成数据填充前运行,这时候outputItems和outputPrices自然是空数组。
修复方案
1. 等待异步任务完成(快速修复)
在main的循环里给scrapeEbay调用加上await,确保每个抓取任务完成后再继续下一个:
async function main(inputArray) { for (let i = 0; i < inputArray.length; i++) { await scrapeEbay(inputArray[i]); // 加上await等待任务完成 } console.log(outputItems, outputPrices); // 此时数组已被填充 }
2. 更优方案:避免全局变量(推荐)
全局变量容易引发状态混乱,建议让scrapeEbay直接返回抓取结果,在main里统一收集:
// 修改scrapeEbay,返回单条抓取结果 async function scrapeEbay(inputString) { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); // 原有页面操作逻辑不变 await page.goto("https://ebay.com/sch/ebayadvsearch"); await page.type("#_nkw", inputString); await page.click("#LH_Sold"); await page.click("#LH_ItemConditionUsed"); await page.click("#LH_LocatedInRadio"); await page.click("#searchBTNLowerLnk"); await page.waitForSelector("span.s-item__price"); const scrapedPrices = await page.$$eval("span.s-item__price", (spans) => { return [...spans].slice(1).map((span) => { var slicedNumber = span.innerHTML.slice(24, -7); var splitNumber = slicedNumber.replace(",", ""); return parseFloat(splitNumber); }); }); const medianPrice = median(scrapedPrices); await browser.close(); return { item: inputString, price: medianPrice }; // 返回当前项的结果 } // 修改main,统一收集所有结果 async function main(inputArray) { // 并行执行所有抓取任务(效率更高) const results = await Promise.all(inputArray.map(item => scrapeEbay(item))); // 提取所需的数组 const outputItems = results.map(r => r.item); const outputPrices = results.map(r => r.price); console.log(outputItems, outputPrices); return { outputItems, outputPrices }; // 可将结果返回给调用者 } main(["i7 6700k", "gtx 970"]);
补充说明
- 如果不需要严格按顺序抓取,
Promise.all并行执行多个任务的效率远高于顺序await。 - 去掉全局变量后,代码的可维护性和可读性会大幅提升,也避免了多个异步任务同时修改全局数组可能引发的冲突。
内容的提问来源于stack exchange,提问作者Skilled Rook
相关产品推荐
相关产品推荐

