因此,我尝试从一个网站(这是一个包含公共(public)信息的网站)中获取一些基本的公司数据。我正在使用 Node 和 Puppeteer 来执行此操作。下面的工作代码成功地抓取了第一页,但是当点击第二页时,我收到错误:执行上下文被破坏,很可能是因为导航。
,现在我'我收到错误消息,说我的函数不是函数。
谁能指出我做错了什么以及抓取所有 28 页的最佳方法是什么?
成功抓取第一页
const puppeteer = require("puppeteer");
// var fs = require("fs");
const fsp = require("fs").promises;
const fs = require("fs");
let pageCount = 1; // 21 full pages of content
let companyRows;
function delay(time) {
return new Promise(function(resolve) {
setTimeout(resolve, time);
});
}
(async () => {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => {
for (let i = 0; i < msg.args().length; ++i)
console.log(`${i}: ${msg.args()[i]}`);
});
await page.goto(
"http://dpsstnet.state.or.us/IRIS_PublicInquiry/PrivateSecurity/SMSAgcyTable.aspx"
);
//Clicks a tag by id
await page.click("#btnNaLL");
await page.waitFor(1000);
const result = await page.evaluate(() => {
let row = document.querySelectorAll("tr");
let companyData = [];
row.forEach(el => {
let company = {};
let count = 0;
for (data of el.cells) {
switch (count) {
case 0:
company.name = data.innerText.trim();
case 1:
company.primaryContact = data.innerText.trim();
case 2:
company.address = data.innerText.trim();
case 3:
company.phone = data.innerText.trim();
case 4:
company.county = data.innerText.trim();
case 5:
company.status = data.innerText.trim();
default:
company.default = data.innerText.trim();
}
count++;
companyData.push(company);
//GOT SOME STUUFFFF
console.log(JSON.stringify(companyData));
}
});
// await page.waitFor(3000);
// await fsp.writeFile("./json/file.json", result.stringify());
companyData = companyData.filter((a, b) => companyData.indexOf(a) === b);
companyData = companyData.filter(e => e.status === "Active");
return companyData;
});
// fsp.writeFile(
// "./json/file.json",
// JSON.stringify(companyData, null, 2),
// err =>
// err
// ? console.error("Data not written!", err)
// : console.log("Data Written")
// );
await fsp.writeFile(
"./json/file.json",
JSON.stringify(result, null, 2),
err =>
err
? console.error("Data not written!", err)
: console.log("Data Written")
);
await page.screenshot({
path: "./screenshots/page1.png"
});
await page.pdf({ path: "./pdfs/page1.pdf" });
await browser.close();
return result;
} catch (error) {
console.log(error);
}
})();
重写代码以浏览页面(不起作用) 目前,我在运行此程序时收到“clickLink 不是函数”。
const puppeteer = require("puppeteer");
const fsp = require("fs").promises;
const fs = require("fs");
let pageCount = 1; // 21 full pages of content
let companyRows;
let pageToClick;
function delay(time) {
return new Promise(function(resolve) {
setTimeout(resolve, time);
});
}
(async () => {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const clickLink = link => {
page.click(link);
page.waitFor(1000);
};
page.on("console", msg => {
for (let i = 0; i < msg.args().length; ++i)
console.log(`${i}: ${msg.args()[i]}`);
});
await page.goto(
"http://dpsstnet.state.or.us/IRIS_PublicInquiry/PrivateSecurity/SMSAgcyTable.aspx"
);
//Clicks a tag by id
await page.click("#btnNaLL");
await page.waitFor(1000);
let fullResult = [];
let result;
result = await page.evaluate(
(fullResult, clickLink => {
let row = document.querySelectorAll("tr");
let companyData = [];
let pageList = document.querySelectorAll("b > a");
for (let step = 0; step < 2; step++) {
row.forEach(el => {
let company = {};
let count = 0;
for (data of el.cells) {
switch (count) {
case 0:
company.name = data.innerText.trim();
case 1:
company.primaryContact = data.innerText.trim();
case 2:
company.address = data.innerText.trim();
case 3:
company.phone = data.innerText.trim();
case 4:
company.county = data.innerText.trim();
case 5:
company.status = data.innerText.trim();
default:
company.default = data.innerText.trim();
}
count++;
companyData.push(company);
//GOT SOME STUUFFFF
console.log(JSON.stringify(companyData));
}
});
companyData = companyData.filter(
(a, b) => companyData.indexOf(a) === b
);
companyData = companyData.filter(e => e.status === "Active");
fullResult = [...fullResult, ...companyData];
// console.log(JSON.stringify(pageList[step].innerText));
clickLink(pageList[step]);
}
return fullResult;
},
fullResult,
clickLink
);
await fsp.writeFile(
"./json/file.json",
JSON.stringify(result, null, 2),
err =>
err
? console.error("Data not written!", err)
: console.log("Data Written")
);
//*
await page.screenshot({
path: "./screenshots/page1.png"
});
await page.pdf({ path: "./pdfs/page1.pdf" });
await browser.close();
return result;
} catch (error) {
console.log(error);
}
})();
我想我只是不理解 puppeteer 最佳实践背后的一些概念。
上面的代码只是我最近的分页和抓取尝试。我还尝试使用 pageList[step].click() 而不是 clickLink 函数。我还尝试将 for 循环移到评估之外,并使用“await page.click(nextPageNode)”多次重新运行代码,但这很困惑,而且也不起作用。
如果您想测试该项目:它已上传到 https://github.com/jIrwinCline/scrapeDPSST 。没有自述文件仅供引用,但它非常简单。只需下拉代码,然后运行“node index.js”即可。
请帮忙!我最后一天一直在做分页的事情。
最佳答案
第一个问题是你不能像这样将函数传递给evaluate
。为了向网页上下文公开函数,您应该使用 exposeFunction
。
即使您公开 clickLink,它也会引发 page
错误。了解不同的执行上下文非常重要。 Puppteer 的执行上下文与网页的执行上下文不同。 page
是 puppeteer 上下文中的对象,而 evaluate
在网页的执行上下文中运行您的函数。所以你不能通过 page
来评估。
对于分页,我建议将该 pageList 取出到 puppeteer 上下文,因为当您导航时执行上下文将被破坏:
const pageList = await page.$$('b > a');
现在循环访问 puppeteer 上下文中的链接并单击它们并运行 page.evaluate
以获取数据。
for (let link of pageList){
await Promise.all([
page.waitForNavigation(),
link.click()
]);
//get result
await page.evaluate(() => ...);
}
确保链接不会在新选项卡中打开,因为页面引用当前页面/选项卡。
关于javascript - 如何使用 Puppeteer 解决分页网页抓取问题?正确定位 Node ,但在 for 循环中单击它不断抛出错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60408776/