首页 > 解决方案 > waitForSelector 突然不再在 puppeteer 中工作

问题描述

我有一个可以工作的 puppeteer 脚本,我想将它制作成一个 API,但是我在使用 waitForSelector 时遇到了问题。

背景: 我编写了一个 puppeteer 脚本,它成功搜索并抓取了我在代码中指定的查询的结果,例如let address = xyz;. 现在我想把它变成一个 API,以便用户可以查询一些东西。我设法编写了本地 API(使用 express)所需的所有内容,并且一切正常。我的意思是:我编写了所有服务器端的东西:我可以发出请求,调用刮板功能,puppeteer 启动,执行我的搜索(我需要输入地址,从下拉列表中选择并按 Enter) .

状态: 我的查询结果是 iFrame 中的一个表单(基本上是 3 列和一些行),我想抓取所有行(稍后我将它们修改为特定的 json)。它的工作方式是我在表单的选择器上使用 waitForSelector,然后使用 frame.evaluate。

问题: 当我运行我的普通爬虫时,一切正常,但是当我在 API 框架中运行(稍微修改但基本相同的)代码时,waitForSelector 突然总是超时。我已经尝试了所有常见的解决方法:waitForNavigation、截屏和检查等,但没有任何帮助。我已经读了很多书,当我从 API 的上下文中调用我的刮板时,我是否在异步/等待方面搞砸了?我对此还是很陌生,所以请多多包涵。这是工作脚本的代码 - 我指出了重要的部分

const puppeteer = require("puppeteer");
const chalk = require("chalk");
const fs = require('fs');
const error = chalk.bold.red;
const success = chalk.keyword("green");

address = 'Gumpendorfer Straße 12, 1060 Wien';

(async () => {
  try {
    // open the headless browser
      var browser = await puppeteer.launch();

    // open a new page
      var page = await browser.newPage();

    // enter url in page
      await page.goto(`https://mein.wien.gv.at/Meine-Amtswege/richtwert?subpage=/lagezuschlag/`, {waitUntil: 'networkidle2'});
   // continue without newsletter
      await page.click('#dss-modal-firstvisit-form > button.btn.btn-block.btn-light');
   // let everyhting load
      await page.waitFor(1000)
      console.log('waiting for iframe with form to be ready.');
      //wait until selector is available
      await page.waitForSelector('iframe');
      console.log('iframe is ready. Loading iframe content');
      //choose the relevant iframe
      const elementHandle = await page.$(
          'iframe[src="/richtwertfrontend/lagezuschlag/"]',
      );
      //go into frame in order to input info
      const frame = await elementHandle.contentFrame();
      //enter address
      console.log('filling form in iframe');
      await frame.type('#input_adresse', address, { delay: 100});

      //choose first option from dropdown
      console.log('Choosing from dropdown');
      await frame.click('#react-autowhatever-1--item-0');

      console.log('pressing button');
      //press button to search
      await frame.click('#next-button');

      // scraping data
            console.log('scraping')
            await frame.waitForSelector('#summary > div > div > br ~ div');//This keeps failing in the API
            const res = await frame.evaluate(() => {
              const rows = [...document.querySelectorAll('#summary > div > div > br ~ div')];
              const cells = rows.map(
                row => [...row.querySelectorAll('div')]
                         .map(cell => cell.innerText)
              );
              return cells;
            });

            await browser.close();
                console.log(success("Browser Closed"));
                const mapFields = (arr1, arr2) => {
                    const mappedArray = arr2.map((el) => {
                        const mappedArrayEl = {};
                        el.forEach((value, i) => {
                            if (arr1.length < (i+1)) return;
                            mappedArrayEl[arr1[i]] = value;
                        });
                        return mappedArrayEl;
                    });
                    return mappedArray;
                }
            const Arr1 = res[0];
            const Arr2 = res.slice(1,3);

            let dataObj = {};
            dataObj[address] = [];
            // dataObj['lagezuschlag'] = mapFields(Arr1, Arr2);
            // dataObj['adresse'] = address;
            dataObj[address] = mapFields(Arr1, Arr2);

            console.log(dataObj);


  } catch (err) {
      // Catch and display errors
      console.log(error(err));
      await browser.close();
      console.log(error("Browser Closed"));
    }


})();

我只是不明白为什么它会在一种情况下起作用,而在另一种情况下却不起作用,即使我几乎没有改变什么。对于 API,我基本上将异步函数的名称更改为const search = async (address) => {这样我就可以在我的服务器端脚本中使用查询来调用它。在此先感谢 - 我没有附加 API 代码,因为我不想让问题变得混乱。如果有必要我可以更新它

标签: node.jsapiweb-scrapingpuppeteer

解决方案


我自己解决了这个问题。事实证明,这个问题并没有我想象的那么复杂,而且解决起来也很简单。问题不在于超时的选择器,而在于以前的选择器,特别是从下拉选择器中输入和选择。本质上,事情进展得太快了。在输入搜索查询之前,下拉菜单已经按下并且废话出来了。我是如何解决的:我在选择下拉列表之前包含了一个 waitFor(1000) 调用,并且一切都很顺利。一个有趣的认识是,即使那个选择器超时,它实际上并不是问题的根源。但就像我说的那样,简单得令人讨厌,我问这个我觉得很愚蠢:) 但也许有人会看到这个并从我的错误中吸取教训


推荐阅读