Como fazer Web Scraping em Python

Uma das melhores maneiras de se praticar suas habilidades de Data Science e construir o seu portfĂłlio, ĂŠ trabalhando em projetos pessoais. Um dos melhores jeitos de encontrar um projeto para fazer ĂŠ com algo relacionado a seus gostos pessoais, como por exemplo, algum esporte.
Para trabalhar em seu projeto, ĂŠ possĂvel utilizar a seção de datasets do Kaggle para verificar se jĂĄ existe uma base do jeito que vocĂŞ quer. Caso nĂŁo exista, ĂŠ possĂvel extraĂ-los do jeito que quiser de sites da Internet com o uso de tĂŠcnicas de Web Scraping.
Uma boa opção para estatĂsticas esportivas sĂŁo os sites do grupo Sports Reference. Eles trazem vĂĄrios tipos de estatĂsticas, desde as mais simples atĂŠ as avançadas sobre os mais variados esportes, como Basquete, Baseball e Futebol Americano, do universitĂĄrio ao profissional das mais diferentes ligas.

Como exemplo, usarei o site de Basquete para extrair dados da NBA, porÊm com os conhecimentos deste tutorial você serå capaz de extrair informaçþes de qualquer um dos outros sites com pequenas modificaçþes, tendo em vista que por serem da mesma empresa eles possuem o mesmo padrão.
Todo o cĂłdigo desse tutorial pode ser acessado nesse gist.
Bibliotecas utilizadas:
requests
: biblioteca para execução de requisiçþes HTTP;BeautifulSoup
: biblioteca para extração de dados em arquivos HTML e XML;Pandas
: biblioteca para armazenar, limpar e salvar os dados em forma de tabela.
Usaremos as bibliotecas acima da seguinte forma: Usaremos arequests
para executar requisiçþes GET e obter o código HTML das påginas que queremos; depois, utilizaremos a BeautifulSoup
para extrair os dados que queremos destas pĂĄginas; por fim, salvaremos esses dados em um Data Frame do Pandas
.
Extraindo tabelas de estatĂsticas
Como primeiro exemplo, vamos utilizar a pĂĄgina de EstatĂsticas Individuais Totais da temporada de 2017/2018 da NBA. Esta pĂĄgina contĂŠm uma Ăşnica tabela, onde cada linha representa um jogador e as respectivas informaçþes deles na Ăşltima temporada da NBA, como por exemplo a quantidade de minutos jogados e arremessos convertidos.
Utilizando a ferramenta de inspeção do navegador na tabela, podemos verificar que os dados estão armazenados em uma tabela HTML, representado pela tag table
.

Sabendo qual elemento devemos extrair da pĂĄgina HTML para conseguir os dados, ĂŠ possĂvel começar a parte da programação!
Primeiro, devemos importar a bibliotecas que serĂŁo usadas:
import pandas as pd
import requests
from bs4 import BeautifulSoup
O primeiro passo serå fazer uma requisição GET para a pågina. Para isso, devemos chamar o mÊtodo requests.get
com a URL da pĂĄgina como argumento.
Este mÊtodo retorna um objeto Response que contÊm vårios informaçþes, das quais utilizaremos o status_code
para verificar se a requisição retornou um status 200, indicando que ela foi bem sucedida, e content
para acessar o cĂłdigo da pĂĄgina HTML.
req = requests.get('https://www.basketball-reference.com/leagues/NBA_2018_totals.html')if req.status_code == 200:
print('Requisição bem sucedida!')
content = req.content
Depois de obter o HTML da pĂĄgina, podemos utilizar a biblioteca BeautifulSoup
para extrair a tabela. Primeiro, devemos criar um objeto que irĂĄ salvar o documento de maneira estruturada de acordo com as tags, e depois podemos acessar o elemento que quisermos chamando o mĂŠtodo find
passando como argumento o nome da tag, no caso table
.
soup = BeautifulSoup(content, 'html.parser')
table = soup.find(name='table')
Agora que temos o cĂłdigo HTML da tabela, podemos utilizar o Pandas
para carregar os dados em um Data Frame, utilizando o mĂŠtodo read_html
. Para isto, existem dois pontos para ficar atento: o primeiro ĂŠ que antes de passar a variĂĄvel âtableâ na função, devemos convertĂŞ-la para string primeiro, tendo em vista que no momento ela ĂŠ um objeto do BeautifulSoup
; o segundo Ê que o retorno deste mÊtodo Ê sempre uma lista de Data Frames, e portanto devemos acessar a posição 0 dela para obter nossa tabela.
table_str = str(table)
df = pd.read_html(table_str))[0]
E se a pĂĄgina tiver mais de uma tabela?
No caso anterior, a pĂĄgina continha somente uma tabela, entĂŁo havia somente uma tag table
para extrair. No entanto, em muitas pĂĄginas existem vĂĄrias tabelas, como ĂŠ o caso da pĂĄgina de Classificaçþes da temporada de 2017/2018 da NBA, por exemplo. Neste caso, para obter uma tabela especĂfica existem duas opçþes:
- A primeira ĂŠ substituir o mĂŠtodo
find
pelofind_all
, que retorna uma lista de todos os elementos encontrados ao invĂŠs de um sĂł. Nesse caso, ĂŠ possĂvel acessar a tabela desejada verificando em qual posição do vetor ela se encontra. - A segunda ĂŠ utilizar o argumento
attrs
do mĂŠtodofind
, passando um dicionĂĄrio que indica quais atributos o elemento obrigatoriamente deve ter para ser extraĂdo. Por exemplo, considerando a pĂĄgina de classificaçþes citada acima e que queremos extrair as colocaçþes dos times na conferĂŞncia Oeste (Western Conference), usamos o inspetor para verificar que o id dessa tabela ĂŠ âconfs_standings_Wâ. Portanto, o cĂłdigo ficaria da seguinte forma:
table = soup.find(name='table', attrs={'id':'confs_standings_W'})

Obtendo estatĂsticas de vĂĄrias temporadas
Agora que conseguimos extrair dados de uma Ăşnica pĂĄgina, seria interessante obter dados de vĂĄrias temporadas de uma sĂł vez. Comparando a URL de EstatĂsticas de 2018 com a de 2017, podemos ver que elas sĂŁo iguais, com exceção do nĂşmero do ano da temporada.
Com isso, ĂŠ possĂvel criar um loop que itere sobre uma lista de anos incluindo eles na URL e repetindo o processo da seção anterior para cada uma delas, montando uma grande tabela. Para obter uma lista de todos os anos em um certo intervalo, podemos usar a função range
nativa do Python.
O cĂłdigo a seguir cria uma função que faz isso automaticamente, e usa ela para extrair as estatĂsticas totais de 2013 a 2018. Perceba que uma coluna Year
ĂŠ criada em cada extração para que seja possĂvel diferenciar de qual ano cada estatĂstica pertence no DataFrame principal.
def scrape_stats(base_url, year_start, year_end):
years = range(year_start,year_end+1,1)
final_df = pd.DataFrame()
for year in years:
print('Extraindo ano {}'.format(year))
req_url = base_url.format(year)
req = requests.get(req_url)
soup = BeautifulSoup(req.content, 'html.parser')
table = soup.find('table', {'id':'totals_stats'})
df = pd.read_html(str(table))[0]
df['Year'] = year final_df = final_df.append(df) return final_dfurl = 'https://www.basketball-reference.com/leagues/NBA_{}_totals.html'df = scrape_stats(url, 2013, 2018)
Pequeno exemplo de uso dos dados
Primeiro, algo importante a se fazer ĂŠ fazer uma pequena limpeza dos dados. Olhando a tabela no site, podemos ver que os nomes das colunas se repetem vĂĄrias vezes no meio dela. Podemos eliminar essas linhas do Data Frame da seguinte maneira:
drop_indexes = df[df['Rk'] == 'Rk'].index # Pega indexes onde a coluna 'Rk' possui valor 'Rk'
df.drop(drop_indexes, inplace=True) # elimina os valores dos index passados da tabela
Outra coisa que tem que ser feita ĂŠ converter os valores que representam nĂşmeros na tabela, pois quando o Panda pega a tabela do HTML, todos os dados sĂŁo lidos como objetos.
numeric_cols = df.columns.drop(['Player','Pos','Tm'])
df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric)
Depois, ĂŠ possĂvel utilizar o Pandas
em conjunto com bibliotecas comoMatplotlib
eSeaborn
para gerar visualizaçþes de dados interessantes.
Por exemplo, o grĂĄfico a seguir mostra a mĂŠdia de bolas de 3 pontos arremessadas por ano na NBA.
import matplotlib.pyplot as plt
import seaborn as snssns.barplot(x=âYearâ, y=â3PAâ)

Perceba como o número de bolas de 3 arremessadas aumentou consideravelmente nos últimos anos, indo de aproximadamente 90 para 120 entre 2013 e 2017, aonde atingiu uma estabilidade. Podemos concluir que nos últimos anos esse tipo de arremesso passou a ter cada vez mais importância no jogo.
JĂĄ que o volume de bolas de 3 aumentou consideravelmente, quais sĂŁo os jogadores que mais acertaram nesse perĂodo? Vamos verificar o top 5 de arremessadores com manipulaçþes do Pandas
.

Uau! O jogador Stephen Curry, da equipe do Golden State Warriors, ocupa 4 das 5 primeiras posiçþes do top 5, seguido de seu companheiro de equipe Klay Thompson! NĂŁo ĂŠ a toa que ele jĂĄ ĂŠ considerado um dos melhores arremessadores da histĂłria, e que seu time ganhou 3 dos 6 campeonatos disputados nesse perĂodo, alĂŠm de uma campanha histĂłrica de 73 vitĂłrias e somente 9 derrotas na Temporada Regular!
E se somarmos todas as bolas de 3 convertidas por jogadores nesse perĂodo, como ficaria o top 5?

Podemos ver que Curry lidera com uma boa folga do segundo colocado, James Harden, e com uma quantidade menor de arremessos, o que mostra que ele foi mais eficiente do que Harden. AlĂŠm disso, podemos ver que tanto Curry quanto Harden arremessam muito mais bolas de 3 comparado aos outros jogadores no top 5.
Concluindo
Espero que este tutorial tenha te ajudado a entender como funciona o processo de Web Scraping e te inspire a começar seus projetos pessoais de Data Science sobre seu esporte favorito!
AlÊm de gerar vårias visualizaçþes de Dados interessantes, outra possibilidade seria gerar modelos de Machine Learning, para prever:
- qual time tem mais chances de ser campeĂŁo;
- quais serão os Novatos com maior evolução com base nos dados históricos;
- comparar jogadores de diferentes perĂodos de acordo com seu estilo de jogo;
- quem serĂĄ o melhor jogador da temporada (PrĂŞmio de MVP).
Caso tenha alguma sugestĂŁo ou dĂşvida, fique a vontade para comentar ou me adicionar no LinkedIn para conversar sobre o artigo.
AtĂŠ mais!
WRITTEN BY
Data Scientist @ IBM
Blog oficial da comunidade Data Hackers
ComentĂĄrios
Postar um comentĂĄrio