
Haizei Worldcup 2026 Skill
- 46 installs
- 7 repo stars
- Updated June 13, 2026
- wuchubuzai2018/haizei-worldcup-2026
Helps with ai & agent building tasks.
About
haizei-worldcup-2026-skill is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- haizei-worldcup-2026-skill
- AI & Agent Building
- AI-coding skill
Haizei Worldcup 2026 Skill by the numbers
- 46 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #7,629 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wuchubuzai2018/haizei-worldcup-2026 --skill haizei-worldcup-2026-skillAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 46 |
|---|---|
| repo stars | ★ 7 |
| Last updated | June 13, 2026 |
| Repository | wuchubuzai2018/haizei-worldcup-2026 ↗ |
What it does
Helps with ai & agent building tasks.
Files
2026年美加墨世界杯数据获取
2026 美加墨世界杯数据。包含 48 支球队的分组配置、赛程、比赛详情、球队详情、球员详情、排名等。
核心能力
赛程查询:today 今日赛程、tomorrow 明日赛程、date 指定日期、group 按组筛选、team 按队筛选、stage 按阶段筛选、dates 可用日期列表、stats 赛程统计
比赛详情:
analysis- AI预测分析(胜率预测/历史战绩/情报/网友投票)lineup- 阵容(首发/替补/阵型/裁判/场地)live- 赛况(事件流/进球/红黄牌/换人/文字播报)stats- 技术统计(控球/射门/角球等)odds- 博彩赔率(初盘/即时盘/亚盘/大小球)
球队数据:
info- 球队资料/荣誉(成立时间/排名/身价/教练)schedule- 球队赛程(世界杯/友谊赛等)lineup- 球队阵容(教练组+26人名单/位置/俱乐部/身价)history- 历史成绩(历届世界杯战绩/小组赛果)stats- 数据统计(进球/助攻/控球等维度)
球员数据:
info- 球员资料(身高/体重/位置/惯用脚/年龄)ability- 能力雷达图(速度/射门/传球/盘带/防守/力量)honor- 球员荣誉(世界杯/联赛/金球奖等)transfer- 转会历史(5次主要转会记录)market- 市场估值趋势(身价变化曲线)news- 球员新闻(10条最新报道)stats- 赛事数据统计schedule- 出场记录
排名数据:
standings- 小组积分榜(12组A-L/已晋级/已淘汰/待定)fifa- FIFA排名(世界前50/升降变化/积分)players- 球员排行榜(30+维度:进球/助攻/过人/抢断/传球/红黄牌等)knockout- 淘汰赛对阵图
球队配置:list 48队、group 按组查询、hosts 东道主、pot 档位分布、find 模糊搜索
竞彩赔率(worldcup-calculator.js,数据源:体彩官方):
had- 胜平负赔率(主胜/平/客胜)hhad- 让球胜平负赔率(含让球数)crs- 比分赔率(31 种比分组合)ttg- 总进球赔率(0-7+ 球)hafu- 混合过关赔率(半场+全场 9 种组合)history- 赔率变化历史(按 matchId 查询)- 支持
--wc(只显示世界杯)、--team、--date、--json、summary模式
---
路由表
| 用户需求 | 脚本 | 参考文档 |
|---|---|---|
| 赛事基本信息、48队分组 | scripts/worldcup-teams.js info | overview.md |
| 球队配置(静态) | scripts/worldcup-teams.js | teams.md |
| 全部赛程 / 今日赛程 / 按组按队查询 | scripts/worldcup-schedule.js | schedule.md |
| 比赛情报 / 阵容 / 赛况 / 统计 / 指数 | scripts/worldcup-match.js | match.md |
| 球队资料 / 阵容 / 历史成绩 / 数据 | scripts/worldcup-team.js | team.md |
| 球员资料 / 动态 / 数据 / 赛程 | scripts/worldcup-player.js | player.md |
| 排名 / 球队榜 / 球员榜 / FIFA排名 | scripts/worldcup-rankings.js | rankings.md |
| 竞彩赔率(胜平负/让球/比分/总进球/混合过关) | scripts/worldcup-calculator.js | calculator.md ⭐ |
| 典型用户问题处理 | — | **workflows.md** ⭐ |
| 跨脚本组合用法 | — | usage.md |
⭐ AI 必读:workflows.md 包含 15+ 个真实工作流
快速命令
球队(静态配置)
node scripts/worldcup-teams.js list # 全部48队
node scripts/worldcup-teams.js group A # A组
node scripts/worldcup-teams.js find 加拿大 # 按名查 teamId
node scripts/worldcup-teams.js info # 赛事基本信息
node scripts/worldcup-teams.js hosts # 东道主(美国/加拿大/墨西哥)
node scripts/worldcup-teams.js pot 1 # 第一档球队赛程
node scripts/worldcup-schedule.js today # 今日赛程
node scripts/worldcup-schedule.js tomorrow # 明日赛程
node scripts/worldcup-schedule.js date 2026-06-14 # 指定日期
node scripts/worldcup-schedule.js group B # B组赛程
node scripts/worldcup-schedule.js team 巴西 # 巴西的赛程
node scripts/worldcup-schedule.js stage 小组赛 # 小组赛阶段
node scripts/worldcup-schedule.js dates # 有比赛的日期列表
node scripts/worldcup-schedule.js stats # 赛程统计比赛详情(matchId 来自赛程)
node scripts/worldcup-match.js info <matchId> # 基本信息
node scripts/worldcup-match.js analysis <matchId> # AI预测/情报/历史/投票
node scripts/worldcup-match.js lineup <matchId> # 首发/替补/阵型/裁判
node scripts/worldcup-match.js live <matchId> # 事件流/场地/文字播报
node scripts/worldcup-match.js stats <matchId> # 技术统计
node scripts/worldcup-match.js odds <matchId> # 博彩赔率球队详情(支持 teamId 或中文球队名)
node scripts/worldcup-team.js lookup 加拿大 # 查 teamId
node scripts/worldcup-team.js info 巴西 # 资料+荣誉(成立/排名/身价/教练)
node scripts/worldcup-team.js schedule 巴西 世界杯 # 该队赛程
node scripts/worldcup-team.js lineup 法国 # 教练+26人名单
node scripts/worldcup-team.js history 巴西 # 历届世界杯成绩
node scripts/worldcup-team.js stats 巴西 2026 小组赛 # 数据统计球员详情(playerId 来自球队阵容)
node scripts/worldcup-player.js info <playerId> # 资料+能力雷达+荣誉+转会
node scripts/worldcup-player.js news <playerId> # 球员新闻(10条)
node scripts/worldcup-player.js stats <playerId> 2026 世界杯 # 数据统计
node scripts/worldcup-player.js schedule <playerId> # 出场记录排名数据
node scripts/worldcup-rankings.js standings # 12组积分榜
node scripts/worldcup-rankings.js fifa 20 # FIFA 前20
node scripts/worldcup-rankings.js players 进球 # 射手榜
node scripts/worldcup-rankings.js players 助攻 10 # 助攻榜前10
node scripts/worldcup-rankings.js players 过人 # 过人榜
node scripts/worldcup-rankings.js categories # 支持的30+种排行榜
node scripts/worldcup-rankings.js knockout # 淘汰赛对阵图竞彩赔率(体彩官方,数据源 sporttery.cn)
node scripts/worldcup-calculator.js summary --wc # 世界杯精简模式(推荐)
node scripts/worldcup-calculator.js had --wc # 只看胜平负
node scripts/worldcup-calculator.js hhad --wc # 只看让球胜平负
node scripts/worldcup-calculator.js crs --wc # 只看比分赔率
node scripts/worldcup-calculator.js ttg --wc # 只看总进球
node scripts/worldcup-calculator.js hafu --wc # 只看混合过关
node scripts/worldcup-calculator.js --wc --team 巴西 # 过滤指定球队
node scripts/worldcup-calculator.js --wc --date 2026-06-14 # 过滤指定日期
node scripts/worldcup-calculator.js history <matchId> had # 赔率变化历史
node scripts/worldcup-calculator.js --wc --json | jq . # JSON 输出球员榜支持分类
进球、助攻、射门、射正、过人、过人成功、任意球、击中门框、快攻、快攻射门、丢失球权、解围、有效阻挡、拦截、抢断、1对1拼抢、1对1拼抢成功、拳击球、守门员出击、守门员出击成功、高空出击、传球、传球成功、关键传球、传中球、传中球成功、长传、成功长传、传球被断、红牌、黄牌、犯规、被侵犯、越位
关键词覆盖
赛程相关:今天有什么比赛、明天比赛、比赛时间、几点开球、哪场比赛、赛程表、今日比赛、明日比赛、小组赛程、X队比赛、X队下一场、揭幕战
比赛结果相关:比赛结果、X赢了还是输了、比分是多少、谁赢了、比赛报告、精彩瞬间、进球球员、红黄牌、换人、乌龙球、点球
直播相关:现在在直播、正在直播的比赛、直播源、哪个台直播
球队相关:球队阵容、球员名单、X队首发、X队球员、X队教练、历史战绩、世界杯成绩、历届表现、小组分组、X组情况、出线形势、东道主、身价多少、总身价
球员相关:球员数据、球员能力、球员水平、能力雷达图、X球员怎么样、球员新闻、转会记录、身价、进球数、世界杯数据、世界杯参赛名单
排名相关:积分榜、射手榜、助攻榜、FIFA排名、排名榜、小组排名、出线情况、X队排名、排行榜、过人榜、金靴
预测分析相关:比赛预测、X能赢吗、盘口、赔率、胜率预测、历史交锋、美国 vs 巴拉圭、巴西 vs 摩洛哥
竞彩玩法相关:竞彩、竞彩赔率、体彩、让球、让几个球、胜平负赔率、比分赔率、总进球赔率、混合过关、大小球、买球、买彩票、过关、单关、博彩、赌球、中国体彩、sporttery
四大每日高价值工作流(+ 体彩竞彩 E)
工作流 A:每日比赛前瞻(推荐每日使用)
场景:用户问「今天/明天有哪些比赛?怎么看?」
执行链:
# 1. 今日赛程概览
node scripts/worldcup-schedule.js today
# 2. 明日赛程
node scripts/worldcup-schedule.js tomorrow
# 3. 单场深度分析(传入 matchId)
node scripts/worldcup-match.js analysis <matchId>
node scripts/worldcup-match.js odds <matchId>analysis 返回字段:
prediction.homeWinRate/awayWinRate- AI预测胜率prediction.similarOddsHistory- 相似赔率历史胜率intelligence- 双方有利/不利情报records- 历史交锋战绩guess- 网友投票分布
odds 返回字段:
bookmakers[].datas[].initial/now- 初盘/即时盘- 包含亚盘(让球)、大小球、胜平负多种盘口
用户追问:
- "这场谁更可能赢" →
prediction.homeWinRate: "40%" - "有什么看点" →
intelligence双方近况 - "盘口怎么走" →
odds博彩公司赔率变化 - "历史交锋谁赢得多" →
recordsH2H 战绩
---
工作流 B:比赛战报回顾(赛后使用)
场景:用户问「昨晚那场比赛怎么样了?」
执行链:
# 1. 找比赛
node scripts/worldcup-schedule.js date 2026-06-13
# 2. 技术统计
node scripts/worldcup-match.js stats <matchId>
# 3. 赛况事件流
node scripts/worldcup-match.js live <matchId>用户追问:
- "谁进球了" → 从
live.incidents过滤goalType: "进球" - "红黄牌有哪些" → 从
live.incidents过滤goalType: "黄牌"/"红牌" - "换了几个人" → 从
live.incidents过滤goalType: "换人" - "全场最佳" → 从
live.narrative文字播报判断
---
工作流 C:积分榜与出线形势(每日更新)
场景:用户问「现在各组什么情况?谁能出线?」
执行:
node scripts/worldcup-rankings.js standings字段解读:
isQualified === true(蓝色背景)→ 已晋级isRelegated === true(红色背景)→ 已淘汰status字段如"晋级32强"、"晋级待定"等
用户追问:
- "A组还有希望吗" → 查该组积分差距
- "巴西能出线吗" → 查C组巴西的
isQualified - "第三名能晋级吗" → 规则是8支小组第3晋级
---
工作流 D:球员表现追踪(射手榜/球员榜)
场景:用户问「现在谁进球最多?明星球员表现如何?」
执行链:
# 1. 射手榜
node scripts/worldcup-rankings.js players 进球 10
# 2. 球员排行榜(30+维度)
node scripts/worldcup-rankings.js players 助攻
node scripts/worldcup-rankings.js players 过人
node scripts/worldcup-rankings.js players 射门
# 3. 明星球员详情
node scripts/worldcup-player.js info <playerId>
node scripts/worldcup-player.js news <playerId>用户追问:
- "姆巴佩进了几个球" → 查球员榜,按名字搜索
- "中场谁助攻最多" →
players 助攻 - "这个球员什么水平" →
ability.radarDims能力评分 - "内马尔转会过几次" →
transfer.list5次转会记录
竞彩赔率查询工作流见 workflows.md 工作流 E。
注意事项
- 数据从百度体育实时抓取,进行中比赛比分延迟约 1-2 分钟
- 赛程页默认展示约 4 天窗口,更早赛程需按日期循环查询
- 球队配置(teams.json)为静态,仅 2026 年赛事
- 球队/球员名仅支持中文(如"巴西"),不支持英文
worldcup-team.js支持中文球队名自动解析为 teamId- 比赛时间均为北京时间(UTC+8)
- 球员排行榜目前仅射手榜有数据,其他分类待赛事进行后丰富
worldcup-calculator.js数据源为中国体彩官方(sporttery.cn),需用移动端 User-Agent 调用;脚本内置 30 个 UA 池每次随机选一;体彩 matchId 与百度体育 matchId 不通用;只展示可投注的赛事
作者
- 爱海贼的无处不在
- 微信公众号:无处不在的技术
{
"tournament": "2026年国际足联世界杯",
"tournamentEn": "2026 FIFA World Cup",
"season": 2026,
"hostCountries": ["美国", "加拿大", "墨西哥"],
"matchUrl": "https://tiyu.baidu.com/al/match?match=%E4%B8%96%E7%95%8C%E6%9D%AF",
"rankingsUrl": "https://tiyu.baidu.com/al/match?match=%E4%B8%96%E7%95%8C%E6%9D%AF&tab=%E6%8E%92%E5%90%8D",
"totalTeams": 48,
"totalGroups": 12,
"teamsPerGroup": 4,
"pot": {
"pot1": ["美国", "加拿大", "墨西哥", "西班牙", "阿根廷", "法国", "英格兰", "巴西", "葡萄牙", "荷兰", "比利时", "德国"],
"pot2": ["克罗地亚", "摩洛哥", "哥伦比亚", "乌拉圭", "瑞士", "日本", "塞内加尔", "伊朗", "韩国", "厄瓜多尔", "奥地利", "澳大利亚"],
"pot3": ["挪威", "巴拿马", "埃及", "阿尔及利亚", "苏格兰", "巴拉圭", "突尼斯", "科特迪瓦", "乌兹别克斯坦", "卡塔尔", "沙特阿拉伯", "南非"],
"pot4": ["约旦", "佛得角", "加纳", "库拉索", "海地", "新西兰", "波黑", "瑞典", "土耳其", "捷克", "民主刚果", "伊拉克"]
},
"groups": {
"A": [
{ "position": 1, "teamName": "墨西哥", "teamId": "9c24b86859123a6a7218b5600e65680e", "pot": 1, "isHost": true, "qualifiedTop32": true },
{ "position": 2, "teamName": "韩国", "teamId": "e350aa265bc58bf922eb6ec5d3b705f3", "pot": 2, "qualifiedTop32": true },
{ "position": 3, "teamName": "捷克", "teamId": "bd0c6be1d4f081864fd88755b05c4f49", "pot": 4, "qualifiedTop32": false },
{ "position": 4, "teamName": "南非", "teamId": "dc7f9fdc795728d06adc20867070e73b", "pot": 3, "qualifiedTop32": false }
],
"B": [
{ "position": 1, "teamName": "加拿大", "teamId": "35779c25e9dec553f154e9d6286925e6", "pot": 1, "isHost": true, "qualifiedTop32": true },
{ "position": 2, "teamName": "波黑", "teamId": "ef7a8c508595fd9083ec6e7ee93820ba", "pot": 4, "qualifiedTop32": true },
{ "position": 3, "teamName": "卡塔尔", "teamId": "1d55f57a5cdc344f0944d2fba5e82f0a", "pot": 3, "qualifiedTop32": false },
{ "position": 4, "teamName": "瑞士", "teamId": "1692195abf8c7835f4212113975b49a6", "pot": 2, "qualifiedTop32": false }
],
"C": [
{ "position": 1, "teamName": "巴西", "teamId": "b92535372efd0c6bc7df7b84b9c7b577", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "摩洛哥", "teamId": "b16a9fdf450c58c762b49202c4d13efe", "pot": 2, "qualifiedTop32": true },
{ "position": 3, "teamName": "海地", "teamId": "a57cdc7e5410f79bf70d8636b03a3985", "pot": 4, "qualifiedTop32": false },
{ "position": 4, "teamName": "苏格兰", "teamId": "bd630ae14342fe2fa1b7376b8c467a36", "pot": 3, "qualifiedTop32": false }
],
"D": [
{ "position": 1, "teamName": "美国", "teamId": "a09c0a1b5f939e899c2c90239c3061f2", "pot": 1, "isHost": true, "qualifiedTop32": true },
{ "position": 2, "teamName": "巴拉圭", "teamId": "13aeb5d36078e7d9a8b2d8ea3c63cd84", "pot": 3, "qualifiedTop32": true },
{ "position": 3, "teamName": "澳大利亚", "teamId": "ad002317be38a25026110feccea14ed1", "pot": 2, "qualifiedTop32": false },
{ "position": 4, "teamName": "土耳其", "teamId": "11cf940dfd3e5766a4108da1968f8c65", "pot": 4, "qualifiedTop32": false }
],
"E": [
{ "position": 1, "teamName": "德国", "teamId": "d885ec68c7b46dbfd4a8f6e41d577ba0", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "库拉索", "teamId": "f304b118f59fd0087bde4807de418c56", "pot": 4, "qualifiedTop32": true },
{ "position": 3, "teamName": "科特迪瓦", "teamId": "122c4a5024a4a2395071b27ae3c96a9c", "pot": 3, "qualifiedTop32": false },
{ "position": 4, "teamName": "厄瓜多尔", "teamId": "e03d262a641690d6021146f8a1960c55", "pot": 2, "qualifiedTop32": false }
],
"F": [
{ "position": 1, "teamName": "荷兰", "teamId": "e37f589321ffb8ced53d4fb4de96e5ff", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "日本", "teamId": "017ebd53c335a35f3ff9c611be8cc5c8", "pot": 2, "qualifiedTop32": true },
{ "position": 3, "teamName": "瑞典", "teamId": "b2eb99748b34be973dbd98415cb0a3c7", "pot": 4, "qualifiedTop32": false },
{ "position": 4, "teamName": "突尼斯", "teamId": "18c8a8c7502910e966a80fb74b4274b5", "pot": 3, "qualifiedTop32": false }
],
"G": [
{ "position": 1, "teamName": "比利时", "teamId": "265243e3f2e8dd1261e98f372183240f", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "埃及", "teamId": "5482e10c7389489cb9f4c912ff822c04", "pot": 3, "qualifiedTop32": true },
{ "position": 3, "teamName": "伊朗", "teamId": "a57d0fd569253f6bfd9486dd6db9d484", "pot": 2, "qualifiedTop32": false },
{ "position": 4, "teamName": "新西兰", "teamId": "606320b6c012a17a7a96eba0a95b8e79", "pot": 4, "qualifiedTop32": false }
],
"H": [
{ "position": 1, "teamName": "西班牙", "teamId": "ca0f779e8db617a828a1c886d6beb530", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "佛得角", "teamId": "3cecc3c32bea4f6662753aaedd32cf90", "pot": 4, "qualifiedTop32": true },
{ "position": 3, "teamName": "沙特阿拉伯", "teamId": "71193d502f1a0147c8d13f3f78eb1729", "pot": 3, "qualifiedTop32": false },
{ "position": 4, "teamName": "乌拉圭", "teamId": "7582d690648eb69085503eb2a08b5b39", "pot": 2, "qualifiedTop32": false }
],
"I": [
{ "position": 1, "teamName": "法国", "teamId": "396c35d0620914f054cdb72e46a5f174", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "塞内加尔", "teamId": "023fb7a8de59b54f288308c180e292fa", "pot": 2, "qualifiedTop32": true },
{ "position": 3, "teamName": "伊拉克", "teamId": "e44c88e683e0aa9a3c500dc75033dad2", "pot": 4, "qualifiedTop32": false },
{ "position": 4, "teamName": "挪威", "teamId": "71738605c250e55a12624947da9bd247", "pot": 3, "qualifiedTop32": false }
],
"J": [
{ "position": 1, "teamName": "阿根廷", "teamId": "3d9f26830ac65f2297cc3c1d20487154", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "阿尔及利亚", "teamId": "36ad997d444aae1f0166dfdcb777493a", "pot": 3, "qualifiedTop32": true },
{ "position": 3, "teamName": "奥地利", "teamId": "87d2f6f08a43085d31f09472e19edee6", "pot": 2, "qualifiedTop32": false },
{ "position": 4, "teamName": "约旦", "teamId": "eb9a7c8d47d75b02bb4156c4f87f17a7", "pot": 4, "qualifiedTop32": false }
],
"K": [
{ "position": 1, "teamName": "葡萄牙", "teamId": "cd3b2deb45caa7373a08831e7de0f5ac", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "刚果民主共和国", "teamId": "92a326ca035e2eb1a7af17e3e70ffafd", "pot": 4, "qualifiedTop32": true },
{ "position": 3, "teamName": "乌兹别克斯坦", "teamId": "1291c7890d5108afd39df6db09c7d85a", "pot": 3, "qualifiedTop32": false },
{ "position": 4, "teamName": "哥伦比亚", "teamId": "c1d483bf3bbaf3040980088edde8560f", "pot": 2, "qualifiedTop32": false }
],
"L": [
{ "position": 1, "teamName": "英格兰", "teamId": "b78be5f1faa30defec36cef442b11431", "pot": 1, "qualifiedTop32": true },
{ "position": 2, "teamName": "克罗地亚", "teamId": "785dab5ff819d718c38cbb2a0b877a69", "pot": 2, "qualifiedTop32": true },
{ "position": 3, "teamName": "加纳", "teamId": "e00c50535a28cf7807621db0085a8382", "pot": 4, "qualifiedTop32": false },
{ "position": 4, "teamName": "巴拿马", "teamId": "90a5daeba664af01a6f9aeebe3491178", "pot": 3, "qualifiedTop32": false }
]
}
}竞彩足球计算器
脚本:scripts/worldcup-calculator.js
从中国体育彩票竞彩足球计算器抓取官方竞彩玩法赔率数据。包含胜平负、让球胜平负、比分、总进球、混合过关、大小球等竞彩玩法。
数据源:https://webapi.sporttery.cn/gateway/uniform/football/getMatchCalculatorV1.qry
注意:该 API 需使用移动端 User-Agent 调用,否则会被 Tencent Cloud EdgeOne 拦截(返回 567 错误)。脚本内置了 30 个真实移动设备 User-Agent(iOS 14-17 + Android 10-14),每次请求随机选择一个,降低被识别为爬虫的风险。
命令
node scripts/worldcup-calculator.js # 列出所有比赛(所有玩法)
node scripts/worldcup-calculator.js had # 只显示胜平负
node scripts/worldcup-calculator.js hhad # 只显示让球胜平负
node scripts/worldcup-calculator.js crs # 只显示比分
node scripts/worldcup-calculator.js ttg # 只显示总进球
node scripts/worldcup-calculator.js hafu # 只显示混合过关
node scripts/worldcup-calculator.js hilo # 只显示大小球
node scripts/worldcup-calculator.js summary # 精简模式(仅显示主要玩法)
node scripts/worldcup-calculator.js history <matchId> had # 获取赔率变化历史
node scripts/worldcup-calculator.js help # 显示帮助选项
| 选项 | 说明 |
|---|---|
--json | 输出 JSON 格式(便于程序化处理) |
--wc | 只显示世界杯比赛 |
--team <名称> | 按球队名过滤(模糊匹配主客队) |
--date <YYYY-MM-DD> | 按日期过滤 |
--json --wc | 组合使用:只输出世界杯比赛的 JSON |
玩法(poolCode)说明
体彩官方计算器当前提供 5 种玩法(不同比赛可能不全):
| poolCode | 中文名 | 说明 |
|---|---|---|
had | 胜平负 | 90 分钟内(含补时)主胜/平/客胜 |
hhad | 让球胜平负 | 主队让球后的胜平负 |
crs | 比分 | 猜全场比分(含 0:0 / 1:0 / 2:1 等 31 种) |
ttg | 总进球 | 猜全场总进球数(0/1/2/3/4/5/6/7+) |
hafu | 混合过关 | 半场+全场组合(9 种) |
注:脚本中保留了对hilo(大小球)、wnm(胜其他)、mnl(胜负)的支持,但当前 API 不返回这些数据。
趋势符号
赔率变化通过 homeTrend / drawTrend / awayTrend 等字段表示:
1= ↑ 上升-1= ↓ 下降0或空 = 不变
数据结构
比赛基本信息
| 字段 | 类型 | 说明 |
|---|---|---|
matchId | number | 比赛 ID(唯一标识,体彩平台 ID) |
matchNum | string | 编号(如"周六005") |
league | string | 赛事名(世界杯/英超/芬超等) |
homeTeam | string | 主队全名 |
awayTeam | string | 客队全名 |
homeRank | string | 主队排名(如"[C组1]",世界杯专用) |
awayRank | string | 客队排名 |
date | string | 比赛日期(YYYY-MM-DD) |
time | string | 开赛时间(HH:mm:ss) |
status | string | 销售状态(Selling 销售中) |
pools | array | 该比赛的所有玩法赔率 |
玩法数据结构
had / hhad(胜平负 / 让球胜平负)
| 字段 | 类型 | 说明 |
|---|---|---|
poolCode | string | 玩法代码 |
name | string | 玩法中文名 |
homeWin | string | 主胜赔率 |
draw | string | 平局赔率(mnl 无此字段) |
awayWin | string | 客胜赔率 |
goalLine | string | 让球数(仅 hhad 有,如"+1"、"-1") |
homeTrend | string | 主胜赔率变化 |
drawTrend | string | 平局赔率变化 |
awayTrend | string | 客胜赔率变化 |
single | number | 是否可单关(0=否, 1=是) |
cbt | number | 是否已截止(0=未截止, 1=已截止) |
crs(比分)
| 字段 | 类型 | 说明 |
|---|---|---|
scores | object | 各比分赔率,键为比分标签 |
trends | object | 对应比分的赔率变化 |
比分标签(共 31 种):
- 客胜(13 种):
0:1,0:2,1:2,0:3,1:3,2:3,0:4,1:4,2:4,0:5,1:5,2:5,客胜其他 - 平局(5 种):
0:0,1:1,2:2,3:3,平其他 - 主胜(13 种):
1:0,2:0,2:1,3:0,3:1,3:2,4:0,4:1,4:2,5:0,5:1,5:2,主胜其他
注:API 原始字段命名是s[主][客]格式(如s01s00= 1:0),脚本已转换为标签形式。
ttg(总进球)
| 字段 | 类型 | 说明 |
|---|---|---|
goals | object | 各进球数赔率,键为 0/1/2/.../7+ |
hafu(混合过关)
半场+全场 9 种组合:
| 标签 | 含义 |
|---|---|
胜胜 | 半场主胜 + 全场主胜 |
胜平 | 半场主胜 + 全场平 |
胜负 | 半场主胜 + 全场客胜 |
平胜 | 半场平 + 全场主胜 |
平平 | 半场平 + 全场平 |
平负 | 半场平 + 全场客 |
负胜 | 半场客胜 + 全场主胜 |
负平 | 半场客胜 + 全场平 |
负负 | 半场客胜 + 全场客胜 |
hilo(大小球)
| 字段 | 类型 | 说明 |
|---|---|---|
goalLine | string | 大小分线(如"2.5") |
over | string | 大分赔率 |
under | string | 小分赔率 |
overTrend | string | 大分赔率变化 |
underTrend | string | 小分赔率变化 |
赔率变化历史(history 命令)
| 字段 | 类型 | 说明 |
|---|---|---|
h / d / a | string | 主/平/客赔率(hilo 为 over/under) |
hf / df / af | string | 对应赔率变化(1=↑, -1=↓, 0=不变) |
updateDate | string | 更新日期 |
updateTime | string | 更新时间 |
典型使用场景
场景 1:查看世界杯所有比赛胜平负赔率
node scripts/worldcup-calculator.js had --wc场景 2:精简模式浏览世界杯所有比赛
node scripts/worldcup-calculator.js summary --wc每场只显示胜平负、让球胜平负、大小球 3 个主流玩法,快速浏览。
场景 3:查看某支球队的所有比赛
node scripts/worldcup-calculator.js summary --wc --team 巴西场景 4:查看某日所有世界杯比赛
node scripts/worldcup-calculator.js summary --wc --date 2026-06-14场景 5:查看某场比赛的比分赔率
node scripts/worldcup-calculator.js crs --wc --json | jq '.matches[0]'场景 6:跟踪赔率变化
node scripts/worldcup-calculator.js history 2040166 had场景 7:批量获取所有世界杯比赛数据(程序化处理)
node scripts/worldcup-calculator.js --wc --json > wc_odds.json与百度体育赔率(worldcup-match odds)的区别
| 数据源 | 覆盖玩法 | 数据特点 |
|---|---|---|
| 百度体育(worldcup-match) | 亚盘、欧赔、大小球 | 多家博彩公司,胜率分析 |
| 体彩竞彩(calculator) | 8 种官方竞彩玩法 | 中国体彩官方赔率,含让球数 |
两个数据源互补:百度体育看盘口走势,体彩看官方玩法。
注意事项
- API 需用移动端 User-Agent,否则会被 Tencent Cloud EdgeOne 拦截(返回 567 错误)。脚本内置 30 个 UA 池(iOS 14-17 + Android 10-14),每次请求随机选择
- 数据实时性:赔率更新延迟约 1-2 分钟
- 赔率变化历史仅保留近期数据,赛前几小时的数据可能为空
- 比分赔率有 31 种,混合过关 9 种,组合计算时注意边界条件
- matchId 与百度体育的 matchId 不通用(不同平台 ID 体系)
- 体彩计算器只展示可投注的赛事,已结束的比赛不会显示
- 日期筛选用的是
--date YYYY-MM-DD,与百度体育脚本的date命令不同 - 高频请求仍可能被风控,建议间隔 1-2 秒
比赛详情
脚本:scripts/worldcup-match.js
从百度体育比赛详情页(https://tiyu.baidu.com/al/live/detail?matchId=...&tab=...)抓取 6 个 tab 的数据。
命令
node scripts/worldcup-match.js info <matchId> # 比赛基本信息
node scripts/worldcup-match.js analysis <matchId> # 分析数据
node scripts/worldcup-match.js lineup <matchId> # 阵容数据
node scripts/worldcup-match.js live <matchId> # 赛况数据
node scripts/worldcup-match.js stats <matchId> # 统计数据
node scripts/worldcup-match.js odds <matchId> # 指数数据
node scripts/worldcup-match.js detail <matchId> [tab] # 完整详情(默认分析)各 tab 数据说明
info - 比赛基本信息(所有比赛)
| 字段 | 类型 | 说明 |
|---|---|---|
| game | string | 赛事名称 |
| matchDesc | string | 完整阶段描述(如 世界杯小组赛B组第1轮) |
| matchStage | string | 阶段简称 |
| dateFormat | string | 友好日期(明天 03:00) |
| time | string | 北京时间 HH:mm |
| timestamp | number | UTC 时间戳 |
| startTime | string | ISO 8601 时间 |
| matchStatusText | string | 状态文案 |
| vs | string | 比分字符串 |
| homeTeam / awayTeam | object | 主/客队信息(name/score/logo/rank/teamId) |
| liveList | array | 直播源列表 |
进行中的比赛页面用 headLive 字段,脚本已自动兼容。analysis - 分析数据
| 字段 | 类型 | 说明 |
|---|---|---|
| intelligence[].title | string | "有利情报" / "不利情报" |
| intelligence[].homeTeamPoints | array | 主队相关情报点 |
| intelligence[].awayTeamPoints | array | 客队相关情报点 |
| records[] | array | 历史战绩(H2H+双方近况) |
| ├─ title | string | "历史战绩"/"卡塔尔近期战绩"等 |
| ├─ probability | array | 胜率/赢盘率/大球率 |
| └─ matches | array | 近期比赛列表 |
| prediction.sampleSize | string | 相似赔率历史样本数 |
| prediction.homeWinRate | string | 主队本场胜率 |
| prediction.awayWinRate | string | 客队本场胜率 |
| prediction.similarOddsHistory | object | 相似赔率下历史胜负平比例 |
| guess.options | array | 猜胜负各选项的人数和占比 |
lineup - 阵容数据(开赛前确认)
| 字段 | 类型 | 说明 |
|---|---|---|
| confirmed | boolean | 阵容是否已确认 |
| court | string | 比赛场地 |
| referee | string | 主裁判 |
| home / away | object | 主/客队阵容信息 |
| ├─ formation | string | 阵型(如 4-1-2-3) |
| ├─ starter[] | array | 首发球员(name/number/position/coordinate/nation/playerId) |
| └─ substitute[] | array | 替补球员 |
live - 赛况数据(已结束比赛)
| 字段 | 类型 | 说明 |
|---|---|---|
| venue | object | 比赛场地(name/city/capacity) |
| narrative[] | array | 文字播报列表(约 51 条赛前+赛中描述) |
| incidents[] | array | 比赛事件(type/text/passedTime/team/direction) |
| events | object | 时间轴事件 |
stats - 统计数据(已结束比赛)
| 字段 | 类型 | 说明 |
|---|---|---|
| items[] | array | 10项技术统计 |
| ├─ name | string | 进球/控球率/射正/角球/黄牌/红牌等 |
| ├─ home | number/string | 主队数值 |
| └─ away | number/string | 客队数值 |
odds - 指数数据(所有比赛)
| 字段 | 类型 | 说明 |
|---|---|---|
| asyncTime | number | 赔率轮询间隔(毫秒) |
| bookmakers[] | array | 博彩公司组(约 4 家) |
| └─ datas[] | array | 盘口数据(initial/now/history) |
模块导出
const {
fetchMatchDetail,
parseHeader, parseAnalysis, parseLineup, parseLive, parseStats, parseOdds
} = require('./scripts/worldcup-match.js');赛事基本信息
基本信息
- 赛事名称:2026年国际足联世界杯(美加墨世界杯)
- 比赛时间:北京时间2026年6月12日至7月20日
- 主办国家:美国(11城)、加拿大(2城)、墨西哥(3城)
- 参赛球队:48支
- 分组数量:12组(A-L),每组4队
- 晋级规则:每组前2名 + 8支成绩最好的小组第3名晋级32强
主办城市
美国(11个):亚特兰大、波士顿、达拉斯、休斯顿、堪萨斯城、洛杉矶、迈阿密、纽约/新泽西、费城、旧金山湾区、西雅图
加拿大(2个):多伦多、温哥华
墨西哥(3个):瓜达拉哈拉、墨西哥城、蒙特雷
48支球队分组一览
| 组别 | 球队 |
|---|---|
| A组 | 墨西哥、韩国、捷克、南非 |
| B组 | 加拿大、波黑、卡塔尔、瑞士 |
| C组 | 巴西、摩洛哥、海地、苏格兰 |
| D组 | 美国、巴拉圭、澳大利亚、土耳其 |
| E组 | 德国、库拉索、科特迪瓦、厄瓜多尔 |
| F组 | 荷兰、日本、瑞典、突尼斯 |
| G组 | 比利时、埃及、伊朗、新西兰 |
| H组 | 西班牙、佛得角、沙特阿拉伯、乌拉圭 |
| I组 | 法国、塞内加尔、伊拉克、挪威 |
| J组 | 阿根廷、阿尔及利亚、奥地利、约旦 |
| K组 | 葡萄牙、刚果民主共和国、乌兹别克斯坦、哥伦比亚 |
| L组 | 英格兰、克罗地亚、加纳、巴拿马 |
4档分档
- 第一档(12队):美国、加拿大、墨西哥、西班牙、阿根廷、法国、英格兰、巴西、葡萄牙、荷兰、比利时、德国
- 第二档(12队):克罗地亚、摩洛哥、哥伦比亚、乌拉圭、瑞士、日本、塞内加尔、伊朗、韩国、厄瓜多尔、奥地利、澳大利亚
- 第三档(12队):挪威、巴拿马、埃及、阿尔及利亚、苏格兰、巴拉圭、突尼斯、科特迪瓦、乌兹别克斯坦、卡塔尔、沙特阿拉伯、南非
- 第四档(12队):约旦、佛得角、加纳、库拉索、海地、新西兰、波黑、瑞典、土耳其、捷克、民主刚果、伊拉克
数据来源
- 百度体育:https://tiyu.baidu.com
- 球队配置数据为静态配置(仅 2026 年赛事)
- 实时数据(赛程、比赛详情、球队数据)从网页实时抓取
注意事项
- 数据从百度体育网页实时抓取,可能存在短暂延迟
- 赛程页面默认展示当前可见时间窗口内的比赛(约 4 天)
- 比赛进行中比分/赛况约 1-2 分钟延迟
- 球队 ID 可用于构建详情页 URL:
- 球队主页:
https://tiyu.baidu.com/al/team?id=<teamId> - 球队数据:
https://tiyu.baidu.com/al/team?id=<teamId>&tab=数据&season=2026&scope=小组赛
球员详情
脚本:scripts/worldcup-player.js
从百度体育球员页(https://tiyu.baidu.com/al/player?id=<playerId>&tab=...)抓取 5 个 tab 的数据。
命令
node scripts/worldcup-player.js info <playerId> # 球员资料
node scripts/worldcup-player.js news <playerId> # 球员动态
node scripts/worldcup-player.js stats <playerId> [赛季] [赛事]
node scripts/worldcup-player.js schedule <playerId> # 球员赛程
node scripts/worldcup-player.js detail <playerId> [tab] # 完整详情(默认资料)playerId 可从球队阵容的 playerId 字段获取。
各 tab 数据说明
info - 球员资料
| 字段 | 类型 | 说明 |
|---|---|---|
| wiki.num | number | 球衣号码 |
| wiki.nickName | string | 英文名 |
| wiki.height / weight | string | 身高/体重 |
| wiki.detail.position | string | 位置(前锋/中场等) |
| wiki.detail.heavyFoot | string | 惯用脚(左/右/左右脚) |
| wiki.detail.age | number | 年龄 |
| wiki.detail.national | string | 国家队 |
| wiki.detail.expiryDate | string | 合同到期日 |
| wiki.detail.socialStatus | string | 身价数值 |
| wiki.detail.socialStatusUnit | string | 身价单位(万欧) |
| wiki.list | array | 详细资料列表 |
| ability.overall | string | 综合评分 |
| ability.overallColor | string | 评分颜色 |
| ability.radarDims | array | 6维能力雷达(速度/射门/传球/盘带/防守/身体) |
| ├─ name | string | 维度名 |
| ├─ value | string | 评分值 |
| └─ level | number | 等级(1-5) |
| honor[] | array | 荣誉列表(按赛事分组) |
| honorRecords[] | array | 荣誉记录(赛事名/次数/赛季) |
| transfer.unit | string | 转会费单位 |
| transfer.list[] | array | 转会历史(date/outTeam/team/price) |
| market | object | 身价曲线数据 |
news - 球员动态
| 字段 | 类型 | 说明 |
|---|---|---|
| total | number | 动态总数 |
| newsList[].title | string | 新闻标题 |
| newsList[].link | string | 详情链接 |
| newsList[].factorTime | number | 发布时间戳 |
| newsList[].source | string | 来源媒体 |
| newsList[].img | string | 配图URL |
| newsList[].nid | string | 新闻ID |
stats - 球员数据
| 字段 | 类型 | 说明 |
|---|---|---|
| seasonStats[] | array | 按赛事+赛季组织 |
| ├─ competition | string | 赛事名称 |
| ├─ season | string | 赛季 |
| ├─ rank | object | 球员在赛事中的排名 |
| └─ statGroups[] | array | 5 大类统计 |
| ├─ group | string | "统计"/"进攻"/"组织"/"防守"/"纪律" |
| └─ items[] | array | 统计项 |
常用查询:
# 世界杯数据
node scripts/worldcup-player.js stats 7bf18ff5b176d56a734dd90598461b2b 2026 世界杯
# 五大联赛数据
node scripts/worldcup-player.js stats <playerId> 2025-2026 意甲
node scripts/worldcup-player.js stats <playerId> 2025-2026 英超schedule - 球员赛程
| 字段 | 类型 | 说明 |
|---|---|---|
| subTabs | array | 可用视角(国家队/俱乐部) |
| teams | object | 按 teamId 分组 |
| ├─ title | string | "德国"/"阿森纳"等 |
| ├─ teamId | string | 球队ID |
| └─ matches[] | array | 出场记录 |
模块导出
const {
fetchPlayerData,
parseSchedule, parseRatings, parseNews, parseInfo, parseStats
} = require('./scripts/worldcup-player.js');排名数据
脚本:scripts/worldcup-rankings.js
从百度体育赛事主页(https://tiyu.baidu.com/al/match?match=世界杯&tab=...)抓取 4 个排名 tab。
4 个 tab 对应命令
| Tab | 命令 | 数据来源 API |
|---|---|---|
| 排名 | standings | go/api/header?rankChildTab=teamRank |
| 球队榜 | team-rank | (复用 standings 数据) |
| 球员榜 | players <分类> | go/api/header?rankChildTab=playerRank&tabId=N |
| FIFA排名 | fifa [N] | go/api/header?rankChildTab=fifaRank |
命令
node scripts/worldcup-rankings.js standings # 12组积分榜
node scripts/worldcup-rankings.js fifa 20 # FIFA 排名前20
node scripts/worldcup-rankings.js fifa # FIFA 排名前50
node scripts/worldcup-rankings.js players 进球 # 射手榜
node scripts/worldcup-rankings.js players 助攻 10 # 助攻榜前10
node scripts/worldcup-rankings.js team-rank # 球队榜(同积分榜)
node scripts/worldcup-rankings.js categories # 球员榜可用分类球员榜可用分类
进球 / 助攻 / 射门 / 射正
过人 / 过人成功 / 任意球 / 击中门框
快攻 / 快攻射门 / 丢失球权
解围 / 有效阻挡 / 拦截 / 抢断
1对1拼抢 / 1对1拼抢成功
拳击球 / 守门员出击 / 守门员出击成功 / 高空出击
传球 / 传球成功 / 关键传球
传中球 / 传中球成功 / 长传 / 成功长传 / 传球被断
红牌 / 黄牌 / 犯规 / 被侵犯 / 越位注:当前 API 对所有 tabId 返回相同的射手榜数据。赛事初期其它分类数据未产生。
数据字段
积分榜(standings)
| 字段 | 类型 | 说明 |
|---|---|---|
| updateTime | string | 更新时间 |
| columns | array | 列定义(球队名称/场次/胜平负/进失/积分) |
| groups[] | array | 12 个小组 |
| └─ list[] | array | 4 支球队(按名次排序) |
| ├─ teamId | string | teamId |
| ├─ teamName | string | 球队名称 |
| ├─ status | string | "晋级32强" / "晋级待定" |
| ├─ isQualified | boolean | 是否已晋级(蓝标) |
| ├─ isRelegated | boolean | 是否已淘汰(红标) |
| ├─ played | string | 已赛场次 |
| ├─ winDrawLoss | string | 胜/平/负(如 "1/0/0") |
| ├─ goals | string | 进/失(如 "2/0") |
| └─ points | string | 积分 |
FIFA 排名(fifa)
| 字段 | 类型 | 说明 |
|---|---|---|
| rankings[].rank | string | 当前排名 |
| rankings[].teamId | string | teamId |
| rankings[].teamName | string | 国家名 |
| rankings[].points | string | FIFA 积分 |
| rankings[].positionChanged | string | 排名变化(正数=上升,负数=下降) |
球员榜(players)
| 字段 | 类型 | 说明 |
|---|---|---|
| statsName | string | 榜单名(如"射手榜") |
| players[].rank | number | 排名 |
| players[].playerId | string | playerId |
| players[].playerName | string | 球员名 |
| players[].team | string | 球队名 |
| players[].position | string | 位置 |
| players[].score | string | 数值(进球数/助攻数等) |
| players[].penaltyValue | string | 点球数 |
模块导出
const {
fetchGroupStandings,
fetchFifaRankings,
fetchPlayerRankings,
fetchKnockoutBracket,
getPlayerTabNames
} = require('./scripts/worldcup-rankings.js');已知限制
- 球员榜 API 对所有 tabId 返回相同数据(赛事初期只有射手有数据)
- 球队榜 tab 的实际数据通过 12 组积分榜呈现
- 淘汰赛对阵图在淘汰赛比赛开打后才会激活
赛程数据
脚本:scripts/worldcup-schedule.js
从百度体育赛程页(https://tiyu.baidu.com/al/match?match=世界杯&tab=赛程)抓取比赛列表。
命令
列出全部赛程(当前可见窗口约 4 天)
node scripts/worldcup-schedule.js all按时间过滤
node scripts/worldcup-schedule.js today # 今天
node scripts/worldcup-schedule.js tomorrow # 明天
node scripts/worldcup-schedule.js date 2026-06-14
node scripts/worldcup-schedule.js date 06-15按小组/球队/阶段筛选
node scripts/worldcup-schedule.js group A # A组全部比赛
node scripts/worldcup-schedule.js team 巴西 # 巴西的赛程
node scripts/worldcup-schedule.js stage 小组赛 # 仅小组赛
node scripts/worldcup-schedule.js stage 淘汰赛 # 仅淘汰赛辅助命令
node scripts/worldcup-schedule.js dates # 可用日期列表
node scripts/worldcup-schedule.js stats # 统计(已结束/未开赛/进行中)数据字段
| 字段 | 类型 | 说明 |
|---|---|---|
| matchId | string | 比赛唯一ID(base64编码) |
| oriKey | string | 原始键 世界杯#2026-06-12#主队vs客队 |
| date | string | 比赛日期 YYYY-MM-DD |
| startTime | string | 开始时间 YYYY-MM-DD HH:mm:ss |
| time | string | 北京时间 HH:mm |
| stage | string | 比赛阶段(如 小组赛A组第1轮) |
| group | string | 所属小组 A-L |
| round | number | 小组赛轮次 |
| homeTeam | string | 主队名称 |
| awayTeam | string | 客队名称 |
| homeScore | string/number | 主队比分(未开赛时为 null) |
| awayScore | string/number | 客队比分 |
| scoreLine | string | 比分字符串(如 2-1) |
| status | string | 比赛状态(未开赛/进行中/已结束) |
| statusId | string | 状态ID(0/1/2) |
| dataSourceText | string | 数据来源(动画直播/比赛战报等) |
| resultDesc | string | 比赛结果描述(如战报标题) |
| winner | string | 获胜方 |
| hasLive | boolean | 是否有直播 |
| hot | number | 热度值 |
| detailUrl | string | 比赛详情URL |
matchId 编码规则
百度体育的 matchId 是 URL 安全的 base64 编码,格式:
世界杯_2026-06-12_墨西哥vs南非可通过 decodeMatchId(matchId) 解码。URL 中 matchId 参数会做 URL-encoding 二次编码。
已知限制
- 页面默认仅展示当前可见时间窗口(约 4 天,12 场比赛)
- 更早/更晚赛程需要分页加载(暂未接入)
- 完整 104 场比赛需循环
date <YYYY-MM-DD>命令
模块导出
const {
fetchAllSchedule,
filterByDate, filterByGroup, filterByTeam, filterByStage,
filterByDateKeyword,
getAvailableDates, getStats,
decodeMatchId
} = require('./scripts/worldcup-schedule.js');球队详情
脚本:scripts/worldcup-team.js
从百度体育球队页(https://tiyu.baidu.com/al/team?id=<teamId>&tab=...)抓取 6 个 tab 的数据。
命令
node scripts/worldcup-team.js lookup <球队名> # 通过 teams.json 查 teamId
node scripts/worldcup-team.js info <teamId> # 球队资料
node scripts/worldcup-team.js schedule <teamId> [赛事] # 赛程
node scripts/worldcup-team.js lineup <teamId> # 阵容
node scripts/worldcup-team.js history <teamId> # 历史成绩
node scripts/worldcup-team.js stats <teamId> <年> <赛事> # 数据统计赛事参数可选值:all(默认)/ 世界杯 / 国际友谊赛
赛事统计参数示例:2026 小组赛 / 2022 世界杯 / 2024 国际友谊赛
各 tab 数据说明
info - 球队资料
| 字段 | 类型 | 说明 |
|---|---|---|
| baseInfo.title | string | "基本资料" |
| baseInfo.items[] | array | 资料项列表 |
| ├─ name | string | 资料项名称(成立时间/世界排名/平均年龄/总身价/主教练) |
| ├─ content | string | 资料内容 |
| └─ link | string | 关联链接(如FIFA排名页) |
| honor.title | string | "荣耀" |
| honor.awards[] | array | 荣誉列表 |
| ├─ title | string | 奖项名称 |
| └─ years | array | 获奖年份列表 |
schedule - 球队赛程
数据结构按 competitions 分组(id: all/世界杯/国际友谊赛):
{
subTabs: ["全部赛程", "世界杯", "国际友谊赛"],
competitions: {
"世界杯": {
title: "世界杯",
index: 0,
matches: [...] // 与 worldcup-schedule.js 的字段相同
},
"国际友谊赛": { ... }
}
}lineup - 球队阵容
| 字段 | 类型 | 说明 |
|---|---|---|
| coaching[] | array | 教练组(7人左右) |
| ├─ name | string | 姓名 |
| ├─ subTitle | string | 职位/年龄/国籍 |
| └─ avatar | string | 头像URL |
| players[] | array | 球员按位置分组 |
| ├─ position | string | "前锋"/"中场"/"后卫"/"门将" |
| ├─ columnTitles | array | 列定义(field/name/type) |
| └─ players[] | array | 球员列表 |
球员字段:number/name/age/playerId/club/goals/court(出场)/assists/value(身价)
history - 历史成绩
| 字段 | 类型 | 说明 |
|---|---|---|
| records[] | array | 历届世界杯参赛记录 |
| ├─ season | string | 赛季(如 "2022年卡塔尔世界杯") |
| ├─ description | string | 成绩描述(如 "止步小组赛,3负") |
| ├─ groupMatches | array | 小组赛比分列表 |
| └─ url | string | 百科链接 |
stats - 球队数据
| 字段 | 类型 | 说明 |
|---|---|---|
| seasonStats[] | array | 按赛事+赛季组织 |
| ├─ competition | string | 赛事名称 |
| ├─ season | string | 赛季年份 |
| ├─ rank | object | 最终排名(win/deuce/loss/score/rank/rankArea) |
| └─ statGroups[] | array | 5 大类统计 |
| ├─ group | string | "统计"/"进攻"/"组织"/"防守"/"纪律" |
| └─ items[] | array | 统计项 |
| ├─ name | string | 统计项名 |
| ├─ value | string | 数值 |
| └─ rankingText + ranking | string | 联盟排名(如 "联盟第1") |
典型用法:
# 当前赛事数据
node scripts/worldcup-team.js stats 35779c25e9dec553f154e9d6286925e6 2026 世界杯
# 历史对比
node scripts/worldcup-team.js stats 35779c25e9dec553f154e9d6286925e6 2022 世界杯球队ID查找
通过 lookup 命令从静态的 teams.json 中查询:
node scripts/worldcup-team.js lookup 加拿大
# 返回: { teamName, teamId, group, pot, isHost, ... }模块导出
const {
fetchTeamData,
parseSchedule, parseLineup, parseInfo, parseHistory, parseStats
} = require('./scripts/worldcup-team.js');球队配置(静态数据)
48 支球队的分组配置保存在 data/teams.json,是其他脚本查找 teamId 的基础。
脚本:scripts/worldcup-teams.js
列出所有48支球队
node scripts/worldcup-teams.js list查询指定小组球队
node scripts/worldcup-teams.js group A
node scripts/worldcup-teams.js group L按名称查找球队(返回 teamId)
node scripts/worldcup-teams.js find 韩国
node scripts/worldcup-teams.js find 阿根廷查看赛事基本信息
node scripts/worldcup-teams.js info列出东道主球队
node scripts/worldcup-teams.js hosts列出指定档位的球队
node scripts/worldcup-teams.js pot 1
node scripts/worldcup-teams.js pot 4数据结构
teams.json 顶层字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| tournament | string | 赛事名称(中文) |
| tournamentEn | string | 赛事名称(英文) |
| season | number | 赛季年份 |
| hostCountries | array | 东道主国家列表 |
| totalTeams | number | 总参赛球队数(48) |
| totalGroups | number | 总分组数(12) |
| teamsPerGroup | number | 每组球队数(4) |
| matchUrl | string | 百度体育赛事主页URL |
| rankingsUrl | string | 百度体育排名页URL |
| pot | object | 4个档位的球队名列表 |
| groups | object | 12个小组的球队配置(A-L) |
球队对象字段(groups[].teams[]):
| 字段 | 类型 | 说明 |
|---|---|---|
| position | number | 组内位置(1-4) |
| teamName | string | 球队中文名称 |
| teamId | string | 百度体育球队ID |
| pot | number | 分档(1-4) |
| isHost | boolean | 是否东道主 |
| qualifiedTop32 | boolean | 是否晋级32强 |
使用示例:
const { loadTeams, findTeamByName } = require('./scripts/worldcup-teams.js');
// 列出全部球队
const data = loadTeams();
console.log(data.totalTeams); // 48
// 按名查找
const team = findTeamByName(data, '加拿大');
console.log(team.teamId); // 35779c25e9dec553f154e9d6286925e6更多详情见 references/overview.md。
跨脚本组合使用
各脚本通过 teamId 和 matchId 互相连接,下面是常用组合用法。
常用工作流
1. 球队名 → 球队赛程 → 比赛详情
const { findTeamByName, loadTeams } = require('./scripts/worldcup-teams.js');
const { fetchAllSchedule, filterByTeam } = require('./scripts/worldcup-schedule.js');
const { fetchMatchDetail, parseLineup } = require('./scripts/worldcup-match.js');
(async () => {
// 1. 查 teamId
const data = loadTeams();
const team = findTeamByName(data, '加拿大');
// 2. 查该球队的赛程
const schedule = await fetchAllSchedule();
const teamMatches = filterByTeam(schedule, team.teamName);
// 3. 选一场比赛查详情
const matchId = teamMatches[0].matchId;
const lineup = await fetchMatchDetail(matchId, '阵容');
console.log(lineup.tabData);
})();2. 今天比赛 → 找正在进行的 → 实时赛况
const { fetchAllSchedule, filterByDateKeyword } = require('./scripts/worldcup-schedule.js');
const { fetchMatchDetail } = require('./scripts/worldcup-match.js');
(async () => {
const all = await fetchAllSchedule();
const today = filterByDateKeyword(all, '今天');
const live = today.filter(m => m.statusId === '1');
for (const m of live) {
const detail = await fetchMatchDetail(m.matchId, '赛况');
console.log(`${m.homeTeam} vs ${m.awayTeam} - 比分 ${m.vs}`);
}
})();3. 球队阵容(team 阵容 + match 首发)
球队阵容(worldcup-team.js lineup)= 全部 26 人名单 比赛阵容(worldcup-match.js lineup)= 当场比赛首发 11 + 替补
// 查加拿大队的全部球员
const teamLineup = await fetchTeamData('35779c25e9dec553f154e9d6286925e6', '阵容');
// 查某场比赛的首发
const matchLineup = await fetchMatchDetail('5LiW55WM...', '阵容');
const starters = matchLineup.tabData.home.starter;4. 球队赛程 vs 比赛赛程
- 球队赛程(
worldcup-team.js schedule):包括世界杯+友谊赛+其他赛事 - 比赛赛程(
worldcup-schedule.js):仅世界杯赛程(带 subTab 切换)
// 查该球队所有赛事
const teamSchedule = await fetchTeamData(teamId, '赛程');
const allMatches = Object.values(teamSchedule.tabData.competitions).flatMap(c => c.matches);
// 仅查世界杯赛程(用于赛事内)
const worldCupOnly = teamSchedule.tabData.competitions['世界杯'].matches;matchId 编解码
const { decodeMatchId } = require('./scripts/worldcup-schedule.js');
const matchId = '5LiW55WM5p2vIzIwMjYtMDYtMTIj5aKo6KWsprit5ZOldnPljZfpnZ4=';
const decoded = decodeMatchId(matchId);
// 世界杯_2026-06-12_墨西哥vs南非团队配置反向查找
通过 teamId 找到球队在 teams.json 中的位置:
const { loadTeams } = require('./scripts/worldcup-teams.js');
const data = loadTeams();
for (const [group, teams] of Object.entries(data.groups)) {
for (const t of teams) {
if (t.teamId === '35779c25e9dec553f154e9d6286925e6') {
console.log(`${t.teamName} 在 ${group} 组,第 ${t.position} 位`);
}
}
}数据流概览
teams.json (静态)
│
▼ teamId
worldcup-team.js ────→ 球队资料/赛程/阵容/历史成绩/数据
│
│ teamName │ playerId
▼ ▼
worldcup-schedule.js ──→ matchId worldcup-player.js ──→ 球员资料/动态/数据/赛程
│
▼
worldcup-match.js ────→ 比赛分析/阵容/赛况/统计/指数球员数据流
球队阵容里的每个球员都有 playerId,可以联动到球员详情页:
// 1. 从球队阵容拿所有球员
const { fetchTeamData } = require('./scripts/worldcup-team.js');
const { fetchPlayerData } = require('./scripts/worldcup-player.js');
(async () => {
const team = await fetchTeamData('d885ec68c7b46dbfd4a8f6e41d577ba0', '阵容');
const players = team.tabData.players.flatMap(g => g.players);
// 2. 逐个查球员详情
for (const p of players.slice(0, 3)) {
const info = await fetchPlayerData(p.playerId, '资料');
console.log(`${p.name} - 评分:${info.tabData.ability.overall}`);
}
})();典型工作流
按用户问题分类,给出具体的脚本调用链。AI 助手可参考这些模式回答用户。
---
四大每日高价值工作流
以下工作流专为一站式获取每日世界杯比赛分析而设计。
---
工作流 A:每日比赛前瞻(推荐每日使用)
场景:用户问「今天/明天有哪些比赛?怎么看?」
一键获取今日全部比赛概览:
node scripts/worldcup-schedule.js today
node scripts/worldcup-schedule.js tomorrow单场比赛深度分析(传入 matchId):
node scripts/worldcup-match.js analysis <matchId>
node scripts/worldcup-match.js odds <matchId>实际数据示例(明天 2026-06-14 赛程):
[
{"time": "03:00", "homeTeam": "卡塔尔", "awayTeam": "瑞士", "stage": "B组第1轮", "hot": "21"},
{"time": "06:00", "homeTeam": "巴西", "awayTeam": "摩洛哥", "stage": "C组第1轮", "hot": "51"},
{"time": "09:00", "homeTeam": "海地", "awayTeam": "苏格兰", "stage": "C组第1轮", "hot": "11"},
{"time": "12:00", "homeTeam": "澳大利亚", "awayTeam": "土耳其", "stage": "D组第1轮", "hot": "21"}
]analysis 返回字段解读:
prediction.homeWinRate/prediction.awayWinRate- AI 预测胜率(如"40%" / "32%")prediction.similarOddsHistory- 相似赔率下历史胜率(如主胜 41%、平局 28%、客胜 31%)intelligence- 双方有利/不利情报(如"美国近5年主场对巴拉圭100%胜率")records- 历史交锋战绩(含胜率、赢盘率、大球率)guess- 网友投票分布(如 66.9% 看好美国)
odds 返回字段解读:
- 多家博彩公司赔率数据
- 包含亚盘(让球盘)、大小球、胜平负三种盘口
initial初盘 /now即时盘
用户追问:
- "这场谁更可能赢" → 从
prediction读取胜率 - "有什么看点" → 从
intelligence读取双方近况分析 - "能买吗/盘口怎么走" → 从
odds读取博彩公司赔率 - "两队历史交手谁赢得多" → 从
records读取 H2H 战绩
---
工作流 B:比赛战报回顾(赛后使用)
场景:用户问「昨晚那场比赛怎么样了?」
完整战报获取:
# 1. 找到比赛(按日期)
node scripts/worldcup-schedule.js date 2026-06-13
# 2. 技术统计
node scripts/worldcup-match.js stats <matchId>
# 3. 赛况事件流(进球/红黄牌/换人)
node scripts/worldcup-match.js live <matchId>
# 4. 阵容详情
node scripts/worldcup-match.js lineup <matchId>实际数据示例(美国 4-1 巴拉圭):
stats 技术统计:
{
"homeTeam": "美国", "awayTeam": "巴拉圭",
"items": [
{"name": "进球", "home": 4, "away": 1},
{"name": "控球率", "home": 65, "away": 35},
{"name": "射正", "home": 6, "away": 1},
{"name": "角球", "home": 3, "away": 1},
{"name": "黄牌", "home": 1, "away": 5}
]
}live 赛况事件流:
{
"venue": {"name": "洛杉矶体育场", "city": "洛杉矶", "capacity": "70240"},
"narrative": [
{"text": "8' - 第1个进球!博瓦迪利亚(美国 乌龙球)", "iconType": "进球"},
{"text": "31' - 第2个进球 - 巴洛贡(美国)", "iconType": "进球"},
{"text": "45+5' - 第3个进球 - 巴洛贡(美国)", "iconType": "进球"},
{"text": "74' - 第4个进球 - 毛里西奥(巴拉圭)", "iconType": "进球"},
{"text": "90+8' - 第5个进球 - 雷纳(美国)", "iconType": "进球"}
],
"incidents": [
{"goalType": "乌龙球", "passedTime": "7'", "team": "美国"},
{"goalType": "进球", "passedTime": "31'", "team": "美国"},
{"goalType": "进球", "passedTime": "45'", "team": "美国"},
{"goalType": "进球", "passedTime": "73'", "team": "巴拉圭"},
{"goalType": "进球", "passedTime": "90'", "team": "美国"}
]
}用户追问:
- "谁进球了" → 从
live.incidents过滤goalType: "进球" - "红黄牌有哪些" → 从
live.incidents过滤goalType: "黄牌"/"红牌" - "换了几个人" → 从
live.incidents过滤goalType: "换人" - "比赛场地在哪" → 从
venue读取城市和球场名称
---
工作流 C:积分榜与出线形势(每日更新)
场景:用户问「现在各组什么情况?谁能出线?」
一键获取全部12组积分榜:
node scripts/worldcup-rankings.js standings实际数据示例(D组当前状态):
{
"groups": [{
"list": [
{"teamName": "美国", "played": 1, "winDrawLoss": "1/0/0", "goals": "4/1", "points": 3, "isQualified": true},
{"teamName": "澳大利亚", "played": 0, "winDrawLoss": "0/0/0", "goals": "0/0", "points": 0, "isQualified": false},
{"teamName": "土耳其", "played": 0, "winDrawLoss": "0/0/0", "goals": "0/0", "points": 0, "isQualified": false},
{"teamName": "巴拉圭", "played": 1, "winDrawLoss": "0/0/1", "goals": "1/4", "points": 0, "isQualified": false}
]
}]
}字段解读:
isQualified === true→ 已晋级(蓝色背景 #456de6)isRelegated === true→ 已淘汰(红色背景 #ff5050)status字段如"晋级32强"、"晋级待定"goals格式为 "进/失"(如"4/1"表示进4球失1球)winDrawLoss格式为 "胜/平/负"
用户追问:
- "A组还有希望吗" → 查该组积分差距
- "巴西能出线吗" → 直接查巴西所在C组的
isQualified - "第三名能晋级吗" → 规则是8支小组第3晋级32强
---
工作流 D:球员表现追踪(射手榜/球员榜)
场景:用户问「现在谁进球最多?明星球员表现如何?」
射手榜:
node scripts/worldcup-rankings.js players 进球 10球员排行榜(30+维度):
node scripts/worldcup-rankings.js players 助攻
node scripts/worldcup-rankings.js players 过人
node scripts/worldcup-rankings.js players 射门
node scripts/worldcup-rankings.js players 关键传球
node scripts/worldcup-rankings.js categories # 查看全部支持分类players 返回示例:
{
"tabName": "进球",
"players": [
{"rank": 1, "playerName": "弗拉林·巴洛贡", "team": "美国", "position": "前锋", "score": "2"},
{"rank": 2, "playerName": "黄仁范", "team": "韩国", "position": "中场", "score": "1"}
]
}明星球员详情深挖:
node scripts/worldcup-player.js info <playerId>
node scripts/worldcup-player.js news <playerId>player info 返回示例(内马尔 playerId: 8b95bfb75b6abad75a141fbfee809950):
{
"wiki": {
"height": "175cm", "weight": "68kg",
"detail": {"position": "前锋", "age": "33岁", "national": "巴西"}
},
"ability": {
"overall": 82,
"radarDims": [
{"name": "速度", "value": 85}, {"name": "射门", "value": 86},
{"name": "传球", "value": 88}, {"name": "盘带", "value": 93}
]
},
"transfer": {
"list": [
{"date": "2023-08-15", "team": "利雅得新月", "price": "9000万欧"},
{"date": "2017-08-03", "team": "巴黎圣日耳曼", "price": "22200万欧"},
{"date": "2013-07-01", "team": "巴塞罗那", "price": "8800万欧"}
]
},
"market": {"axis": {"data": [[[{"x": 53, "y": 100}]]]}}, // 身价变化曲线
"honor": [
{"match": "南美年度足球先生", "seasons": [{"date": "2012"}]},
{"match": "欧冠冠军", "seasons": [{"date": "2014-2015"}]}
]
}用户追问:
- "姆巴佩进了几个球" → 查球员榜,按名字搜索
- "中场谁助攻最多" →
players 助攻 - "这个球员什么水平" → 从
ability.radarDims读取能力评分 - "内马尔转会过几次" → 从
transfer.list读取5次转会记录
---
工作流 E:竞彩赔率查询(体彩官方)
用户问:「巴西这场让几个球?/ 这场赔率多少?/ 比分赔率怎么看?/ 体彩赔率查询」
执行链:
# 1. 精简模式:浏览所有世界杯比赛的主流玩法
node scripts/worldcup-calculator.js summary --wc
# 2. 按玩法过滤
node scripts/worldcup-calculator.js had --wc # 胜平负
node scripts/worldcup-calculator.js hhad --wc # 让球胜平负
node scripts/worldcup-calculator.js crs --wc # 比分
node scripts/worldcup-calculator.js ttg --wc # 总进球
node scripts/worldcup-calculator.js hafu --wc # 混合过关
# 3. 按球队/日期过滤
node scripts/worldcup-calculator.js summary --wc --team 巴西
node scripts/worldcup-calculator.js summary --wc --date 2026-06-14
# 4. 赔率变化历史(matchId 来自 --json 输出)
node scripts/worldcup-calculator.js history <matchId> had实际数据示例(巴西 vs 摩洛哥):
周六006 巴西 vs 摩洛哥
2026-06-14 06:00:00
------------------------------------------------------------
胜平负 主胜:1.49 平:3.60 客胜:5.55
让球(-1) 主胜:2.72↑ 平:3.11↓ 客胜:2.27JSON 输出示例:
{
"matchId": 2040167,
"homeTeam": "巴西",
"awayTeam": "摩洛哥",
"pools": [
{
"poolCode": "had",
"homeWin": "1.49",
"draw": "3.60",
"awayWin": "5.55"
},
{
"poolCode": "hhad",
"goalLine": "-1",
"homeWin": "2.72",
"draw": "3.11",
"awayWin": "2.27"
}
]
}字段解读:
- 趋势符号:
↑赔率上升(庄家不看好该结果)/↓下降(庄家看好)/ 无 不变 goalLine字段:让球数(如"-1"表示主队让 1 球,"-3"表示让 3 球)- crs 共 31 种比分赔率(客胜 13 + 平 5 + 主胜 13)
与百度体育赔率(worldcup-match odds)的区别:
| 数据源 | 覆盖 | 特点 |
|---|---|---|
百度体育 worldcup-match.js odds | 亚盘/欧赔/大小球 | 多家博彩公司盘口 |
体彩 worldcup-calculator.js | 5 种官方竞彩玩法 | 中国体彩官方,含让球数 |
用户追问:
- "巴西这场让几个球" →
hhad.goalLine字段 - "主队胜率多少" → 1/1.49 ≈ 67%(隐含概率)
- "比分赔率最低的是哪个" → crs 中找最小值
- "这场的赔率在变吗" → 关注
homeTrend/drawTrend/awayTrend字段 - "赔率变化历史" →
history <matchId> <玩法>命令
---
扩展工作流
工作流 1:今日观赛指南
用户问:「今天有什么世界杯比赛?几点开赛?」
执行:
node scripts/worldcup-schedule.js today扩展场景:
- 用户问"今天最值得看哪场" → 按
hot字段排序(51最高为巴西 vs 摩洛哥) - 用户问"有直播吗" → 过滤
hasLive === true - 用户问"几点开球" → 从
time字段读取(如"03:00"、"06:00"、"09:00")
---
工作流 2:正在直播
用户问:「现在有什么比赛在直播?」
执行:
node scripts/worldcup-schedule.js today | jq '.[] | select(.statusId=="1")'扩展场景:
- 实时赛况:
node scripts/worldcup-match.js live <matchId> - 实时比分:
scoreLine字段(如"4-1")
---
工作流 3:球队下一场比赛
用户问:「巴西下一场几点打?对手是谁?」
执行链:
node scripts/worldcup-team.js lookup 巴西
node scripts/worldcup-team.js schedule <teamId> 世界杯巴西赛程示例:
[
{"date": "2026-06-14", "time": "06:00", "homeTeam": "巴西", "awayTeam": "摩洛哥"},
{"date": "2026-06-20", "time": "08:30", "homeTeam": "巴西", "awayTeam": "海地"},
{"date": "2026-06-25", "time": "06:00", "homeTeam": "苏格兰", "awayTeam": "巴西"}
]---
工作流 4:球队核心球员
用户问:「德国队谁最贵?有哪些明星球员?」
执行:
node scripts/worldcup-team.js lineup d885ec68c7b46dbfd4a8f6e41d577ba0扩展场景:
- 按身价排序:
value字段(如"1.2亿"、"800万") - 按位置分组:
position字段 - 教练组:
coaching字段
---
工作流 5:FIFA 排名查询
用户问:「中国/日本 FIFA 排名第几?」
执行:
node scripts/worldcup-rankings.js fifa 50实际数据示例(前10):
[
{"rank": 1, "teamName": "阿根廷", "points": 1877, "positionChanged": 2},
{"rank": 2, "teamName": "西班牙", "points": 1874, "positionChanged": 0},
{"rank": 3, "teamName": "法国", "points": 1870, "positionChanged": -2},
{"rank": 6, "teamName": "巴西", "points": 1765, "positionChanged": 0},
{"rank": 7, "teamName": "摩洛哥", "points": 1755, "positionChanged": 1}
]---
工作流 6:球队历史 vs 当前
用户问:「巴西队世界杯历史成绩怎么样?」
执行:
node scripts/worldcup-team.js history 巴西历史成绩示例:
{
"records": [
{"season": "2022年卡塔尔世界杯", "description": "止步八强,3胜1平1负"},
{"season": "2018年俄罗斯世界杯", "description": "止步八强,3胜1平1负"},
{"season": "2002年韩日世界杯", "description": "冠军,7战7胜"},
{"season": "1994年美国世界杯", "description": "冠军,5胜2平"}
]
}---
工作流 7:新闻/动态关注
用户问:「C罗最近有什么新闻?」
执行链:
node scripts/worldcup-team.js lookup 葡萄牙
node scripts/worldcup-team.js lineup <teamId>
node scripts/worldcup-player.js news <playerId>新闻返回示例:
{
"newsList": [
{"title": "世界杯折射的美国例外论", "source": "虎嗅APP", "endTime": "昨天18:21"},
{"title": "曾经的世界第三人,过去3年仅出战49场", "source": "篮球圈里的那些事儿", "endTime": "5月29日"}
]
}---
工作流 8:球队信息快速查询
用户问:「东道主是谁?有几支亚洲球队?」
执行:
node scripts/worldcup-teams.js hosts # 东道主(美国、加拿大、墨西哥)
node scripts/worldcup-teams.js list # 全部48队
node scripts/worldcup-teams.js pot 1 # 第一档强队---
工作流 9:赛程日期范围
用户问:「这周后半段还有什么比赛?」
执行:
node scripts/worldcup-schedule.js dates # 可用日期列表
node scripts/worldcup-schedule.js stats # 赛程统计stats 返回示例:
{"total": 16, "finished": 4, "pending": 12, "live": 0, "dateRange": {"from": "2026-06-12", "to": "2026-06-16"}}---
错误处理模式
未找到球队
→ 提示用户用 node worldcup-teams.js list 查看所有48支球队
赛程数据获取失败
→ 提示用户网络问题,请稍后重试
比赛未开赛无阵容
→ 提示用户"阵容尚未公布,开赛前2小时左右会更新"
比赛进行中
→ 提示用户"技术统计和赛况约1-2分钟延迟"
---
注意事项
- 数据延迟:实时数据 1-2 分钟延迟
- 静态数据:teams.json 不会自动更新
- matchId 编码:脚本已自动处理
- 球员/球队ID:跨脚本通用
- 比赛时间:均以北京时间(UTC+8)为准
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const DATA_FILE = path.join(__dirname, '..', '..', 'data', 'teams.json');
function loadTeams() {
return JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
}
function listAllTeams(data) {
const list = [];
for (const [group, teams] of Object.entries(data.groups)) {
for (const t of teams) {
list.push({ group, position: t.position, teamName: t.teamName, teamId: t.teamId, pot: t.pot, isHost: !!t.isHost, qualifiedTop32: !!t.qualifiedTop32 });
}
}
return list;
}
function findTeamByName(data, name) {
for (const [group, teams] of Object.entries(data.groups)) {
for (const t of teams) {
if (t.teamName === name || t.teamName.includes(name)) {
return { group, ...t };
}
}
}
return null;
}
function findTeamById(data, id) {
for (const [group, teams] of Object.entries(data.groups)) {
for (const t of teams) {
if (t.teamId === id) return { group, ...t };
}
}
return null;
}
function getGroup(data, groupKey) {
const key = groupKey.toUpperCase();
const teams = data.groups[key];
return teams ? { group: key, teams } : null;
}
const HEX_ID_RE = /^[a-f0-9]{20,}$/;
function isTeamId(input) {
return HEX_ID_RE.test(input);
}
function lookupTeam(input, data) {
if (isTeamId(input)) return findTeamById(data, input);
return findTeamByName(data, input);
}
function resolveTeamId(input, data) {
const team = lookupTeam(input, data);
return team ? team.teamId : null;
}
module.exports = { loadTeams, listAllTeams, findTeamByName, findTeamById, getGroup, lookupTeam, resolveTeamId, isTeamId };
/**
* 共享 User-Agent 池
* 30 个真实浏览器的 UA,避免固定 UA 触发反爬
*/
const USER_AGENTS = [
// Chrome (Windows)
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
// Chrome (macOS)
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
// Edge (Windows)
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0',
// Firefox (Windows)
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0',
// Firefox (macOS)
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0',
// Safari (macOS)
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',
// iPhone
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1',
// iPad
'Mozilla/5.0 (iPad; CPU OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPad; CPU OS 17_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1',
// Android Chrome
'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36',
// Android WebView
'Mozilla/5.0 (Linux; Android 13; Mi 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36',
// HarmonyOS Huawei Browser
'Mozilla/5.0 (Linux; Android 12; HarmonyOS; LNA-AL00; HMSCore 6.13.0.302) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 HuaweiBrowser/14.0.5.330 Mobile Safari/537.36'
];
/**
* 随机选择一个 UA
* @returns {string}
*/
function getRandomUserAgent() {
return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
}
module.exports = { USER_AGENTS, getRandomUserAgent };
#!/usr/bin/env node
const axios = require('axios');
const API_BASE = 'https://webapi.sporttery.cn/gateway/uniform';
const CHANNEL = 'mchannel';
// 30 个真实移动设备 User-Agent,每次请求随机选择一个
const USER_AGENTS = [
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
'Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.81 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 10; SM-N976B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 11; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 11; SM-A526B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 12; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 12; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.79 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; SM-A546B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'
];
function getRandomHeaders() {
return {
'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)],
'Referer': 'https://m.sporttery.cn/',
'Accept': 'application/json',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive'
};
}
const poolCodeMap = {
had: '胜平负',
hhad: '让球胜平负',
crs: '比分',
ttg: '总进球',
hafu: '混合过关',
hilo: '大小球',
wnm: '胜其他',
mnl: '胜负'
};
const availablePoolCodes = ['had', 'hhad', 'crs', 'ttg', 'hafu'];
const poolCodeHelp = Object.entries(poolCodeMap).map(([k, v]) => ` ${k} - ${v}`).join('\n');
function printUsage() {
console.log(`
竞彩足球计算器数据获取
用法:
node worldcup-calculator.js [命令] [选项]
命令:
list 列出所有比赛(默认)
had 只显示胜平负玩法
hhad 只显示让球胜平负玩法
crs 只显示比分玩法
ttg 只显示总进球玩法
hafu 只显示混合过关玩法
hilo 只显示大小球玩法
summary 精简模式(每场只显示胜平负+让球+大小球)
history <matchId> <玩法> 获取赔率变化历史
玩法: had, hhad, ttg, crs, hilo, hafu
help 显示帮助
选项:
--json 输出 JSON 格式
--wc 只显示世界杯比赛
--team <名称> 按球队名过滤(模糊匹配)
--date <YYYY-MM-DD> 按日期过滤
示例:
node worldcup-calculator.js # 列出所有比赛
node worldcup-calculator.js had # 只显示胜平负
node worldcup-calculator.js --wc # 只显示世界杯
node worldcup-calculator.js summary --wc # 世界杯精简模式
node worldcup-calculator.js --team 巴西 # 巴西相关比赛
node worldcup-calculator.js --date 2026-06-14 # 指定日期
node worldcup-calculator.js history 2040166 had # 赔率历史
支持的玩法:
${poolCodeHelp}
`);
}
async function getMatchCalculator(poolCode) {
let url = `${API_BASE}/football/getMatchCalculatorV1.qry?channel=${CHANNEL}`;
if (poolCode) {
url += `&poolCode=${poolCode}`;
}
const response = await axios.get(url, { headers: getRandomHeaders() });
if (response.data.success !== true) {
throw new Error(`API error: ${response.data.errorMessage || response.data.errorCode}`);
}
return response.data.value;
}
async function getOddsHistory(matchId, poolCode) {
const url = `${API_BASE}/football/getOddsHistoryV1.qry?matchId=${matchId}&poolCode=${poolCode}`;
const response = await axios.get(url, { headers: getRandomHeaders() });
const data = response.data;
if (data.success !== true && data.errorCode) {
throw new Error(`API error: ${data.errorMessage || data.errorCode}`);
}
return data.value || {};
}
function parsePoolOdds(poolCode, poolData, odds) {
const result = { poolCode, name: poolCodeMap[poolCode] || poolCode };
switch (poolCode) {
case 'had':
case 'hhad':
result.homeWin = odds.h;
result.draw = odds.d;
result.awayWin = odds.a;
result.goalLine = odds.goalLine || (poolCode === 'hhad' ? '让球' : '');
result.homeTrend = odds.hf || '';
result.drawTrend = odds.df || '';
result.awayTrend = odds.af || '';
break;
case 'ttg':
result.goals = {
'0': odds.s0, '1': odds.s1, '2': odds.s2, '3': odds.s3,
'4': odds.s4, '5': odds.s5, '6': odds.s6, '7+': odds.s7
};
break;
case 'hafu':
result.options = {
'胜胜': odds.hh, '胜平': odds.hd, '胜负': odds.ha,
'平胜': odds.dh, '平平': odds.dd, '平负': odds.da,
'负胜': odds.ah, '负平': odds.ad, '负负': odds.aa
};
result.trends = {
'胜胜': odds.hhf, '胜平': odds.hdf, '胜负': odds.haf,
'平胜': odds.dhf, '平平': odds.ddf, '平负': odds.daf,
'负胜': odds.ahf, '负平': odds.adf, '负负': odds.aaf
};
break;
case 'hilo':
result.goalLine = odds.goalLine || '';
result.over = odds.h;
result.under = odds.l;
result.overTrend = odds.hf || '';
result.underTrend = odds.lf || '';
break;
case 'crs':
result.scores = {};
result.trends = {};
const scoreMap = {
'0:1': 's00s01', '0:2': 's00s02', '1:2': 's01s02',
'0:3': 's00s03', '1:3': 's01s03', '2:3': 's02s03',
'0:4': 's00s04', '1:4': 's01s04', '2:4': 's02s04',
'0:5': 's00s05', '1:5': 's01s05', '2:5': 's02s05',
'0:0': 's00s00', '1:1': 's01s01', '2:2': 's02s02', '3:3': 's03s03', '平其他': 's1sd',
'1:0': 's01s00', '2:0': 's02s00', '2:1': 's02s01',
'3:0': 's03s00', '3:1': 's03s01', '3:2': 's03s02',
'4:0': 's04s00', '4:1': 's04s01', '4:2': 's04s02',
'5:0': 's05s00', '5:1': 's05s01', '5:2': 's05s02',
'主胜其他': 's1sh', '客胜其他': 's1sa'
};
for (const [label, key] of Object.entries(scoreMap)) {
if (odds[key]) {
result.scores[label] = odds[key];
result.trends[label] = odds[key + 'f'] || '';
}
}
break;
case 'wnm':
result.winners = {
'主胜1': odds.w1, '主胜2': odds.w2, '主胜3': odds.w3,
'主胜4': odds.w4, '主胜5': odds.w5, '主胜6': odds.w6,
'客胜1': odds.l1, '客胜2': odds.l2, '客胜3': odds.l3,
'客胜4': odds.l4, '客胜5': odds.l5, '客胜6': odds.l6
};
break;
case 'mnl':
result.homeWin = odds.h;
result.awayWin = odds.a;
result.homeTrend = odds.hf || '';
result.awayTrend = odds.af || '';
break;
}
result.single = poolData.single;
result.cbt = poolData.cbtValue;
return result;
}
function formatMatch(match, filterWc) {
const pools = [];
if (match.poolList) {
for (const poolInfo of match.poolList) {
const poolCode = poolInfo.poolCode.toLowerCase();
const odds = match[poolCode];
if (odds) {
pools.push(parsePoolOdds(poolCode, poolInfo, odds));
}
}
}
const result = {
matchId: match.matchId,
matchNum: match.matchNumStr,
league: match.leagueAllName || match.leagueAbbName,
homeTeam: match.homeTeamAllName,
awayTeam: match.awayTeamAllName,
homeRank: match.homeRank,
awayRank: match.awayRank,
date: match.matchDate,
time: match.matchTime,
status: match.matchStatus,
pools
};
if (filterWc) {
const isWc = result.league.includes('世界杯') ||
(match.homeRank && /[A-L]组/.test(match.homeRank));
if (!isWc) return null;
}
return result;
}
function printMatch(match) {
console.log(`\n${match.matchNum} ${match.homeTeam}(${match.homeRank}) vs ${match.awayTeam}(${match.awayRank})`);
console.log(`${match.date} ${match.time} [${match.status}]`);
console.log('-'.repeat(60));
for (const pool of match.pools) {
console.log(`\n【${pool.name}】单关:${pool.single} 截止:${pool.cbt}`);
switch (pool.poolCode) {
case 'had':
case 'mnl':
console.log(` 主胜:${pool.homeWin || '--'} ${pool.homeTrend === '1' ? '↑' : pool.homeTrend === '-1' ? '↓' : ''}`);
if (pool.draw !== undefined) {
console.log(` 平局:${pool.draw || '--'} ${pool.drawTrend === '1' ? '↑' : pool.drawTrend === '-1' ? '↓' : ''}`);
}
console.log(` 客胜:${pool.awayWin || '--'} ${pool.awayTrend === '1' ? '↑' : pool.awayTrend === '-1' ? '↓' : ''}`);
break;
case 'hhad':
console.log(` 让球:${pool.goalLine}`);
console.log(` 主胜:${pool.homeWin || '--'} ${pool.homeTrend === '1' ? '↑' : pool.homeTrend === '-1' ? '↓' : ''}`);
console.log(` 平局:${pool.draw || '--'} ${pool.drawTrend === '1' ? '↑' : pool.drawTrend === '-1' ? '↓' : ''}`);
console.log(` 客胜:${pool.awayWin || '--'} ${pool.awayTrend === '1' ? '↑' : pool.awayTrend === '-1' ? '↓' : ''}`);
break;
case 'ttg':
console.log(' 总进球赔率:');
console.log(` 0球:${pool.goals['0'] || '--'} 1球:${pool.goals['1'] || '--'} 2球:${pool.goals['2'] || '--'} 3球:${pool.goals['3'] || '--'}`);
console.log(` 4球:${pool.goals['4'] || '--'} 5球:${pool.goals['5'] || '--'} 6球:${pool.goals['6'] || '--'} 7+:${pool.goals['7+'] || '--'}`);
break;
case 'hafu':
console.log(' 比分混合过关:');
for (const [key, value] of Object.entries(pool.options)) {
console.log(` ${key}:${value || '--'}`);
}
break;
case 'hilo':
console.log(` 大小分线:${pool.goalLine}`);
console.log(` 大:${pool.over || '--'} ${pool.overTrend === '1' ? '↑' : pool.overTrend === '-1' ? '↓' : ''}`);
console.log(` 小:${pool.under || '--'} ${pool.underTrend === '1' ? '↑' : pool.underTrend === '-1' ? '↓' : ''}`);
break;
case 'crs':
console.log(' 比分(客胜/平/主胜):');
const scores = pool.scores;
const awayRow = ['0:1', '0:2', '1:2', '0:3', '1:3', '2:3'];
const awayRow2 = ['0:4', '1:4', '2:4', '0:5', '1:5', '2:5', '客胜其他'];
const drawRow = ['0:0', '1:1', '2:2', '3:3', '平其他'];
const homeRow = ['1:0', '2:0', '2:1', '3:0', '3:1', '3:2'];
const homeRow2 = ['4:0', '4:1', '4:2', '5:0', '5:1', '5:2', '主胜其他'];
const fmtScore = (s) => {
const v = scores[s] || '--';
const t = pool.trends?.[s];
const trend = t === '1' ? '↑' : t === '-1' ? '↓' : '';
return `${s}:${v}${trend}`;
};
console.log(' 客胜: ' + awayRow.map(fmtScore).join(' '));
console.log(' ' + awayRow2.map(fmtScore).join(' '));
console.log(' 平局: ' + drawRow.map(fmtScore).join(' '));
console.log(' 主胜: ' + homeRow.map(fmtScore).join(' '));
console.log(' ' + homeRow2.map(fmtScore).join(' '));
break;
}
}
}
function printOddsHistory(oddsList, poolCode) {
console.log(`\n赔率变化历史 [${poolCodeMap[poolCode] || poolCode}]`);
console.log('='.repeat(60));
console.log(' 胜 平 负 时间');
console.log('-'.repeat(60));
for (const odd of oddsList) {
const trend = (t) => t === '1' ? '↑' : t === '-1' ? '↓' : '';
const h = odd.h + trend(odd.hf);
const d = odd.d + trend(odd.df);
const a = odd.a + trend(odd.af);
const time = `${odd.updateDate?.substring(5) || ''} ${odd.updateTime?.substring(0, 5) || ''}`;
console.log(` ${h.padEnd(8)} ${d.padEnd(8)} ${a.padEnd(8)} ${time}`);
}
}
function printMatchSummary(match) {
console.log(`\n${match.matchNum} ${match.homeTeam} vs ${match.awayTeam}`);
console.log(`${match.date} ${match.time}`);
console.log('-'.repeat(60));
const findPool = (code) => match.pools.find(p => p.poolCode === code);
const had = findPool('had');
const hhad = findPool('hhad');
const hilo = findPool('hilo');
if (had) {
const trendH = had.homeTrend === '1' ? '↑' : had.homeTrend === '-1' ? '↓' : '';
const trendD = had.drawTrend === '1' ? '↑' : had.drawTrend === '-1' ? '↓' : '';
const trendA = had.awayTrend === '1' ? '↑' : had.awayTrend === '-1' ? '↓' : '';
console.log(` 胜平负 主胜:${had.homeWin}${trendH} 平:${had.draw}${trendD} 客胜:${had.awayWin}${trendA}`);
}
if (hhad) {
const trendH = hhad.homeTrend === '1' ? '↑' : hhad.homeTrend === '-1' ? '↓' : '';
const trendD = hhad.drawTrend === '1' ? '↑' : hhad.drawTrend === '-1' ? '↓' : '';
const trendA = hhad.awayTrend === '1' ? '↑' : hhad.awayTrend === '-1' ? '↓' : '';
console.log(` 让球(${hhad.goalLine}) 主胜:${hhad.homeWin}${trendH} 平:${hhad.draw}${trendD} 客胜:${hhad.awayWin}${trendA}`);
}
if (hilo) {
const trendO = hilo.overTrend === '1' ? '↑' : hilo.overTrend === '-1' ? '↓' : '';
const trendU = hilo.underTrend === '1' ? '↑' : hilo.underTrend === '-1' ? '↓' : '';
console.log(` 大小(${hilo.goalLine}) 大:${hilo.over}${trendO} 小:${hilo.under}${trendU}`);
}
}
async function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === 'help') {
printUsage();
return;
}
if (args[0] === 'history') {
if (args.length < 3) {
console.log('用法: node worldcup-calculator.js history <matchId> <玩法>');
console.log('示例: node worldcup-calculator.js history 2040166 had');
return;
}
const matchId = args[1];
const poolCode = args[2].toLowerCase();
const data = await getOddsHistory(matchId, poolCode);
const list = data.hadList || data.hhadList || data.ttgList || data.crsList || data.hiloList || data.hafuList || [];
if (list.length === 0) {
console.log('暂无赔率变化历史数据');
} else {
printOddsHistory(list, poolCode);
}
return;
}
const poolCode = args[0].startsWith('--') || args[0] === 'summary' ? '' : args[0];
const outputJson = args.includes('--json');
const filterWc = args.includes('--wc');
const summaryMode = args[0] === 'summary';
const teamIdx = args.indexOf('--team');
const teamFilter = teamIdx > -1 ? args[teamIdx + 1] : null;
const dateIdx = args.indexOf('--date');
const dateFilter = dateIdx > -1 ? args[dateIdx + 1] : null;
const data = await getMatchCalculator(poolCode);
if (data.matchInfoList) {
const matches = [];
for (const leagueData of data.matchInfoList) {
for (const subMatch of leagueData.subMatchList) {
const match = formatMatch(subMatch, filterWc);
if (!match) continue;
if (teamFilter && !match.homeTeam.includes(teamFilter) && !match.awayTeam.includes(teamFilter)) {
continue;
}
if (dateFilter && match.date !== dateFilter) {
continue;
}
matches.push(match);
if (!outputJson) {
if (summaryMode) {
printMatchSummary(match);
} else {
printMatch(match);
}
}
}
}
if (outputJson) {
console.log(JSON.stringify({
lastUpdateTime: data.lastUpdateTime,
matches
}, null, 2));
} else {
console.log(`\n总计 ${matches.length} 场比赛`);
console.log(`\n数据更新时间: ${data.lastUpdateTime || '未知'}`);
}
} else {
if (outputJson) {
console.log(JSON.stringify({ matches: [], lastUpdateTime: data.lastUpdateTime }, null, 2));
} else {
console.log('暂无比赛数据');
}
}
}
main().catch(err => {
console.error('Error:', err.message);
process.exit(1);
});#!/usr/bin/env node
/**
* 2026年世界杯比赛详情获取工具
* 从百度体育比赛详情页抓取多 tab 数据
*
* 6个tab:
* 分析 - 情报、历史战绩、赛前预测、猜胜负
* 阵容 - 双方首发/替补/阵型/场地/裁判
* 聊天 - 聊天室
* 赛况 - 比赛实况:进球/红黄牌/换人事件流,场地信息
* 统计 - 控球/射门/角球等技术统计
* 指数 - 多家博彩公司赔率
*/
const https = require('https');
const { getRandomUserAgent } = require('./lib/user-agents');
const HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding': 'identity',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Referer': 'https://tiyu.baidu.com/',
'Origin': 'https://tiyu.baidu.com'
};
const DETAIL_URL = 'https://tiyu.baidu.com/al/live/detail';
const VALID_TABS = ['分析', '阵容', '聊天', '赛况', '统计', '指数'];
function httpGet(url) {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const options = {
hostname: urlObj.hostname,
path: urlObj.pathname + urlObj.search,
method: 'GET',
headers: { ...HEADERS, 'User-Agent': getRandomUserAgent() }
};
const req = https.request(options, (res) => {
const chunks = [];
res.on('data', (chunk) => { chunks.push(chunk); });
res.on('end', () => {
resolve(Buffer.concat(chunks).toString('utf-8'));
});
});
req.on('error', reject);
req.setTimeout(15000, () => {
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
}
/**
* 提取页面中嵌入的 JSON 数据
* 未开始/已结束: {"data":{"header":{...}, ...}, "data":{tabData}}
* 进行中: {"data":{"headLive":{...}, ...}, "data":{tabData}}
* 第二个 "data":{ 是 tab 数据
*/
function extractDetailJson(html) {
const matches = [...html.matchAll(/"data":\{/g)];
if (matches.length < 2) {
throw new Error('未找到比赛详情 JSON 数据');
}
// 第一个 data 是 header/headLive,第二个是 tab 数据
const headerStart = matches[0].index + '"data":'.length;
const tabStart = matches[1].index + '"data":'.length;
const headerEnd = findMatchingBrace(html, headerStart);
const tabEnd = findMatchingBrace(html, tabStart);
const headerObj = JSON.parse(html.slice(headerStart, headerEnd));
// 进行中的比赛用 headLive,已开始/未开始/已结束用 header
return {
header: headerObj.header || headerObj.headLive || headerObj,
tabData: JSON.parse(html.slice(tabStart, tabEnd))
};
}
function findMatchingBrace(str, start) {
let depth = 0;
let inString = false;
let escape = false;
for (let i = start; i < str.length; i++) {
const c = str[i];
if (escape) { escape = false; continue; }
if (c === '\\') { escape = true; continue; }
if (c === '"') { inString = !inString; continue; }
if (inString) continue;
if (c === '{') depth++;
else if (c === '}') {
depth--;
if (depth === 0) return i + 1;
}
}
return -1;
}
function extractTeamId(link) {
if (!link) return '';
const m = link.match(/id=([a-f0-9]+)/);
return m ? m[1] : '';
}
/**
* 解析 header:比赛基本信息
*/
function parseHeader(header) {
return {
game: header.game || '',
matchDesc: header.matchDesc || '',
matchStage: header.matchStage || '',
date: header.date || '',
dateFormat: header.dateFormat || '',
weekday: header.weekday || '',
time: header.time || '',
timestamp: header.timestamp || 0,
startTime: header.timestamp ? new Date(header.timestamp * 1000).toISOString() : '',
status: header.status || '',
matchStatus: header.matchStatus || '',
matchStatusText: header.matchStatusText || '',
vs: header.vs || '',
winner: header.winner || '',
hasLive: !!header.hasLive,
hasFlash: !!header.hasFlash,
hasVipLive: !!header.hasVipLive,
hot: header.hot || 0,
plainKey: header.plaintext_key || header.rawKey || '',
homeTeam: {
name: header.leftLogo ? header.leftLogo.name : '',
score: header.leftLogo ? header.leftLogo.score : '',
logo: header.leftLogo ? header.leftLogo.logo : '',
rank: header.leftLogo && header.leftLogo.rankInfo1 ? header.leftLogo.rankInfo1.text : '',
teamId: extractTeamId(header.leftLogo ? header.leftLogo.link : '')
},
awayTeam: {
name: header.rightLogo ? header.rightLogo.name : '',
score: header.rightLogo ? header.rightLogo.score : '',
logo: header.rightLogo ? header.rightLogo.logo : '',
rank: header.rightLogo && header.rightLogo.rankInfo1 ? header.rightLogo.rankInfo1.text : '',
teamId: extractTeamId(header.rightLogo ? header.rightLogo.link : '')
},
liveList: (header.pcLiveList || []).map(l => ({
name: l.name,
category: l.category,
link: l.link,
isExternal: l.to === 'out'
}))
};
}
/**
* 解析"分析"tab
*/
function parseAnalysis(data) {
if (!data) return null;
const result = {};
if (Array.isArray(data.igence)) {
result.intelligence = data.igence.map(item => ({
title: item.intelligencetitle || '',
homeTeam: item.intelligence ? item.intelligence.intelligenceTeamInfo : null,
homeTeamPoints: item.intelligence ? (item.intelligence.intelligenceteam || []).map(p => p.content) : [],
awayTeam: item.intelligence ? item.intelligence.intelligenceteamLeaterInfo : null,
awayTeamPoints: item.intelligence ? (item.intelligence.intelligenceteamleater || []).map(p => p.content) : []
}));
}
if (Array.isArray(data.homeRecord)) {
result.records = data.homeRecord.map(rec => {
const h = rec.history || {};
return {
title: h.title || '',
subTitle: h.subTitle || '',
result: h.result || '',
resultArr: h.resultArr || null,
probability: h.probability || [],
matches: (h.list || []).map(m => ({
date: m.date,
match: m.match,
home: { name: m.left.name, score: m.left.score, isWin: m.left.isWin },
away: { name: m.right.name, score: m.right.score, isWin: m.right.isWin },
score: m.vs,
handicap: m.oddsHandicap,
totalGoals: m.oddsTotalGoals
}))
};
});
}
if (data.result) {
result.prediction = {
sampleSize: data.result.num,
homeWinRate: data.result.team && data.result.team[0] ? data.result.team[0].winrate : '',
awayWinRate: data.result.team && data.result.team[1] ? data.result.team[1].winrate : '',
similarOddsHistory: {
homeWin: data.result.percentage.victory,
draw: data.result.percentage.draw,
awayWin: data.result.percentage.lost
},
teams: data.result.team || []
};
}
if (data.guess && data.guess.data) {
result.guess = {
title: data.guess.title,
total: data.guess.total,
deadline: data.guess.deadlineTime,
options: data.guess.data.map(d => ({
name: d.name,
count: d.count,
percentage: data.guess.total > 0 ? ((d.count / data.guess.total) * 100).toFixed(1) + '%' : '0%'
}))
};
}
return result;
}
/**
* 解析"阵容"tab
*/
function parseLineup(data) {
if (!data) return null;
const parseTeam = (t) => {
if (!t) return null;
const parsePlayer = (p) => ({
name: p.name,
number: p.number,
position: p.position,
playerId: p.playerId,
nation: p.nation ? p.nation.name : '',
coordinate: p.coordinate || null,
incidents: p.incidents || []
});
return {
name: t.name,
logo: t.logo,
formation: t.formation,
starter: (t.starter || []).map(parsePlayer),
substitute: (t.substitute || []).map(parsePlayer),
playerList: (t.playerList || []).map(parsePlayer)
};
};
return {
confirmed: data.confirmed,
updateTime: data.update_time,
court: data.court,
referee: data.referee,
home: parseTeam(data.home),
away: parseTeam(data.away)
};
}
/**
* 解析"赛况"tab
*/
function parseLive(data) {
if (!data) return null;
return {
venue: data.graphic_incidents ? data.graphic_incidents.venue : null,
narrative: data.graphic_incidents ? (data.graphic_incidents.graphic || []).map(g => ({
text: g.word,
time: g.time,
teamName: g.teamName,
iconType: g.icon_type,
position: g.position
})) : [],
incidents: data.graphic_incidents ? (data.graphic_incidents.incidents || []).map(i => ({
type: i.type,
text: i.text,
goalType: i.goaltype,
passedTime: i.passedTime,
sortTime: i.sortTime,
team: i.right ? i.right.teamName : (i.left ? i.left.teamName : ''),
direction: i.right ? 'away' : (i.left ? 'home' : ''),
detail: i.detail || null
})) : [],
events: data.events
};
}
/**
* 解析"统计"tab
*/
function parseStats(data) {
if (!data) return null;
const stats = data['line-statistics'];
if (!stats) return null;
return {
title: stats.title,
homeTeam: stats.team ? stats.team.left.name : '',
awayTeam: stats.team ? stats.team.right.name : '',
items: (stats.list || []).map(item => ({
name: item.title,
home: item.left,
away: item.right
}))
};
}
/**
* 解析"指数"tab
*/
function parseOdds(data) {
if (!data) return null;
return {
asyncTime: data.asyncTime,
bookmakers: (data.list || []).map(b => ({
datas: (b.datas || []).map(d => ({
initial: d.initial,
now: d.now,
history: d.history
}))
}))
};
}
/**
* 获取比赛详情
*/
async function fetchMatchDetail(matchId, tab = '分析') {
const url = `${DETAIL_URL}?matchId=${encodeURIComponent(matchId)}&tab=${encodeURIComponent(tab)}`;
const html = await httpGet(url);
const { header, tabData } = extractDetailJson(html);
const result = {
matchId,
tab,
match: parseHeader(header),
tabData: null
};
switch (tab) {
case '分析': result.tabData = parseAnalysis(tabData); break;
case '阵容': result.tabData = parseLineup(tabData); break;
case '赛况': result.tabData = parseLive(tabData); break;
case '统计': result.tabData = parseStats(tabData); break;
case '指数': result.tabData = parseOdds(tabData); break;
case '聊天': result.tabData = tabData; break;
default: result.tabData = tabData;
}
return result;
}
async function main() {
const args = process.argv.slice(2);
const command = args[0];
try {
switch (command) {
case 'detail':
case 'get':
case '-d': {
const matchId = args[1];
const tab = args[2] || '分析';
if (!matchId) {
console.error('Error: 请提供 matchId');
process.exit(1);
}
const detail = await fetchMatchDetail(matchId, tab);
console.log(JSON.stringify(detail, null, 2));
break;
}
case 'info': {
const matchId = args[1];
if (!matchId) {
console.error('Error: 请提供 matchId');
process.exit(1);
}
const detail = await fetchMatchDetail(matchId, '分析');
console.log(JSON.stringify(detail.match, null, 2));
break;
}
case 'analysis':
case 'lineup':
case 'live':
case 'stats':
case 'odds': {
const matchId = args[1];
if (!matchId) {
console.error('Error: 请提供 matchId');
process.exit(1);
}
const tabMap = { analysis: '分析', lineup: '阵容', live: '赛况', stats: '统计', odds: '指数' };
const tab = tabMap[command];
const detail = await fetchMatchDetail(matchId, tab);
console.log(JSON.stringify(detail.tabData, null, 2));
break;
}
case 'help':
default:
console.log(`
2026年世界杯比赛详情获取工具
用法:
node worldcup-match.js <command> <matchId> [tab]
命令:
info <matchId> 比赛基本信息
analysis <matchId> 分析数据(情报/历史战绩/预测/猜胜负)
lineup <matchId> 阵容数据(首发/替补/阵型/裁判)
live <matchId> 赛况数据(事件流/场地信息)
stats <matchId> 统计数据(控球/射门/角球等)
odds <matchId> 指数数据(多家博彩公司赔率)
detail <matchId> [tab] 完整详情(默认 分析)
可用tab:
分析 (analysis) - 情报、历史战绩、赛前预测、猜胜负
阵容 (lineup) - 首发、替补、阵型、场地、裁判
赛况 (live) - 比赛事件流(进球/红黄牌/换人)、场地信息
统计 (stats) - 控球率/射门/角球等技术统计(仅已结束比赛)
指数 (odds) - 多家博彩公司初盘/即时盘
聊天 (chat) - 聊天室信息
示例:
node worldcup-match.js info 5LiW55WM...
node worldcup-match.js analysis 5LiW55WM...
node worldcup-match.js lineup 5LiW55WM...
node worldcup-match.js live 5LiW55WM...
node worldcup-match.js stats 5LiW55WM...
`);
process.exit(0);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
module.exports = {
fetchMatchDetail,
extractDetailJson,
parseHeader,
parseAnalysis,
parseLineup,
parseLive,
parseStats,
parseOdds,
VALID_TABS
};
if (require.main === module) {
main();
}#!/usr/bin/env node
/**
* 2026年世界杯球员数据获取工具
* 从百度体育球员页抓取资料/动态/数据/赛程
*/
const https = require('https');
const { getRandomUserAgent } = require('./lib/user-agents');
const HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding': 'identity',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Referer': 'https://tiyu.baidu.com/',
'Origin': 'https://tiyu.baidu.com'
};
const PLAYER_URL = 'https://tiyu.baidu.com/al/player';
const VALID_TABS = ['赛程', '评分', '动态', '资料', '数据'];
const TAB_TO_INDEX = {
'赛程': 0,
'评分': 1,
'动态': 2,
'资料': 3,
'数据': 4
};
function httpGet(url) {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const options = {
hostname: urlObj.hostname,
path: urlObj.pathname + urlObj.search,
method: 'GET',
headers: { ...HEADERS, 'User-Agent': getRandomUserAgent() }
};
const req = https.request(options, (res) => {
const chunks = [];
res.on('data', (chunk) => { chunks.push(chunk); });
res.on('end', () => {
resolve(Buffer.concat(chunks).toString('utf-8'));
});
});
req.on('error', reject);
req.setTimeout(15000, () => {
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
}
function findMatchingBrace(str, start) {
let depth = 0;
let inString = false;
let escape = false;
for (let i = start; i < str.length; i++) {
const c = str[i];
if (escape) { escape = false; continue; }
if (c === '\\') { escape = true; continue; }
if (c === '"') { inString = !inString; continue; }
if (inString) continue;
if (c === '[') depth++;
else if (c === ']') {
depth--;
if (depth === 0) return i + 1;
}
}
return -1;
}
function extractTabsList(html) {
const idx = html.indexOf('tabsList');
if (idx === -1) throw new Error('未找到 tabsList 标记');
const arrStart = html.indexOf('[', idx);
const end = findMatchingBrace(html, arrStart);
if (end === -1) throw new Error('tabsList 数组未闭合');
return JSON.parse(html.slice(arrStart, end));
}
/**
* 解析赛程 tab(item 0)
*/
function parseSchedule(item) {
if (!item || !item.subTabList) return null;
const result = { subTabs: item.subTabs || [], teams: {} };
for (const sub of item.subTabList) {
result.teams[sub.id] = {
title: sub.title,
teamId: sub.id,
matches: []
};
for (const dayBlock of sub.data || []) {
for (const m of dayBlock.list || []) {
result.teams[sub.id].matches.push({
matchId: m.matchId,
oriKey: m.oriKey,
date: m.startTime ? m.startTime.split(' ')[0] : '',
time: m.time,
weekday: m.weekday,
stage: m.matchStage,
homeTeam: m.leftLogo ? m.leftLogo.name : '',
awayTeam: m.rightLogo ? m.rightLogo.name : '',
homeScore: m.scoreInfo ? m.scoreInfo.leftRegularScore : '',
awayScore: m.scoreInfo ? m.scoreInfo.rightRegularScore : '',
scoreLine: m.vsLine,
status: m.matchStatusText,
competition: m.game
});
}
}
}
return result;
}
/**
* 解析评分 tab(item 1)
*/
function parseRatings(item) {
return item && Object.keys(item).length > 1 ? item : null;
}
/**
* 解析动态 tab(item 2)
*/
function parseNews(item) {
if (!item) return null;
return {
total: item.pn || (item.newsList || []).length,
newsList: (item.newsList || []).map(n => ({
title: n.title,
link: n.link,
linkType: n.linkType,
endTime: n.endTime,
factorTime: n.factorTime,
source: n.source,
nid: n.nid,
img: n.img,
single: n.single
}))
};
}
/**
* 解析资料 tab(item 3)
* data: {ability, honor, honorRecords, market, transfer, wiki}
*/
function parseInfo(item) {
if (!item || !item.data) return null;
const data = item.data;
const wiki = data.wiki || {};
const detail = wiki.detail || {};
return {
wiki: {
num: wiki.num,
img: wiki.img,
nickName: wiki.nickName,
height: wiki.height,
weight: wiki.weight,
detail: {
position: detail.position,
heavyFoot: detail.heavyFoot,
age: detail.age,
national: detail.national,
expiryDate: detail.expiryDate,
socialStatus: detail.socialStatus,
socialStatusUnit: detail.socialStatusUnit
},
list: wiki.list || []
},
ability: data.ability ? {
overall: data.ability.overall,
overallColor: data.ability.overallColor,
radarDims: (data.ability.radarDims || []).map(d => ({
name: d.name,
value: d.value,
level: d.level,
color: d.color
})),
categories: data.ability.categories || []
} : null,
honor: (data.honor || []).map(h => ({
match: h.match,
logo: h.logo,
seasons: (h.list || []).map(l => ({ date: l.date, logo: l.logo }))
})),
honorRecords: (data.honorRecords || []).map(r => ({
honorName: r.honorName,
totalWins: r.totalWins,
seasons: r.seasons || []
})),
transfer: data.transfer ? {
unit: data.transfer.unit,
list: (data.transfer.list || []).map(t => ({
date: t.date,
description: t.description,
price: t.price,
team: t.team,
teamLogo: t.teamLogo,
outTeam: t.outTeam,
outTeamLogo: t.outTeamLogo
}))
} : null,
market: data.market || null
};
}
/**
* 解析数据 tab(item 4)
* data: {seasonStats: [{match, season, rank, stats: [{group, list}]}]}
*/
function parseStats(item) {
if (!item || !item.data) return null;
const data = item.data;
return {
seasonStats: (data.seasonStats || []).map(ss => ({
competition: ss.match,
season: ss.season,
rank: ss.rank || null,
statGroups: (ss.stats || []).map(g => ({
group: g.group,
field: g.groupField,
items: (g.list || []).map(s => ({
name: s.name,
value: s.value,
average: s.average,
rank: s.rank,
ranking: s.ranking,
rankingText: s.rankingTxt
}))
}))
}))
};
}
/**
* 获取球员数据
*/
async function fetchPlayerData(playerId, tab = '资料') {
const url = `${PLAYER_URL}?id=${encodeURIComponent(playerId)}&tab=${encodeURIComponent(tab)}`;
const html = await httpGet(url);
const tabsList = extractTabsList(html);
const tabIndex = TAB_TO_INDEX[tab];
const item = tabsList[tabIndex];
const result = { playerId, tab, tabData: null };
switch (tab) {
case '赛程': result.tabData = parseSchedule(item); break;
case '评分': result.tabData = parseRatings(item); break;
case '动态': result.tabData = parseNews(item); break;
case '资料': result.tabData = parseInfo(item); break;
case '数据': result.tabData = parseStats(item); break;
default: result.tabData = item;
}
return result;
}
async function main() {
const args = process.argv.slice(2);
const command = args[0];
try {
switch (command) {
case 'info': {
const playerId = args[1];
if (!playerId) {
console.error('Error: 请提供 playerId');
process.exit(1);
}
const data = await fetchPlayerData(playerId, '资料');
console.log(JSON.stringify(data.tabData, null, 2));
break;
}
case 'news':
case '动态': {
const playerId = args[1];
if (!playerId) {
console.error('Error: 请提供 playerId');
process.exit(1);
}
const data = await fetchPlayerData(playerId, '动态');
console.log(JSON.stringify(data.tabData, null, 2));
break;
}
case 'stats':
case '数据': {
const playerId = args[1];
const season = args[2];
const competition = args[3];
if (!playerId) {
console.error('Error: 请提供 playerId');
process.exit(1);
}
const data = await fetchPlayerData(playerId, '数据');
let stats = data.tabData.seasonStats;
if (season) stats = stats.filter(s => s.season === season);
if (competition) stats = stats.filter(s => s.competition === competition);
console.log(JSON.stringify({ seasonStats: stats }, null, 2));
break;
}
case 'schedule': {
const playerId = args[1];
if (!playerId) {
console.error('Error: 请提供 playerId');
process.exit(1);
}
const data = await fetchPlayerData(playerId, '赛程');
console.log(JSON.stringify(data.tabData, null, 2));
break;
}
case 'detail': {
const playerId = args[1];
const tab = args[2] || '资料';
if (!playerId) {
console.error('Error: 请提供 playerId');
process.exit(1);
}
const data = await fetchPlayerData(playerId, tab);
console.log(JSON.stringify(data, null, 2));
break;
}
case 'help':
default:
console.log(`
2026年世界杯球员数据获取工具
用法:
node worldcup-player.js <command> <playerId> [args]
命令:
info <playerId> 球员资料(基本资料+能力雷达+荣誉+转会)
news <playerId> 球员动态(新闻列表)
stats <playerId> [赛季] [赛事] 球员数据统计
schedule <playerId> 球员赛程(按俱乐部/国家队)
detail <playerId> [tab] 完整详情(默认资料)
可用tab:
资料 (info) - 基本资料/能力雷达/荣誉/转会历史
动态 (news) - 球员相关新闻
数据 (stats) - 球员多维数据统计(按赛事+赛季)
赛程 (schedule)- 球员出场记录
评分 (ratings) - 球员评分
示例:
node worldcup-player.js info 4262f62d5769aaf16ecc96d12bdf6b57
node worldcup-player.js news 4262f62d5769aaf16ecc96d12bdf6b57
node worldcup-player.js stats 7bf18ff5b176d56a734dd90598461b2b 2026 世界杯
node worldcup-player.js schedule 4262f62d5769aaf16ecc96d12bdf6b57
`);
process.exit(0);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
module.exports = {
fetchPlayerData,
parseSchedule,
parseRatings,
parseNews,
parseInfo,
parseStats,
VALID_TABS,
TAB_TO_INDEX
};
if (require.main === module) {
main();
}#!/usr/bin/env node
const { loadTeams, listAllTeams, findTeamByName, findTeamById, getGroup } = require('./lib/teams');
async function main() {
const args = process.argv.slice(2);
const command = args[0];
const data = loadTeams();
try {
switch (command) {
case 'list':
case 'ls':
case '--list': {
console.log(JSON.stringify(listAllTeams(data), null, 2));
break;
}
case 'group':
case '-g': {
const groupKey = args[1];
if (!groupKey) {
console.error('Error: 请提供小组标识,例如 A、B、C ... L');
process.exit(1);
}
const groupData = getGroup(data, groupKey);
if (!groupData) {
console.error(`Error: 找不到 ${groupKey} 组`);
process.exit(1);
}
console.log(JSON.stringify(groupData, null, 2));
break;
}
case 'find':
case '-f': {
const keyword = args[1];
if (!keyword) {
console.error('Error: 请提供球队名称关键字');
process.exit(1);
}
const team = findTeamByName(data, keyword);
if (!team) {
console.error(`Error: 找不到球队 ${keyword}`);
process.exit(1);
}
console.log(JSON.stringify(team, null, 2));
break;
}
case 'info': {
console.log(JSON.stringify({
tournament: data.tournament,
tournamentEn: data.tournamentEn,
season: data.season,
hostCountries: data.hostCountries,
totalTeams: data.totalTeams,
totalGroups: data.totalGroups,
teamsPerGroup: data.teamsPerGroup,
matchUrl: data.matchUrl,
rankingsUrl: data.rankingsUrl
}, null, 2));
break;
}
case 'hosts': {
const hosts = listAllTeams(data).filter(t => t.isHost);
console.log(JSON.stringify(hosts, null, 2));
break;
}
case 'pot': {
const pot = parseInt(args[1]);
if (isNaN(pot) || pot < 1 || pot > 4) {
console.error('Error: 档位必须在 1-4 之间');
process.exit(1);
}
const teams = listAllTeams(data).filter(t => t.pot === pot);
console.log(JSON.stringify({ pot, teams }, null, 2));
break;
}
default:
console.log(`
2026年世界杯球队配置查询工具
用法:
node worldcup-teams.js <command> [options]
命令:
list, ls, --list 列出所有48支球队
group, -g <A-L> 查询指定小组的4支球队
find, -f <球队名> 按名称模糊查找球队
info 查看赛事基本信息
hosts 列出东道主球队
pot <1-4> 列出指定档位的球队
示例:
node worldcup-teams.js list
node worldcup-teams.js group A
node worldcup-teams.js find 韩国
node worldcup-teams.js info
node worldcup-teams.js hosts
node worldcup-teams.js pot 1
`);
process.exit(0);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
module.exports = { loadTeams, listAllTeams, findTeamByName, findTeamById, getGroup };
if (require.main === module) {
main();
}