nic*_*ng2 8 javascript node.js web-scraping infinite-scroll puppeteer
所以我试图在下图中的封闭区域中刮取所有音乐会:
https://i.stack.imgur.com/7QIMM.jpg
问题是列表只显示前 10 个选项,直到您在该特定 div 中向下滚动到底部,然后它会动态显示更多选项,直到没有更多结果。我尝试按照下面的答案链接,但无法向下滚动以显示所有“音乐会”:
这是我的基本代码:
const browser = await puppeteerExtra.launch({ args: [
'--no-sandbox'
]});
async function functionName() {
const page = await browser.newPage();
await preparePageForTests(page);
page.once('load', () => console.log('Page loaded!'));
await page.goto(`https://www.google.com/search?q=concerts+near+poughkeepsie&client=safari&rls=en&uact=5&ibp=htl;events&rciv=evn&sa=X&fpstate=tldetail`);
const resultList = await page.waitForSelector(".odIJnf");
const scrollableSection = await page.waitForSelector("#Q5Vznb"); //I think this is the div that contains all the concert items.
const results = await page.$$(".odIJnf"); //this needs to be iterable to be used in the for loop
//this is where I'd like to scroll down the div all the way to the bottom
for (let i = 0; i < results.length; i++) {
const result = await (await results[i].getProperty('innerText')).jsonValue();
console.log(result)
}
}
Run Code Online (Sandbox Code Playgroud)
正如您在问题中提到的,当您运行 时page.$$,您会返回一个ElementHandle. 来自Puppeteer 的文档:
ElementHandle 表示页内 DOM 元素。ElementHandles 可以使用该
page.$方法创建。
这意味着您可以迭代它们,但您还必须运行evaluate()或$eval()遍历每个元素才能访问 DOM 元素。
我从您的代码片段中看到您正在尝试访问div处理列表scroll事件的父级。问题是该页面似乎正在使用自动生成的classes和ids. 这可能会使您的代码变得脆弱或无法正常工作。最好尝试直接访问ul, li, 。div
我创建了这个片段,可以ITEMS从该网站获取大量的音乐会:
const puppeteer = require('puppeteer')
/**
* Constants
*/
const ITEMS = process.env.ITEMS || 50
const URL = process.env.URL || "https://www.google.com/search?q=concerts+near+poughkeepsie&client=safari&rls=en&uact=5&ibp=htl;events&rciv=evn&sa=X&fpstate=tldetail"
/**
* Main
*/
main()
.then( () => console.log("Done"))
.catch((err) => console.error(err))
/**
* Functions
*/
async function main() {
const browser = await puppeteer.launch({ args: ["--no-sandbox"] })
const page = await browser.newPage()
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36')
await page.goto(URL)
const results = await getResults(page)
console.log(results)
await browser.close()
}
async function getResults(page) {
await page.waitForSelector("ul")
const ul = (await page.$$("ul"))[0]
const div = (await ul.$x("../../.."))[0]
const results = []
const recurse = async () => {
// Recurse exit clause
if (ITEMS <= results.length) {
return
}
const $lis = await page.$$("li")
// Slicing this way will avoid duplicating the result. It also has
// the benefit of not having to handle the refresh interval until
// new concerts are available.
const lis = $lis.slice(results.length, Math.Infinity)
for (let li of lis) {
const result = await li.evaluate(node => node.innerText)
results.push(result)
}
// Move the scroll of the parent-parent-parent div to the bottom
await div.evaluate(node => node.scrollTo(0, node.scrollHeight))
await recurse()
}
// Start the recursive function
await recurse()
return results
}
Run Code Online (Sandbox Code Playgroud)
通过研究页面结构,我们看到ul列表的 嵌套在距处理 的3div层深处。我们也知道页面上只有两个,第一个就是我们想要的。这就是我们在这些方面所做的:divscrollul
const ul = (await page.$$("ul"))[0]
const div = (await ul.$x("../../.."))[0]
Run Code Online (Sandbox Code Playgroud)
该$x函数计算相对于文档的 XPath 表达式作为其上下文节点*。它允许我们遍历 DOM 树,直到找到div我们需要的为止。然后我们运行递归函数,直到获得我们想要的项目。