【发布时间】:2020-10-13 18:27:00
【问题描述】:
我有一个可以工作的 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 代码,因为我不想让问题变得混乱。有需要我可以更新
【问题讨论】:
-
你试过使用 frame.waitFor('#summary > div > div > br ~ div');这是一个废弃的函数,将在以后的版本中删除,但看看它是否与实际函数有问题会很有趣。
-
感谢您的回答!刚试了一下,它也超时了。如果有帮助,我正在使用 2 个单独的文件。一个处理服务器(处理搜索查询等),另一个我有实际的抓取代码。我导出抓取代码并在服务器特定脚本中调用它
标签: node.js api web-scraping puppeteer