如何用Crawlee/Apify抓取带无限滚动的Twitch页面?
解决Twitch VTuber频道无限滚动抓取问题
你的问题出在infiniteScroll的默认配置不匹配Twitch主页面的滚动结构,导致滚动逻辑没在主页面生效,反而被带到了详情页。以下是具体修正方案:
核心问题分析
Twitch的VTuber目录内容在特定容器内实现无限滚动,默认的infiniteScroll()会监听整个页面滚动,但主页面的内容加载容器并非全局body;同时缺少滚动后的等待逻辑,导致滚动未触发内容加载就提前执行了链接入队操作。
修正后的代码
import { Dataset, createPlaywrightRouter, enqueueLinks, playwrightUtils, } from "crawlee"; export const router = createPlaywrightRouter(); router.addDefaultHandler( async ({ log, page, request, infiniteScroll }) => { log.debug(`Processing: ${request.url}`); // 等待频道卡片容器加载完成 await page.waitForSelector('[data-testid="directory-page__card-grid"]'); // 配置infiniteScroll,针对Twitch的内容容器滚动 await infiniteScroll({ // 指定Twitch的频道列表容器作为滚动目标 scrollTarget: '[data-testid="directory-page__card-grid"]', // 每次滚动的距离(适配Twitch的卡片高度) scrollDownBy: 800, // 滚动后等待新内容加载的时间 waitForScrollStop: 2000, // 最大滚动次数(可根据需求调整) maxScrolls: 5, }); // 滚动完成后,入队所有频道详情链接 await enqueueLinks({ selector: '[data-a-target="preview-card-image-link"]', label: "detail", }); } ); router.addHandler("detail", async ({ request, page, log }) => { log.debug(`Extracting data: ${request.url}`); await page.waitForSelector('[id="live-channel-about-panel"]'); const instagram = await page .locator('a[role="link"][href*="instagram"]') .getAttribute("href"); const twitter = await page .locator('a[role="link"][href*="twitter"]') .getAttribute("href"); const discord = await page .locator('a[role="link"][href*="discord"]') .getAttribute("href"); const results = { url: request.url, instagram, twitter, discord }; // 将结果保存到Dataset(原代码漏了这一步,数据不会持久化) await Dataset.pushData(results); log.debug(results); });
关键修改点说明
- 指定滚动目标容器:通过
scrollTarget参数定位Twitch的频道列表容器,确保滚动逻辑作用在正确区域。 - 调整滚动参数:设置
scrollDownBy控制每次滚动距离,waitForScrollStop等待新内容加载完成,maxScrolls限制滚动次数避免无限滚动。 - 修正链接选择器:用更稳定的
[data-a-target="preview-card-image-link"]选择频道链接,替代原有的动态类选择器(Twitch类名可能随时变化)。 - 添加数据持久化:新增
await Dataset.pushData(results),将抓取的数据保存到Dataset(原代码仅做日志输出,数据不会留存)。
备选手动滚动方案
如果内置infiniteScroll仍不生效,可尝试手动模拟滚动:
// 替换infiniteScroll的手动滚动逻辑 const scrollContainer = page.locator('[data-testid="directory-page__card-grid"]'); for (let i = 0; i < 5; i++) { await scrollContainer.evaluate(el => el.scrollBy(0, 800)); await page.waitForTimeout(2000); // 等待新卡片加载完成 await page.waitForSelector('[data-a-target="preview-card-image-link"]', { state: 'attached' }); }
内容的提问来源于stack exchange,提问作者Syed Hasnain
相关产品推荐
相关产品推荐

