php项目统计SofaScore综合评分?

wen PHP项目 1

PHP项目统计SofaScore综合评分方案

理解需求

SofaScore 综合评分(Rating)是针对球员单场表现生成的评分(1.0~10.0),综合评分一般是多场比赛评分的加权平均,要统计综合评分,核心是:

php项目统计SofaScore综合评分?

  1. 获取球员每场比赛的评分数据
  2. 设计合理的加权算法
  3. 存储与聚合计算

数据来源

方式1:SofaScore 官方 API(非公开,需逆向)

SofaScore 有内部 API,典型接口:

GET https://api.sofascore.com/api/v1/player/{playerId}/statistics/overall
GET https://api.sofascore.com/api/v1/player/{playerId}/events/last/0
GET https://api.sofascore.com/api/v1/event/{eventId}/player/{playerId}/statistics

⚠️ 注意:官方未授权公开 API,使用需遵守其服务条款,建议:

  • 个人学习/研究使用,控制请求频率
  • 加 User-Agent、代理、缓存,避免封 IP
  • 商业项目建议购买数据服务(如 API-Football、Sportmonks)

方式2:第三方数据源

  • API-Football(RapidAPI):有球员评分
  • Sportmonks:付费正规
  • Football-Data.org:免费,但无球员评分

方式3:自行抓取(爬虫)

用 PHP + Guzzle 抓页面或内部接口,需注意反爬。


核心数据结构设计(MySQL)

-- 球员表
CREATE TABLE players (
    id INT PRIMARY KEY AUTO_INCREMENT,
    sofa_player_id BIGINT UNIQUE,
    name VARCHAR(100),
    team_id INT,
    position VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 单场评分记录表
CREATE TABLE player_match_ratings (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    player_id INT NOT NULL,
    match_id BIGINT NOT NULL,
    match_date DATE NOT NULL,
    competition_id INT,
    season VARCHAR(20),
    minutes_played INT DEFAULT 0,
    rating DECIMAL(3,1) NOT NULL,       -- 1.0 ~ 10.0
    goals INT DEFAULT 0,
    assists INT DEFAULT 0,
    is_starter TINYINT DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_player_match (player_id, match_id),
    KEY idx_player_date (player_id, match_date),
    KEY idx_competition (competition_id, season)
);
-- 综合评分缓存/汇总表(可选,提升查询性能)
CREATE TABLE player_aggregate_ratings (
    player_id INT PRIMARY KEY,
    competition_id INT DEFAULT 0,       -- 0 表示全部赛事
    season VARCHAR(20) DEFAULT 'all',   -- all 表示全部赛季
    matches_count INT DEFAULT 0,
    total_minutes INT DEFAULT 0,
    weighted_sum DECIMAL(10,2) DEFAULT 0,
    avg_rating DECIMAL(3,2) DEFAULT 0,  -- 简单平均
    weighted_rating DECIMAL(3,2) DEFAULT 0, -- 加权平均
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

综合评分算法

简单平均(不推荐,短时间出场拉低/拉高失真)

$$\text{avg} = \frac{\sum r_i}{n}$$

出场时间加权(推荐)

$$\text{weighted} = \frac{\sum (r_i \times \min(m_i, 90))}{\sum \min(m_i, 90)}$$

理由:出场 5 分钟得 7.0 和踢满 90 分钟得 7.0 分量不同。

更多维度加权(进阶)

weight = minutes_factor × competition_factor × recency_factor
  • minutes_factor:min(minutes, 90) / 90
  • competition_factor:欧冠 1.2 / 联赛 1.0 / 杯赛 0.8
  • recency_factor:exp(-λ × 天数差) 做时间衰减,λ ≈ 0.002(约一年半衰期)

PHP 实现示例

<?php
class RatingCalculator
{
    /**
     * 出场时间加权平均
     * @param array $ratings 每项 ['rating'=>7.5,'minutes'=>90,'competition_id'=>1,'match_date'=>'2024-03-01']
     */
    public function weightedAverage(array $ratings): float
    {
        if (empty($ratings)) return 0.0;
        $sum = 0.0;
        $weightSum = 0.0;
        foreach ($ratings as $r) {
            $minutes = min((int)$r['minutes'], 90);
            if ($minutes <= 0) continue; // 未出场忽略
            $w = $minutes
               * $this->competitionFactor($r['competition_id'] ?? 0)
               * $this->recencyFactor($r['match_date'] ?? date('Y-m-d'));
            $sum       += $r['rating'] * $w;
            $weightSum += $w;
        }
        return $weightSum > 0 ? round($sum / $weightSum, 2) : 0.0;
    }
    private function competitionFactor(int $competitionId): float
    {
        // 按赛事重要性调整,示例映射
        $map = [
            1  => 1.2,  // 欧冠
            2  => 1.1,  // 欧联
            8  => 1.0,  // 国内联赛
            0  => 0.9,  // 友谊赛/其他
        ];
        return $map[$competitionId] ?? 1.0;
    }
    private function recencyFactor(string $matchDate): float
    {
        $days = (time() - strtotime($matchDate)) / 86400;
        $lambda = 0.002; // 半衰期约 346 天
        return exp(-$lambda * max(0, $days));
    }
    /** 简单平均,供对比 */
    public function simpleAverage(array $ratings): float
    {
        $vals = array_filter(array_column($ratings, 'rating'), fn($v) => $v > 0);
        return count($vals) ? round(array_sum($vals) / count($vals), 2) : 0.0;
    }
}

数据采集(Guzzle 示例)

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
class SofaFetcher
{
    private Client $http;
    public function __construct()
    {
        $this->http = new Client([
            'base_uri' => 'https://api.sofascore.com/',
            'timeout'  => 10,
            'headers'  => [
                'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
                'Accept'     => 'application/json',
                'Referer'    => 'https://www.sofascore.com/',
            ],
        ]);
    }
    /** 获取球员近 N 场评分 */
    public function getPlayerRecentRatings(int $playerId, int $page = 0): array
    {
        $res = $this->http->get("api/v1/player/{$playerId}/events/last/{$page}");
        $data = json_decode($res->getBody()->getContents(), true);
        $result = [];
        foreach ($data['events'] ?? [] as $ev) {
            // 该接口返回赛事列表,还需要再查每场评分
            // 实际可用 /event/{id}/player/{pid}/statistics
            $result[] = [
                'match_id'       => $ev['id'],
                'match_date'     => date('Y-m-d', $ev['startTimestamp']),
                'competition_id' => $ev['tournament']['uniqueTournament']['id'] ?? 0,
            ];
        }
        return $result;
    }
    /** 获取单场评分 */
    public function getMatchRating(int $eventId, int $playerId): ?array
    {
        try {
            $res = $this->http->get("api/v1/event/{$eventId}/player/{$playerId}/statistics");
            $data = json_decode($res->getBody()->getContents(), true);
            return $data['statistics'] ?? null;
        } catch (\Throwable $e) {
            return null;
        }
    }
}

配合限速(sleep(1))、缓存(Redis 存 24h)、失败重试。


完整统计流程

┌─────────────┐   ┌──────────────┐   ┌───────────────┐   ┌──────────────┐
│ 采集层       │→→│ 清洗/入库     │→→│ 聚合计算       │→→│ 对外接口/展示 │
│ Sofa API    │   │ 去重+校验     │   │ 加权评分       │   │ REST/前端     │
└─────────────┘   └──────────────┘   └───────────────┘   └──────────────┘

定时任务(cron / 队列):

  1. 每日拉取昨日比赛评分 → 入库
  2. 重算受影响球员的聚合
  3. 写入 player_aggregate_ratings

聚合 SQL 示例(简单加权):

INSERT INTO player_aggregate_ratings
    (player_id, competition_id, season, matches_count, total_minutes, weighted_sum, avg_rating, weighted_rating)
SELECT
    player_id,
    0,
    'all',
    COUNT(*),
    SUM(minutes_played),
    SUM(rating * LEAST(minutes_played, 90)),
    ROUND(AVG(rating), 2),
    ROUND(SUM(rating * LEAST(minutes_played, 90)) / NULLIF(SUM(LEAST(minutes_played, 90)), 0), 2)
FROM player_match_ratings
GROUP BY player_id
ON DUPLICATE KEY UPDATE
    matches_count    = VALUES(matches_count),
    total_minutes    = VALUES(total_minutes),
    weighted_sum     = VALUES(weighted_sum),
    avg_rating       = VALUES(avg_rating),
    weighted_rating  = VALUES(weighted_rating),
    updated_at       = NOW();

REST 接口示例

// GET /api/players/{id}/rating?competition=0&season=all&method=weighted
$stmt = $pdo->prepare(
    'SELECT * FROM player_aggregate_ratings 
     WHERE player_id = ? AND competition_id = ? AND season = ?'
);
$stmt->execute([$playerId, $competitionId, $season]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo json_encode([
    'player_id'        => $playerId,
    'matches'          => (int)$row['matches_count'],
    'avg_rating'       => (float)$row['avg_rating'],
    'weighted_rating'  => (float)$row['weighted_rating'],
    'updated_at'       => $row['updated_at'],
]);

实战注意事项

问题 建议
反爬/封禁 加缓存(Redis 24h)、随机延迟、代理池,分散请求
评分口径 SofaScore 会回溯修正评分,需定期重拉历史(如 7 天内比赛)
短时间出场 出场 < 15 分钟的比赛建议按系数削减或排除出"综合评分"
位置差异 门将/后卫/前锋评分基准不同,可按位置分组统计
合法合规 商业项目请使用 API-Football 等授权渠道
重复比赛 用 UNIQUE(player_id, match_id) 保证幂等

可选进阶:综合评分模型

不只依赖 SofaScore 单一评分,可融合多源做综合分:

$$ S = w1 \cdot \text{SofaScore}{\text{加权}} + w_2 \cdot \frac{\text{进球+助攻}}{\text{出场}} + w_3 \cdot \text{球队胜率} + \dots $$

标准化到 0~10 后加权——这就是"自建综合评分",SofaScore 本身只是输入之一。


如果需要,我可以进一步提供:

  • Laravel 版的 Service/Command/模型代码
  • Redis 缓存 + 队列的完整采集流程
  • 爬虫防封的具体策略(UA 池、代理、异常重试)
  • 针对某个 具体球员/球队的完整可运行 demo

告诉我你的 PHP 框架(原生/Laravel/Symfony)和目标场景,我可以直接给可跑代码。

抱歉,评论功能暂时关闭!

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝