gpt4 book ai didi

javascript - puppeteer : Scrape sometimes works, sometimes fails with TypeError

转载 作者:行者123 更新时间:2023-12-03 07:40:43 25 4
gpt4 key购买 nike

作为个人挑战,我正在尝试创建一个工具,该工具将使用Puppeteer抓取网站(本实验所使用的购物平台AliBaba)的搜索结果,并将输出保存到JSON对象中,以便以后用于创建前端的可视化。
我的第一步是访问搜索结果的第一页,并将列表从那里刮到一个数组中:

const puppeteer = require('puppeteer');
const fs = require('fs');

/* First page search URL */
const url = (keyword) => `https://www.alibaba.com/trade/search?fsb=y&IndexArea=product_en&CatId=&SearchText=${keyword}`

/* keyword to search for */
const keyword = `future`;

(async () => {
try {
const browser = await puppeteer.launch({
headless: true
});

const page = await browser.newPage();

await page.goto(url(keyword), {
waitUntil: 'networkidle2'
});

await page.waitForSelector('.m-gallery-product-item-v2');

let urls = await page.evaluate(() => {
let results = [];
let items = document.querySelectorAll('.m-gallery-product-item-v2');

// This console.log never gets printed to either the browser window or the terminal?
console.log(items)

items.forEach( item => {
let CurrentTime = Date.now();
let title = item.querySelector('h4.organic-gallery-title__outter').getAttribute("title");
let link = item.querySelector('.organic-list-offer__img-section').getAttribute("href");
let img = item.querySelector('.seb-img-switcher__imgs').getAttribute("data-image");

results.push({
'scrapeTime': CurrentTime,
'title': title,
'link': `https:${link}`,
'img': `https:${img}`,
})
});
return results;

})
console.log(urls)
browser.close();

} catch (e) {
console.log(e);
browser.close();
}
})();
当我使用Node在终端中运行文件(test-2.js)时,它有时会返回 results数组,但有时会抛出错误。大约一半时间抛出的终端错误是:
Error: Evaluation failed: TypeError: Cannot read property 'getAttribute' of null
at __puppeteer_evaluation_script__:11:82
at NodeList.forEach (<anonymous>)
at __puppeteer_evaluation_script__:8:19
at ExecutionContext._evaluateInternal (/Users/dmnk/scraper/node_modules/puppeteer/lib/ExecutionContext.js:102:19)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async ExecutionContext.evaluate (/Users/dmnk/scraper/node_modules/puppeteer/lib/ExecutionContext.js:33:16)
at async /Users/dmnk/scraper/test-2.js:24:20
-- ASYNC --
at ExecutionContext.<anonymous> (/Users/dmnk/scraper/node_modules/puppeteer/lib/helper.js:94:19)
at DOMWorld.evaluate (/Users/dmnk/scraper/node_modules/puppeteer/lib/DOMWorld.js:89:24)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
-- ASYNC --
at Frame.<anonymous> (/Users/dmnk/scraper/node_modules/puppeteer/lib/helper.js:94:19)
at Page.evaluate (/Users/dmnk/scraper/node_modules/puppeteer/lib/Page.js:612:14)
at Page.<anonymous> (/Users/dmnk/scraper/node_modules/puppeteer/lib/helper.js:95:27)
at /Users/dmnk/scraper/test-2.js:24:31
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:53159) UnhandledPromiseRejectionWarning: ReferenceError: browser is not defined
at /Users/dmnk/scraper/test-2.js:52:9
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:53159) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:53159) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
我对掌握和学习异步JavaScript相对较新。
我已经尝试了几天,以了解为什么会发生此错误,但无济于事。非常感谢您对了解原因/故障排除的任何帮助。

最佳答案

实际上,您确实滥用了异步JavaScript,这会导致脚本失败。对于互联网连接速度稍慢的我来说,Evaluation failed: TypeError: Cannot read property 'getAttribute' of null错误始终存在。通过将 networkidle2 中的domcontentloaded替换为page.goto waitUntil设置,您可以稍微提高稳定性(请确保阅读文档之间有什么区别)。
主要问题是异步事件(与chrome api的通信)未等待。您可以考虑以下几点开始重构脚本:
更有效地选择元素

  • 我建议使用const以避免意外覆盖已选择的元素。
  • 使用page上下文标识元素。 Puppeteer(chrome)还为$$提供了querySelectorAll别名; $:querySelector(docs)
  • 总是await异步事件,一切都被认为是异步的,需要与chrome api进行通信!

  • 之前:
    let items = document.querySelectorAll('.m-gallery-product-item-v2');
    之后:
    const items = await page.$$('.m-gallery-product-item-v2');

    评估DOM内容
    结合使用elementHandles和 page.evaluate来检索内容(在极少数情况下需要 .getAttribute):
    之前:
    let title = item.querySelector('h4.organic-gallery-title__outter').getAttribute("title");

    之后:
    const title = await page.evaluate(el => el.title, (await page.$$('h4.organic-gallery-title__outter'))[i])

    不要使用 forEach遍历异步事件
    幸运的是,您没有在 forEach循环中使用async/await。但是,实际上,缺少异步是如果未及时加载页面导致脚本失败的原因。您确实需要异步,只是不需要在 forEach内(不,也不需要在 Array.map内!)。我宁愿建议 使用 for...of 或常规的for循环,如果您希望使用伪造者 Action 进行可预测的行为。 (在当前示例中,数组索引具有关键部分,因此为了简单起见,我使用了for循环)
    注意:可以使用 forEach,但是您需要使用 Promise.all对其进行包装。

    使用try ... catch捕获较小的代码片段
    例如:在每次迭代的循环内,因此如果只有一个数组元素有问题,您的脚本不会崩溃。如果您运行刮板几个小时,并且快要失效了,那可能会非常令人沮丧。

    重构“URL”功能 page.evaluate部分使代码保持异步,但是您也可以通过使用上面的建议并等待每个步骤来解决此问题。您最终都不会返回 results对象,但是可以在循环的每次迭代中填充它。
    重构的例子
    它不会再失败,而且 console.log(items);也将被记录到控制台。
    const puppeteer = require('puppeteer');

    /* first page search URL */
    const url = keyword => `https://www.alibaba.com/trade/search?fsb=y&IndexArea=product_en&CatId=&SearchText=${keyword}`;

    /* keyword to search for */
    const keyword = 'future';

    const results = [];

    (async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    try {
    await page.goto(url(keyword), { waitUntil: 'domcontentloaded' });
    await page.waitForSelector('.m-gallery-product-item-v2');
    const items = await page.$$('.m-gallery-product-item-v2');

    // this console.log never gets printed to either the browser window or the terminal?
    console.log(items);

    for (let i = 0; i < items.length; i++) {
    try {
    let CurrentTime = Date.now();
    const title = await page.evaluate(el => el.title, (await page.$$('h4.organic-gallery-title__outter'))[i]);
    const link = await page.evaluate(el => el.href, (await page.$$('.organic-list-offer__img-section, .list-no-v2-left__img-container'))[i]);
    const img = await page.evaluate(el => el.getAttribute('data-image'), (await page.$$('.seb-img-switcher__imgs'))[i]);

    results.push({
    scrapeTime: CurrentTime,
    title: title,
    link: `https:${link}`,
    img: `https:${img}`
    });

    } catch (e) {
    console.error(e);
    }
    }

    console.log(results);
    await browser.close();
    } catch (e) {
    console.log(e);
    await browser.close();
    }
    })();
    编辑:该脚本有时会失败,因为在阿里巴巴的网站上 .organic-list-offer__img-section CSS类已更改为 .list-no-v2-left__img-container。他们要么AB用不同的选择器测试两个布局,要么经常更改CSS类。

    编辑2:如果一个元素在每个用户 session 中可以有多个选择器(可能由于产品AB测试),则可以使用两个可能的选择器,并用逗号分隔,例如:
    const link = await page.evaluate(el => el.href, (await page.$$('.organic-list-offer__img-section, .list-no-v2-left__img-container'))[i]);
    这将确保在两种情况下都可以选择该元素, comma的作用类似于 OR运算符。

    关于javascript - puppeteer : Scrape sometimes works, sometimes fails with TypeError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62516119/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com