使用Puppeteer与Node.js开发YouTube搜索爬虫时无限滚动失效问题
解决YouTube搜索结果爬虫无法获取全部内容的问题
你的代码存在几个核心问题,导致只能爬取部分结果:
- 先执行爬取再滚动页面,抓取的只是初始加载的少量内容,滚动后加载的新内容完全没被处理
- 原滚动函数没适配YouTube的动态加载特性,滚动一次后可能新内容还没渲染完成就停止了,而且页面高度会随新内容加载持续增加,原逻辑会提前终止滚动
- 页面跳转时未等待完全加载,初始内容可能还没渲染好就开始爬取
下面是修改后的完整代码,解决这些问题:
const puppeteer = require('puppeteer'); async function parseSearch(page) { // 等待目标元素加载完成,避免DOM未渲染就爬取 await page.waitForSelector(".text-wrapper.style-scope.ytd-video-renderer"); const search = await page.evaluate(() => { const searchResults = document.querySelectorAll(".text-wrapper.style-scope.ytd-video-renderer"); const lists = []; searchResults.forEach(element => { const vidName = element.querySelector("h3"); const channel = element.querySelector("a.yt-simple-endpoint.style-scope.yt-formatted-string"); lists.push({ video_name: vidName?.innerText || '无标题', // 增加容错,避免元素不存在报错 channel_name: channel?.innerText || '未知频道' // 用innerText代替innerHTML,避免获取HTML标签 }); }); return lists; }); return search; } async function autoScroll(page) { await page.evaluate(async () => { let lastScrollHeight = document.body.scrollHeight; while (true) { window.scrollTo(0, document.body.scrollHeight); // 等待新内容加载,可根据网络情况调整等待时长 await new Promise(resolve => setTimeout(resolve, 1000)); const newScrollHeight = document.body.scrollHeight; // 滚动高度不再变化时,说明没有新内容了 if (newScrollHeight === lastScrollHeight) { break; } lastScrollHeight = newScrollHeight; } }); } (async () => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); // 等待页面网络空闲,确保初始内容加载完成 await page.goto("https://www.youtube.com/results?search_query=programming", { waitUntil: 'networkidle2' }); // 先滚动到底部加载全部内容 await autoScroll(page); // 再爬取所有结果 const result = await parseSearch(page); console.log(`共爬取到${result.length}条结果:`); console.log(result); await browser.close(); })(); console.log("END");
关键修改说明:
- 调整执行顺序:先滚动加载全部内容,再调用爬取函数
- 优化滚动逻辑:循环滚动直到页面高度不再变化,确保所有动态内容加载完成,每次滚动后等待1秒让内容渲染
- 增加等待机制:
page.waitForSelector确保目标元素存在后再爬取,page.goto的waitUntil参数保证初始页面加载完成 - 容错处理:用可选链
?.避免元素不存在时抛出错误,给字段设置默认值 - 修正文本获取:用
innerText代替innerHTML获取频道名称,避免拿到多余HTML标签
内容的提问来源于stack exchange,提问作者Vishnu Nair
相关产品推荐
相关产品推荐

