Meu TCC precisava de todas as notas de jogadores do Brasileirão Série A 2025. O Sofascore tem, atrás da mesma API interna que o próprio site usa. Como não é uma API pública oficial, o crawler foi feito pra ser entediante: uma requisição a cada 1,2 a 2,4 segundos, novas tentativas com backoff, um checkpoint ao fim de cada rodada pra que uma queda retome em vez de recomeçar, e validação com Pydantic em cada linha antes de ela chegar no CSV.
Isso rendeu 11.977 registros de jogador por partida, de todos os 380 jogos, disputados entre 29 de março e 7 de dezembro de 2025. Coletar foi a parte fácil. Os dados tinham três armadilhas, e nenhuma delas gerou erro.
Datas no fuso errado
O crawler guardava as datas das partidas em UTC. Os jogos noturnos no Brasil começam às 21h ou mais tarde, o que já é o dia seguinte em UTC, então 67 das 380 partidas estavam com a data errada. A correção foi recalcular a data a partir do timestamp do início do jogo em America/Sao_Paulo, tanto na limpeza quanto no próprio crawler.
Três tipos de ausência
Uma célula vazia significava três coisas diferentes, e cada uma ganhou sua própria regra:
- Não se aplica. Defesas de um centroavante. Essas chaves nunca aparecem pra jogadores de linha, então recebem
-1, um valor que nenhuma contagem real pode ter. - Não medido. Estatísticas físicas (distância, sprints, velocidade máxima) existem em só 349 das 380 partidas, conduções de bola em só 83. Essas continuam ausentes. Preencher com zero seria afirmar que o jogador não correu.
- Zero, omitido. O Sofascore não envia estatísticas que valem 0. Nos dados brutos,
goalsestava vazio em 11.124 linhas eexpected_goalsem 6.314, exatamente as linhas em que o jogador não finalizou. Esses viram 0.
Dentro dos grupos de físicas e de conduções, a regra é por jogador: se ele tem qualquer métrica do grupo, as que faltam são zeros omitidos. Se não tem nenhuma, o grupo não foi medido pra ele.
Um caso de borda: goals_prevented pode ser negativo pra goleiros (de -3,79 a 3,12), então ali o sentinela -1 colide com valores reais. A coluna de posição desfaz o empate.
Rodadas fora de ordem
O jeito óbvio de ordenar uma temporada é pelo número da rodada. Está errado. Rodadas são remarcadas por causa das competições continentais, e a maior parte da rodada 34 só foi jogada em novembro, depois de as rodadas 35 a 37 terem sido jogadas em outubro. Ao longo da temporada, 1.795 dos 72.010 pares possíveis de partidas (2,49%) têm números de rodada que contradizem o horário de início.
Isso deixa de ser curiosidade assim que você calcula o histórico de um jogador. Ordenar por rodada em vez de horário muda a ordem das partidas de 335 dos 686 jogadores (48,8%) com dois jogos ou mais. Pra um modelo que só pode olhar partidas passadas, isso é vazamento: um jogo futuro se infiltrando no histórico de um jogo passado. Tudo dali pra frente ordena por kickoff_timestamp, nunca por round.
Depois da limpeza, 163 atuações sem nota foram descartadas, todas de 7 minutos ou menos. Sobraram 11.814 linhas de 732 jogadores, que foi com o que os modelos de Quando o R² é bom demais foram treinados.