使用Cheerio JS提取赛事赔率并按场次聚合存储的技术求助
解决Cheerio爬取赛事表格时的同场次赔率聚合问题
我正在用Node.js和Cheerio练习网页爬取,需要从下方的赛事HTML表格中提取每场比赛的主队赔率(Home Odds)和客队赔率(Away Odds),将单个赔率存入对象后,把同一场次的所有赔率对象聚合到对应赛事的odds数组中。目前我的实现代码无法正确完成聚合,请求帮忙修正。
示例HTML表格
<table> <thead> <tr> <th>Match Date</th> <th>League</th> <th>Win (Home Team)</th> <th>Home Odds</th> <th>Radish (Standard)</th> <th>Away Odds</th> <th>Lose (Away Team)</th> </tr> </thead> <tbody> <tr> <td>04-24 19:00</td> <td>Ukraine D1</td> <td>Dnipro-1</td> <td>1.26</td> <td>5.11</td> <td>9.67</td> <td>Rukh Lviv</td> </tr> <tr> <td>04-24 19:00</td> <td>Ukraine D1</td> <td>Dnipro-1</td> <td>1.87</td> <td>2.75</td> <td>1.87</td> <td>Rukh Lviv</td> </tr> <tr> <td>04-24 20:00</td> <td>Ukraine D1</td> <td>FC Oleksandriya</td> <td>1.58</td> <td>3.59</td> <td>5.51</td> <td>FC Inhulets Petrove</td> </tr> <tr> <td>04-24 20:00</td> <td>Ukraine D1</td> <td>FC Oleksandriya</td> <td>1.82</td> <td>2.25</td> <td>1.92</td> <td>FC Inhulets Petrove</td> </tr> </tbody> <tbody> <tr> <td>04-24 21:00</td> <td>PBA</td> <td>Meralco Bolts</td> <td>1.26</td> <td>5.11</td> <td>9.67</td> <td>Barangay Ginebra</td> </tr> <tr> <td>04-24 21:00</td> <td>PBA</td> <td>Meralco Bolts</td> <td>1.87</td> <td>2.75</td> <td>1.87</td> <td>Barangay Ginebra</td> </tr> <tr> <td>04-24 22:00</td> <td>PBA</td> <td>San Miguel Beermen</td> <td>1.58</td> <td>3.59</td> <td>5.51</td> <td>Blackwater</td> </tr> <tr> <td>04-24 22:00</td> <td>PBA</td> <td>San Miguel Beermen</td> <td>1.82</td> <td>2.25</td> <td>1.92</td> <td>Blackwater</td> </tr> </tbody> </table>
当前实现代码
初始爬取代码
const data = []; const $table = $("table > tbody"); let odds = []; $table.find("tr").each((i, row) => { const $trs = $(row); const $date = $trs.find("td:first").text().trim(); const $league_name = $trs.find("td:nth-child(2)").text().trim(); const $home_team = $trs.find("td:nth-child(3)").text().trim(); const $away_team = $trs.find("td:last").text().trim(); // Odds const $home_odds = $trs.find("td:nth-child(4)").text().trim(); const $away_odds = $trs.find("td:nth-child(6)").text().trim(); let game_odds = {}; game_odds = { home: $home_odds, away: $away_odds, }; odds.push(game_odds); data.push({ date: $date, league_name: $league_name, home_team: $home_team, away_team: $away_team, odds, }); });
去重处理代码
let finalData = []; let previousLeague = null; let previousHomeTeam = null; let previousAwayTeam = null; for (let i = 0; i < data.length; i++) { if (data[i].league_name === previousLeague) { if ( data[i].home_team === previousHomeTeam && data[i].away_team === previousAwayTeam ) { // The odds of that match is pushed to the finalData array } else { previousHomeTeam = data[i].home_team; previousAwayTeam = data[i].away_team; finalData.push({ date: data[i].date, league_name: data[i].league_name, home_team: data[i].home_team, away_team: data[i].away_team, }); } } else { previousLeague = data[i].league_name; } }
预期输出
[ { date: '04-24 19:00', league_name: 'Ukraine D1', home_team: 'Dnipro-1', away_team: 'Rukh Lviv', odds: [ { home: 1.26, away: 9.67, }, { home: 1.87, away: 1.87, }, ] }, { date: '04-24 20:00', league_name: 'Ukraine D1', home_team: 'FC Oleksandriya', away_team: 'FC Inhulets Petrove', odds: [ { home: 1.58, away: 5.51, }, { home: 1.82, away: 1.92, }, ] }, { date: '04-24 21:00', league_name: 'PBA', home_team: 'Meralco Bolts', away_team: 'Barangay Ginebra', odds: [ { home: 1.26, away: 9.67, }, { home: 1.87, away: 1.87, }, ] }, { date: '04-24 22:00', league_name: 'PBA', home_team: 'San Miguel Beermen', away_team: 'Blackwater', odds: [ { home: 1.58, away: 5.51, }, { home: 1.82, away: 1.92, }, ] } ]
问题分析
你的代码存在两个核心问题:
- 初始爬取时使用全局
odds数组,每遍历一行就追加当前赔率,导致每个赛事对象的odds都包含所有历史赔率,而非仅当前赛事的。 - 去重逻辑仅处理了赛事条目去重,但未实现将对应赔率聚合到已有赛事对象的逻辑。
修正后的代码
我们可以通过唯一标识分组的方式,在遍历过程中直接完成赛事和赔率的聚合,无需后续去重:
const finalData = []; // 用对象存储已存在的赛事,键为唯一标识 const matchMap = {}; $("table > tbody tr").each((i, row) => { const $tr = $(row); // 提取赛事基础信息 const date = $tr.find("td:first").text().trim(); const league_name = $tr.find("td:nth-child(2)").text().trim(); const home_team = $tr.find("td:nth-child(3)").text().trim(); const away_team = $tr.find("td:last").text().trim(); // 提取当前赔率并转为数值类型 const home_odds = parseFloat($tr.find("td:nth-child(4)").text().trim()); const away_odds = parseFloat($tr.find("td:nth-child(6)").text().trim()); const currentOdds = { home: home_odds, away: away_odds }; // 生成唯一标识,确保同场次赛事被正确分组 const matchKey = `${date}-${league_name}-${home_team}-${away_team}`; if (matchMap[matchKey]) { // 赛事已存在,追加当前赔率到odds数组 matchMap[matchKey].odds.push(currentOdds); } else { // 赛事不存在,创建新对象并加入结果集合 const newMatch = { date, league_name, home_team, away_team, odds: [currentOdds] }; matchMap[matchKey] = newMatch; finalData.push(newMatch); } }); console.log(finalData);
代码说明
- 唯一标识分组:用
日期-联赛-主队-客队作为唯一键,确保同一场次的赛事被归为一组。 - 实时聚合:遍历每一行时直接判断赛事是否存在,存在则追加赔率,不存在则新建赛事对象,一次遍历完成所有逻辑。
- 数据类型转换:将赔率字符串转为数值类型,符合预期输出的格式要求。
- 简化流程:避免了先收集重复数据再去重的冗余操作,提升了代码效率。
内容的提问来源于stack exchange,提问作者HilthPH
相关产品推荐
相关产品推荐

