概述:
这不是实际的问题,但我已经简化了问题并将其转换为更容易理解的问题。我得到从 page.evaluate()
返回的相同数据放置在 for...of
内在循环的每次迭代期间循环。我猜是由于闭包,我很久没用JS编程了,所以无法实现IIFE
与 async
功能。另外,我认为我们无法实现 forEach
而不是for...if
与 async
功能。如果发布的问题太长,我提前道歉,但我想说清楚。
描述: 我正在尝试抓取包含杂货数据的网页。食品杂货的类型(如蔬菜、水果、乳制品、面包店)可以从选择元素(下拉列表)中进行选择。当选择选项时,根据类型显示杂货 list 。
-
grocery-choice
是让我们选择选项的 select 标签的 id (即select#grocery-choice
) -
grocery-item
是包含杂货中每个单独商品的数据的 div 类。(即div.grocery-item
)例如: 对于杂货类别 水果 它包含香蕉、苹果等的数据。每个 div 都有一个<h4>
和两个<p>
分别表示名称、价格和可用性元素。
我正在循环数组 ( [ 'fruit', 'vegetable', 'bakery']
) 中的项目,然后将每个项目作为 JSON 添加到数组 grocery_info
中并返回grocery_inf
来自page.evaluate()
。 grocery_all
是分配返回值的变量。
这是我遇到问题的代码部分。
async function scrape_and_display(item) {
await page.select("select#grocery-choice", item);
await page.waitForSelector("div.grocery-item");
const grocery_all = await page.evaluate((x) => {
let grocery_info = [];
let grocery_list = document.querySelectorAll("div.grocery-item");
grocery_list.forEach((item) => {
grocery_info.push({
name: item.getElementsByTagName("h4")[0].innerText,
price: item.getElementsByTagName("p")[0].innerText,
availability: item.getElementsByTagName("p")[1].innerText,
});
});
return grocery_info;
});
console.log(grocery_all);
}
for (item of ["fruit", "vegetable", "bakery"]) {
await scrape_and_display(item);
}
所需输出:
[
{ name: 'Banana', price: '$10 / kg', availability: 'In Stock' },
{ name: 'Apple', price: '$20 / kg', availability: 'In Stock' },
{ name: 'Grape', price: '$45 / kg', availability: 'Out of Stock' },
{ name: 'Orange', price: '$10 / kg', availability: 'In Stock' }
]
[
{ name: 'Brocli', price: '$10 / kg', availability: 'In Stock' },
{ name: 'Pumpkin', price: '$15 / kg', availability: 'In Stock' },
{ name: 'Lettuce', price: '$7 / kg', availability: 'In Stock' }
]
[
{ name: 'Bread', price: '$15 / piece', availability: 'In Stock' },
{ name: 'Apple Pie', price: '$20 / piece', availability: 'In Stock' }
]
当前输出:
[
{ name: 'Bread', price: '$15 / piece', availability: 'In Stock' },
{ name: 'Apple Pie', price: '$20 / piece', availability: 'In Stock' }
]
[
{ name: 'Bread', price: '$15 / piece', availability: 'In Stock' },
{ name: 'Apple Pie', price: '$20 / piece', availability: 'In Stock' }
]
[
{ name: 'Bread', price: '$15 / piece', availability: 'In Stock' },
{ name: 'Apple Pie', price: '$20 / piece', availability: 'In Stock' }
]
这是实际问题,我没有杂货类别,而是尼泊尔各州,而不是杂货商品 em>,我需要学校;我不是登录控制台,而是将其导出为CSV 文件。 如果您检查 CSV 文件,您会发现所有相同的数据,即下拉列表的最后一项,即最后一个选项。 这是工作代码:
const puppeteer = require("puppeteer");
const createCsvWriter = require("csv-writer").createObjectCsvWriter;
(async () => {
const browser = await puppeteer.launch({
headless: false,
IgnoreHTTPSErrors: true,
});
const page = await browser.newPage();
await page.setViewport({ width: 1366, height: 820 });
await page.goto("https://www.pabson.org/#!/School-locator");
const selectOptions = await page.$$eval(
"select#ddlState > option",
(options) => {
return options.map(function (option) {
return { option: option.innerHTML, value: option.value };
});
}
);
async function scrape_and_save(item) {
// let item = obj;
await page.select("#ddlState", item.value);
try {
await page.waitForSelector("div.count-box-info");
const school_array = await page.evaluate((x) => {
let school = [];
let school_list = document.querySelectorAll("div.count-box-info");
school_list.forEach((item) => {
school.push({
name: item.getElementsByTagName("h4")[0].innerText,
phone: item.getElementsByTagName("p")[0].innerText,
email: item.getElementsByTagName("p")[1].innerText,
});
});
return school;
});
let csvWriter = createCsvWriter({
path: "school_" + item.option + ".csv",
header: [
{ id: "name", title: "NAME" },
{ id: "phone", title: "PHONE" },
{ id: "email", title: "EMAIL" },
],
});
csvWriter
.writeRecords(school_array) // returns a promise
.then(() => {
console.log("...Done");
});
} catch (e) {
console.log(e);
}
}
for (item of selectOptions) {
await scrape_and_save(item);
}
await browser.close();
})();
任何帮助将不胜感激。提前致谢。
最佳答案
重新渲染学校列表似乎需要一些时间,而您没有等待这个时间,因此您的代码只是太快地抓取了相同的数据。您可以等待一些更改,例如列表上方主要信息文本的更改:
async function scrape_and_save(item) {
const previousData = await page.evaluate(
() => document.querySelector('#divschoolData').innerText
);
await page.select("#ddlState", item.value);
await page.waitForFunction(
data => { return data !== document.querySelector('#divschoolData').innerText; },
{},
previousData,
);
try {
// ...
顺便说一句,尝试始终添加 const/let for 循环变量以防止范围问题:
for (const item of selectOptions) {
并使用严格模式以免忘记这一点)
关于javascript - 循环内的“page.evaluate()”(在 Puppeteer 中)返回相同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63704842/