php项目统计SofaScore综合评分?

wen PHP项目 10

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

SofaScore 的评分体系在足球数据分析中很有参考价值,下面我给你一套完整的 PHP 实现思路和代码。

php项目统计SofaScore综合评分?

SofaScore 评分机制说明

SofaScore 的综合评分(Rating)特点:

  • 基础分 6.0,表现好加分,表现差减分
  • 范围约 4.0 ~ 10.0
  • 依据数据:进球、助攻、传球成功率、抢断、射门、关键传球、失误等
  • 不同位置权重不同(前锋重进攻,后卫重防守)

数据库设计

-- 球员基础信息
CREATE TABLE players (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    position ENUM('GK','DF','MF','FW') COMMENT '门将/后卫/中场/前锋'
);
-- 单场统计数据
CREATE TABLE player_match_stats (
    id INT PRIMARY KEY AUTO_INCREMENT,
    player_id INT,
    match_id INT,
    minutes_played INT DEFAULT 0,
    goals INT DEFAULT 0,
    assists INT DEFAULT 0,
    shots_on_target INT DEFAULT 0,
    key_passes INT DEFAULT 0,
    passes_total INT DEFAULT 0,
    passes_accurate INT DEFAULT 0,
    dribbles_success INT DEFAULT 0,
    tackles INT DEFAULT 0,
    interceptions INT DEFAULT 0,
    clearances INT DEFAULT 0,
    duels_won INT DEFAULT 0,
    duels_total INT DEFAULT 0,
    fouls_committed INT DEFAULT 0,
    yellow_cards INT DEFAULT 0,
    red_cards INT DEFAULT 0,
    saves INT DEFAULT 0,          -- 门将扑救
    goals_conceded INT DEFAULT 0, -- 门将失球
    rating DECIMAL(3,1) DEFAULT NULL,
    INDEX idx_player (player_id),
    INDEX idx_match (match_id)
);

核心评分算法(PHP 实现)

<?php
class SofaScoreRating
{
    // 各位置权重系数
    private array $weights = [
        'GK' => [
            'saves'          => 0.35,
            'goals_conceded' => -0.30,
            'passes'         => 0.10,
            'duels'          => 0.15,
        ],
        'DF' => [
            'tackles'        => 0.30,
            'interceptions'  => 0.25,
            'clearances'     => 0.20,
            'duels'          => 0.15,
            'passes'         => 0.10,
            'goals'          => 0.50,
            'assists'        => 0.30,
        ],
        'MF' => [
            'passes'         => 0.30,
            'key_passes'     => 0.25,
            'dribbles'       => 0.15,
            'tackles'        => 0.10,
            'interceptions'  => 0.10,
            'goals'          => 0.40,
            'assists'        => 0.35,
        ],
        'FW' => [
            'goals'          => 0.60,
            'assists'        => 0.30,
            'shots'          => 0.20,
            'key_passes'     => 0.15,
            'dribbles'       => 0.15,
        ],
    ];
    /**
     * 计算单场评分
     */
    public function calculate(array $stats, string $position): float
    {
        // 出场时间不足按比例折算(踢满90分钟算全场)
        $minutes = $stats['minutes_played'] ?? 0;
        if ($minutes <= 0) return 0.0;
        $timeFactor = min($minutes / 90, 1.0);
        // 基础分 6.0
        $rating = 6.0;
        // 1. 进球 & 助攻(位置加成)
        $goalWeight    = $this->weights[$position]['goals']    ?? 0.40;
        $assistWeight  = $this->weights[$position]['assists']  ?? 0.30;
        $rating += ($stats['goals']   ?? 0) * $goalWeight;
        $rating += ($stats['assists'] ?? 0) * $assistWeight;
        // 2. 传球成功率
        $passTotal   = $stats['passes_total']    ?? 0;
        $passAcc     = $stats['passes_accurate'] ?? 0;
        if ($passTotal > 0) {
            $passRate = $passAcc / $passTotal;      // 0~1
            // 70% 为基准线
            $rating += ($passRate - 0.70) * 3.0 * ($this->weights[$position]['passes'] ?? 0.2);
        }
        // 3. 防守数据
        $rating += ($stats['tackles']       ?? 0) * ($this->weights[$position]['tackles'] ?? 0.2) * 0.5;
        $rating += ($stats['interceptions'] ?? 0) * ($this->weights[$position]['interceptions'] ?? 0.15) * 0.5;
        $rating += ($stats['clearances']    ?? 0) * ($this->weights[$position]['clearances'] ?? 0.1) * 0.3;
        // 4. 关键传球 & 射正
        $rating += ($stats['key_passes']     ?? 0) * 0.15;
        $rating += ($stats['shots_on_target']?? 0) * 0.10;
        // 5. 过人成功
        $rating += ($stats['dribbles_success'] ?? 0) * 0.08;
        // 6. 对抗成功率
        $duelTotal = $stats['duels_total'] ?? 0;
        if ($duelTotal >= 3) {
            $duelRate = ($stats['duels_won'] ?? 0) / $duelTotal;
            $rating += ($duelRate - 0.5) * 0.8;
        }
        // 7. 门将特殊处理
        if ($position === 'GK') {
            $rating += ($stats['saves'] ?? 0) * 0.20;
            $rating -= ($stats['goals_conceded'] ?? 0) * 0.35;
            if (($stats['saves'] ?? 0) >= 5) $rating += 0.5; // 神扑加成
        }
        // 8. 负面数据
        $rating -= ($stats['fouls_committed'] ?? 0) * 0.05;
        $rating -= ($stats['yellow_cards']    ?? 0) * 0.30;
        $rating -= ($stats['red_cards']       ?? 0) * 1.50;
        // 9. 时间折算(替补出场按比例)
        $rating = 6.0 + ($rating - 6.0) * $timeFactor;
        // 10. 边界限制 4.0 ~ 10.0
        return round(max(4.0, min(10.0, $rating)), 1);
    }
    /**
     * 批量计算并按位置排名(SofaScore 风格:同位置内比较)
     */
    public function batchCalculate(array $playersStats): array
    {
        $result = [];
        foreach ($playersStats as $p) {
            $result[] = [
                'player_id' => $p['player_id'],
                'position'  => $p['position'],
                'rating'    => $this->calculate($p, $p['position']),
            ];
        }
        return $result;
    }
}

使用示例

$engine = new SofaScoreRating();
// 一场曼城比赛哈兰德的数据
$haaland = [
    'player_id'       => 1,
    'position'        => 'FW',
    'minutes_played'  => 90,
    'goals'           => 2,
    'assists'         => 1,
    'shots_on_target' => 4,
    'key_passes'      => 2,
    'passes_total'    => 18,
    'passes_accurate' => 15,
    'dribbles_success'=> 3,
    'tackles'         => 0,
    'interceptions'   => 0,
    'clearances'      => 0,
    'duels_won'       => 6,
    'duels_total'     => 9,
    'fouls_committed' => 1,
    'yellow_cards'    => 0,
    'red_cards'       => 0,
];
$rating = $engine->calculate($haaland, 'FW');
echo "哈兰德本场评分: {$rating}"; // 输出类似 8.7

进阶:抓取真实数据(爬虫)

如果需要从 SofaScore 实时抓取,需要注意官方无公开 API,采集方式:

<?php
class SofaScoreFetcher
{
    private const BASE = 'https://api.sofascore.com/api/v1';
    public function getMatchStatistics(int $matchId): array
    {
        $url = self::BASE . "/event/{$matchId}/statistics";
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => [
                'User-Agent: Mozilla/5.0',
                'Accept: application/json',
                'Referer: https://www.sofascore.com/',
            ],
            CURLOPT_TIMEOUT => 10,
        ]);
        $resp = curl_exec($ch);
        curl_close($ch);
        return json_decode($resp, true) ?? [];
    }
}

⚠️ 注意

  1. SofaScore 有 Cloudflare 反爬,直接 curl 常被 403,需配合 cloudscraper 或 Puppeteer
  2. 商业项目使用需遵守其 ToS 和版权,建议购买官方数据源(如 Sportmonks / API-Football
  3. 调用频率限制、代理池

可选方案:接入第三方 API

如果不想维护爬虫,推荐:

服务 特点
API-Football 免费 100次/天,覆盖广
Sportmonks 数据全,付费
Football-Data.org 免费层可用
飞鲸/纳米数据 国内,中文,付费

项目架构建议

sofa-rating/
├── src/
│   ├── SofaScoreRating.php       # 评分算法
│   ├── Repository/
│   │   └── MatchStatsRepository.php
│   ├── Fetcher/
│   │   └── SofaScoreFetcher.php  # 数据抓取
│   └── Service/
│       └── RatingService.php     # 业务编排
├── config/
├── public/
│   └── index.php
├── storage/
└── composer.json

RatingService 编排逻辑:抓取 → 入库 → 计算 → 缓存 → 输出排名。


几点经验提示

  1. 权重需要自我校准:官方算法未公开,最好拿已抓取的真实 SofaScore 评分做回归拟合,用 sklearn/PHP-ML 反推权重,误差能压到 ±0.2 以内。
  2. 门将独立模型:门将与其他位置差异极大,建议抽离成 GoalkeeperRating 类。
  3. 加入上下文修正:对手强弱、比赛重要性、比分(落后时追分表现)都是 SofaScore 会考虑的因子。

需要我进一步给出 权重拟合脚本Laravel 集成版本 吗?

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