Ligas de Futbol Soccer Extrayendo datos de ESPN, Scraping con Python [Parte 1.1]
Bueno al principio no iba a colocar esta entrada debido a que encontré otra fuente mas interesante y que se presta para trabajar con toda la estadística que quiero trabajar sobre el deporte.
Pero al final quise dar a conocer algunos traspiés con tablas que se ven bien pero tarde o temprano nos pueden dar dolores de cabeza.La pagina a la que le vamos a hacer Scraping hoy es esta: futbol posiciones liga esp.1
Lo que no me gusta es que no tiene Goles en casa y de visita :S, por lo que busque otras fuentes y ademas no tiene histórico fecha a fecha que es algo me interesa y mucho :D.
Pero de los pequeños problemas podemos conseguir soluciones futuras, veamos el siguiente codigo que es mas sencillo que el visto en la entrada anterior:
import urllib2, re
from bs4 import BeautifulSoup
import re
from prettytable import PrettyTable
# url that we are scraping
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/esp.1"
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, "lxml")
table = soup.find('table')
rows = table.find_all('tr')
results = []
for row in rows:
table_headers = row.find_all('th')
if table_headers:
results.append([headers.get_text() for headers in table_headers])
table_data = row.find_all('td')
if table_data:
results.append([data.get_text() for data in table_data])
print results
La salida de esta primera etapa del programa es esta:
[[u'1Las PalmasPAL', u'2', u'2', u'0', u'0', u'9', u'3', u'+6', u'6'], [u'2BarcelonaBAR', u'2', u'2', u'0', u'0', u'7', u'2', u'+5', u'6'], [u'3Real MadridMAD', u'2', u'2', u'0', u'0', u'5', u'1', u'+4', u'6'], [u'4Sevilla FCSEV', u'2', u'1', u'1', u'0', u'6', u'4', u'+2', u'4'], [u'5Sporting Gij\xf3nGIJ', u'2', u'1', u'1', u'0', u'2', u'1', u'+1', u'4'], [u'6Deportivo La Coru\xf1aDEP', u'2', u'1', u'1', u'0', u'2', u'1', u'+1', u'4'], [u'7Legan\xe9sLegan\xe9s', u'2', u'1', u'1', u'0', u'1', u'0', u'+1', u'4'], [u'8EibarEIB', u'2', u'1', u'0', u'1', u'2', u'2', u'0', u'3'], [u'9Real SociedadSOC', u'2', u'1', u'0', u'1', u'2', u'3', u'-1', u'3'], [u'10M\xe1lagaMGA', u'2', u'0', u'2', u'0', u'3', u'3', u'0', u'2'], [u'11Atl\xe9tico MadridATL', u'2', u'0', u'2', u'0', u'1', u'1', u'0', u'2'], [u'12VillarrealVLR', u'2', u'0', u'2', u'0', u'1', u'1', u'0', u'2'], [u'13Alav\xe9sAlav\xe9s', u'2', u'0', u'2', u'0', u'1', u'1', u'0', u'2'], [u'14EspanyolESP', u'2', u'0', u'1', u'1', u'6', u'8', u'-2', u'1'], [u'15OsasunaOSA', u'2', u'0', u'1', u'1', u'1', u'3', u'-2', u'1'], [u'16GranadaGRN', u'2', u'0', u'1', u'1', u'2', u'6', u'-4', u'1'], [u'17Real BetisReal Betis', u'2', u'0', u'1', u'1', u'2', u'6', u'-4', u'1'], [u'18Celta VigoVIG', u'2', u'0', u'0', u'2', u'1', u'3', u'-2', u'0'], [u'19Athletic BilbaoBIL', u'2', u'0', u'0', u'2', u'1', u'3', u'-2', u'0'], [u'20ValenciaVAL', u'2', u'0', u'0', u'2', u'2', u'5', u'-3', u'0']]
A primera vista parece que todo esta bien, pero si miramos bien encontramos esto: [ u'1Las PalmasPAL' ] y esto si qu es un problema ya que si nos fijamos bien 1 es la posición de Las Palmas el nombre y PAL es el nombre abreviado, así que si pensamos en slicing o alternativas de cortar cadenas estamos fritos por que seria un código muy largo :S, recuerdo que en este Blog hice algo de NIF a Rif con rebanado de cadenas pero aquí no va a funcionar, el código es poco pythonico :S
Por suerte y gracias a la Especialización de Python que aun no he terminado, por que retrasaron el ultimo modulo :S, podemos usar Expresiones Regulares y bueno es precisamente lo que utilice aunque se me complico un poco igual lo del nombre al final di con la expresión correcta y este es el código final hasta el prettytables:
import urllib2, re
from bs4 import BeautifulSoup
import re
from prettytable import PrettyTable
# url that we are scraping
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/esp.1"
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, "lxml")
table = soup.find('table')
rows = table.find_all('tr')
results = []
for row in rows:
table_headers = row.find_all('th')
if table_headers:
results.append([headers.get_text() for headers in table_headers])
table_data = row.find_all('td')
if table_data:
results.append([data.get_text() for data in table_data])
numero_items = len(results)
table = PrettyTable(["Posicion", "Equipo", "Puntos", "Dif_Gol"])
i = 0
for i in range(0,numero_items):
# Agregamos expresiones regulares para formatear campos
patron = re.compile('^[0-9]+') # necesitamos la posicion en la tabla
Nombre_posi = results[i][0]
posicion1 = patron.findall(Nombre_posi)
num_posicion = posicion1[0] # lo que necesitamos
patron_nom_completo = re.compile('[^0-9]+[^A-Z]') #Necesitamos El nombre
nombre_completo = patron_nom_completo.findall(Nombre_posi)
nombre_equipo = nombre_completo[0]
table.add_row([num_posicion, nombre_equipo, results[i][8] , results[i][7]])
print table
Si nos fijamos en el código hay varios comentarios que hacen referencia a las expresiones regulares agregados por lo motivos antes señalados, Sin embargo esta es la salida:
+----------+----------------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+----------------------+--------+---------+
| 1 | Las Palmas | 6 | +6 |
| 2 | Barcelona | 6 | +5 |
| 3 | Real Madrid | 6 | +4 |
| 4 | Sevilla | 4 | +2 |
| 5 | Sporting Gijón | 4 | +1 |
| 6 | Deportivo La Coruña | 4 | +1 |
| 7 | LeganésLeganés | 4 | +1 |
| 8 | Eibar | 3 | 0 |
| 9 | Real Sociedad | 3 | -1 |
| 10 | Málaga | 2 | 0 |
| 11 | Atlético Madrid | 2 | 0 |
| 12 | Villarreal | 2 | 0 |
| 13 | AlavésAlavés | 2 | 0 |
| 14 | Espanyol | 1 | -2 |
| 15 | Osasuna | 1 | -2 |
| 16 | Granada | 1 | -4 |
| 17 | Real BetisReal Betis | 1 | -4 |
| 18 | Celta Vigo | 0 | -2 |
| 19 | Athletic Bilbao | 0 | -2 |
| 20 | Valencia | 0 | -3 |
+----------+----------------------+--------+---------+
Y esta no es la salida que quiero, por que tiene 2 errores y es que nuestra expresión regular no dio exactamente con lo que necesitamos [Real BetisReal Betis y LeganésLeganés ].
Aunque la culpa no es nuestra quizá por que el formato esta bien hasta llegar a estos 2 equipos que usan su nombre como nombre abreviado lo que nos hace a nosotros un problema, me decidí agregar un par de condicionales para modificar a estos 2 y salir del problema, si alguien tiene una mejor solución bienvenida, el código final para la liga española es esta:
import urllib2, re
from bs4 import BeautifulSoup
import re
from prettytable import PrettyTable
# url that we are scraping
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/esp.1"
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, "lxml")
table = soup.find('table')
rows = table.find_all('tr')
results = []
for row in rows:
table_headers = row.find_all('th')
if table_headers:
results.append([headers.get_text() for headers in table_headers])
table_data = row.find_all('td')
if table_data:
results.append([data.get_text() for data in table_data])
print results
numero_items = len(results)
table = PrettyTable(["Posicion", "Equipo", "Puntos", "Dif_Gol"])
i = 0
for i in range(0,numero_items):
patron = re.compile('^[0-9]+')
Nombre_posi = results[i][0]
posicion1 = patron.findall(Nombre_posi)
num_posicion = posicion1[0]
patron_nom_completo = re.compile('[^0-9]+[^A-Z]')
nombre_completo = patron_nom_completo.findall(Nombre_posi)
nombre_equipo = nombre_completo[0]
### Agregue estos condicionales para tener un mejor formato pero puede haber una mejor solucion
if "Real Betis" in nombre_equipo:
nombre_equipo = "Real Betis"
elif u'Legan\xe9s' in nombre_equipo:
nombre_equipo = u'Legan\xe9s'
table.add_row([num_posicion, nombre_equipo, results[i][8] , results[i][7]])
print table
Y ahora la salida es esta:
+----------+---------------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+---------------------+--------+---------+
| 1 | Las Palmas | 6 | +6 |
| 2 | Barcelona | 6 | +5 |
| 3 | Real Madrid | 6 | +4 |
| 4 | Sevilla | 4 | +2 |
| 5 | Sporting Gijón | 4 | +1 |
| 6 | Deportivo La Coruña | 4 | +1 |
| 7 | Leganés | 4 | +1 |
| 8 | Eibar | 3 | 0 |
| 9 | Real Sociedad | 3 | -1 |
| 10 | Málaga | 2 | 0 |
| 11 | Atlético Madrid | 2 | 0 |
| 12 | Villarreal | 2 | 0 |
| 13 | AlavésAlavés | 2 | 0 |
| 14 | Espanyol | 1 | -2 |
| 15 | Osasuna | 1 | -2 |
| 16 | Granada | 1 | -4 |
| 17 | Real Betis | 1 | -4 |
| 18 | Celta Vigo | 0 | -2 |
| 19 | Athletic Bilbao | 0 | -2 |
| 20 | Valencia | 0 | -3 |
+----------+---------------------+--------+---------+
Ahora si, pero habíamos dicho que este código era para varias ligas veamos 3 salidas mas para las urls, ojo en la liga Alemana hay empates y no sale el numero que debería salir en la cadena, por lo que posición debería ser igual a i +1 Lo iba a colocar desde el principio pero me parecía mas instructivo asi :D.... Ahora veamos el nuevo código:
import urllib2, re
from bs4 import BeautifulSoup
import re
from prettytable import PrettyTable
# url that we are scraping
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/ger.1"
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, "lxml")
table = soup.find('table')
rows = table.find_all('tr')
results = []
for row in rows:
table_headers = row.find_all('th')
if table_headers:
results.append([headers.get_text() for headers in table_headers])
table_data = row.find_all('td')
if table_data:
results.append([data.get_text() for data in table_data])
print results
numero_items = len(results)
table = PrettyTable(["Posicion", "Equipo", "Puntos", "Dif_Gol"])
i = 0
for i in range(0,numero_items):
patron = re.compile('^[0-9]+')
Nombre_posi = results[i][0]
posicion1 = patron.findall(Nombre_posi)
num_posicion = i + 1 # este deberia ser el deber ser siempre :D
patron_nom_completo = re.compile('[^0-9]+[^A-Z]')
nombre_completo = patron_nom_completo.findall(Nombre_posi)
nombre_equipo = nombre_completo[0]
### Agregue estos condicionales para tener un mejor formato pero puede haber una mejor solucion
if "Real Betis" in nombre_equipo:
nombre_equipo = "Real Betis"
elif u'Legan\xe9s' in nombre_equipo:
nombre_equipo = u'Legan\xe9s'
table.add_row([num_posicion, nombre_equipo, results[i][8] , results[i][7]])
i += 1
print table
Lo que le hemos agregado [if (i + 1) in posicion1:], ya con esto podemos recorrer cada liga incluso hasta otras que tengan este formato :D
#Para la liga española - Liga BBVA
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/esp.1"
# Para la liga inglesa - La premier
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/eng.1"
# Para la Liga Alemana - Bundesliga
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/ger.1"
# Para la Liga Italiana - Serie A
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/ita.1"
Claro podemos crear un bucle for para cada liga :D, pero ahora no por que este código no es el principal por eso es la parte 1.1Aqui las salidas por liga de acuerdo a la url usada.
Para la Española:
# Para la liga Española - La Liga BBVA
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/esp.1"
+----------+---------------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+---------------------+--------+---------+
| 1 | Las Palmas | 6 | +6 |
| 2 | Barcelona | 6 | +5 |
| 3 | Real Madrid | 6 | +4 |
| 4 | Sevilla | 4 | +2 |
| 5 | Sporting Gijón | 4 | +1 |
| 6 | Deportivo La Coruña | 4 | +1 |
| 7 | Leganés | 4 | +1 |
| 8 | Eibar | 3 | 0 |
| 9 | Real Sociedad | 3 | -1 |
| 10 | Málaga | 2 | 0 |
| 11 | Atlético Madrid | 2 | 0 |
| 12 | Villarreal | 2 | 0 |
| 13 | AlavésAlavés | 2 | 0 |
| 14 | Espanyol | 1 | -2 |
| 15 | Osasuna | 1 | -2 |
| 16 | Granada | 1 | -4 |
| 17 | Real Betis | 1 | -4 |
| 18 | Celta Vigo | 0 | -2 |
| 19 | Athletic Bilbao | 0 | -2 |
| 20 | Valencia | 0 | -3 |
+----------+---------------------+--------+---------+
Para la Inglesa:
# Para la liga inglesa - La premier
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/eng.1"
+----------+----------------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+----------------------+--------+---------+
| 1 | Manchester City | 9 | +6 |
| 2 | Chelsea | 9 | +5 |
| 3 | Manchester United | 9 | +5 |
| 4 | Everton | 7 | +2 |
| 5 | Hull City | 6 | +2 |
| 6 | Middlesbrough | 5 | +1 |
| 7 | Tottenham Hotspur | 5 | +1 |
| 8 | Arsenal | 4 | +1 |
| 9 | Leicester City | 4 | 0 |
| 10 | West Bromwich Albion | 4 | 0 |
| 11 | Liverpool | 4 | -1 |
| 12 | West Ham United | 3 | -2 |
| 13 | Burnley | 3 | -2 |
| 14 | Swansea City | 3 | -2 |
| 15 | Southampton | 2 | -2 |
| 16 | Sunderland | 1 | -2 |
| 17 | Crystal Palace | 1 | -2 |
| 18 | Watford | 1 | -3 |
| 19 | AFC Bournemouth | 1 | -3 |
| 20 | Stoke City | 1 | -4 |
+----------+----------------------+--------+---------+
Para la Bundesliga:
# Para la liga Alemana - Bundesliga
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/ger.1"
+----------+--------------------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+--------------------------+--------+---------+
| 1 | Bayern Munich | 3 | +6 |
| 2 | VfL Wolfsburg | 3 | +2 |
| 3 | FC Cologne | 3 | +2 |
| 4 | Borussia Dortmund | 3 | +1 |
| 5 | Hertha Berlin | 3 | +1 |
| 6 | Borussia Monchengladbach | 3 | +1 |
| 7 | Eintracht Frankfurt | 3 | +1 |
| 8 | RB Leipzig | 1 | 0 |
| 9 | TSG Hoffenheim | 1 | 0 |
| 10 | FC Ingolstadt 0 | 1 | 0 |
| 11 | Hamburg | 1 | 0 |
| 12 | Mainz | 0 | -1 |
| 13 | SC Freiburg | 0 | -1 |
| 14 | Bayer Leverkusen | 0 | -1 |
| 15 | Schalke 0 | 0 | -1 |
| 16 | SV Darmstadt 9 | 0 | -2 |
| 17 | F. C. Augsburgo | 0 | -2 |
| 18 | Werder Bremen | 0 | -6 |
+----------+--------------------------+--------+---------+
Para la Italiana:
# Para la liga Italiana - Serie A
url = "http://www.espn.com.ve/futbol/posiciones/_/liga/ita.1"
+----------+----------------+--------+---------+
| Posicion | Equipo | Puntos | Dif_Gol |
+----------+----------------+--------+---------+
| 1 | Génova | 6 | +4 |
| 2 | Juventus | 6 | +2 |
| 3 | Sampdoria | 6 | +2 |
| 4 | AS Roma | 4 | +4 |
| 5 | US Pescara | 4 | +3 |
| 6 | Napoli | 4 | +2 |
| 7 | Torino | 3 | +3 |
| 8 | Chievo Verona | 3 | +1 |
| 9 | Lazio | 3 | 0 |
| 10 | Fiorentina | 3 | 0 |
| 11 | AC Milan | 3 | -1 |
| 12 | Udinese | 3 | -2 |
| 13 | Sassuolo | 3 | -2 |
| 14 | Bolonia | 3 | -3 |
| 15 | Palermo | 1 | -1 |
| 16 | Cagliari | 1 | -2 |
| 17 | Internazionale | 1 | -2 |
| 18 | Atalanta | 0 | -2 |
| 19 | Crotone | 0 | -3 |
| 20 | Empoli | 0 | -3 |
+----------+----------------+--------+---------+
Bueno son salidas Viejas en la Proxima entrada aprovecho y coloco las salidas nuevas, chao.
Éxito en tu trabajo y Que Dios te bendiga recuerda que en Dios nuestros pasos son mas seguros :D
Great job ..Thank you for sharing :)
interesante bro, donde lo volcabas luego en una app?
Jajajaja no, quiza en una pagina de estadisticas diferentes en un futuro :D