Puppeteer选择器仅返回单个元素:无法获取全部足球赛事数据求助
Puppeteer爬取Flashscore赛事数据仅返回单场的解决方案
问题背景
使用Puppeteer开发API获取https://www.flashscore.com.br的足球赛事数据,用于移动端应用,但脚本仅返回单场赛事数据,而非所有赛事。
原服务文件代码
import puppeteer from "puppeteer"; class NextGamesService { async execute() { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://www.flashscore.com.br'); const games = await page.$$('#live-table > section > div > div'); const game1 = [] for (const game of games) { const time = await page.evaluate((el) => el.querySelector('#g_1_IcYs8jIk > div.event__time')?.textContent, game) const home = await page.evaluate((el) => el.querySelector('div.event__participant.event__participant--home')?.textContent, game) const away = await page.evaluate((el) => el.querySelector('div.event__participant.event__participant--away')?.textContent, game) const league = await page.evaluate((el) => el.querySelector('div.icon--flag.event__title.fl_81 > div > span.event__title--name')?.textContent, game) game1.push({ time, home, away, league}); } return ({game1}) } } export { NextGamesService }
原控制器代码
import { NextGamesService } from "./nextGamesService"; import { Request, Response } from "express"; class NextGamesController { async handle(req: Request, res: Response) { const nextGamesService =new NextGamesService(); const games = await nextGamesService.execute() return res.json(games) } } export {NextGamesController}
当前错误响应
{ "game1": [ { "time": "11:30", "home": "Dortmund", "away": "Augsburg", "league": "Bundesliga" } ] }
问题原因
- 时间选择器用固定ID:
#g_1_IcYs8jIk是单场赛事的唯一ID,所有循环迭代都只会获取同一场的时间。 - 联赛选择器用固定类:
.fl_81是特定联赛的标识类,无法匹配其他联赛的赛事。 - 元素定位不准确:原赛事选择器未精准匹配所有赛事元素,且未等待页面动态加载完成。
- 多次调用
page.evaluate:循环内重复调用该方法,效率低且易出现上下文冲突。
修复后的服务文件代码
import puppeteer from "puppeteer"; class NextGamesService { async execute() { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://www.flashscore.com.br'); // 等待赛事元素加载完成,处理页面动态渲染 await page.waitForSelector('#live-table .event__match'); // 一次性获取所有赛事数据,避免多次evaluate调用 const gameData = await page.evaluate(() => { // 获取所有赛事元素 const gameElements = Array.from(document.querySelectorAll('#live-table .event__match')); return gameElements.map(game => { // 从当前赛事元素内获取相对数据 const time = game.querySelector('.event__time')?.textContent?.trim(); const home = game.querySelector('.event__participant--home')?.textContent?.trim(); const away = game.querySelector('.event__participant--away')?.textContent?.trim(); // 从赛事所在的联赛板块获取联赛名称 const league = game.closest('section')?.querySelector('.event__title--name')?.textContent?.trim(); return { time, home, away, league }; }).filter(item => item.time && item.home && item.away); // 过滤无效数据 }); await browser.close(); // 关闭浏览器,避免资源泄漏 return { games: gameData }; } } export { NextGamesService }
关键修改说明
- 等待动态元素:用
page.waitForSelector确保赛事元素渲染完成后再抓取,避免因页面加载慢导致的元素缺失。 - 相对选择器:所有数据选择器基于当前赛事元素或其父级联赛板块,彻底摆脱固定ID/类的限制。
- 单次
page.evaluate调用:一次性处理所有赛事数据,大幅提升效率并避免上下文问题。 - 过滤无效数据:移除未完全加载的赛事条目,保证返回数据的有效性。
- 关闭浏览器:添加
browser.close()防止资源长时间占用。
内容的提问来源于stack exchange,提问作者Bruno Araújo
相关产品推荐
相关产品推荐

