node.js - 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 代码,因为我不想让问题变得混乱。如果有必要我可以更新它
解决方案
我自己解决了这个问题。事实证明,这个问题并没有我想象的那么复杂,而且解决起来也很简单。问题不在于超时的选择器,而在于以前的选择器,特别是从下拉选择器中输入和选择。本质上,事情进展得太快了。在输入搜索查询之前,下拉菜单已经按下并且废话出来了。我是如何解决的:我在选择下拉列表之前包含了一个 waitFor(1000) 调用,并且一切都很顺利。一个有趣的认识是,即使那个选择器超时,它实际上并不是问题的根源。但就像我说的那样,简单得令人讨厌,我问这个我觉得很愚蠢:) 但也许有人会看到这个并从我的错误中吸取教训
推荐阅读
- python - 在列表的每个节点上存储列表列表
- sql - 如何添加两个不同查询的结果?
- terminal - 如何在 UTOP 中设置制表符空间键绑定
- python-2.7 - 如何使用用户输入 MGRS 坐标通过 arcpy 创建 Shapfile
- vue.js - 如何将数据传递给链接元素的键和链接?
- rust - Reqwest 的 Client.post() 为 File.io API 返回 400 错误请求
- python - 重试python请求模块挂起
- c# - 我可以在 HttpModule 中使用 Db 上下文吗?
- c++ - 通过 move_pages() 查询失败
- python - python - 通过分隔符拆分 CSV 文件中的字符串