Ligas de Futbol Soccer Extrayendo datos de ESPN, Scraping con Python [Parte 1.1]

in #spanish8 years ago

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


Sort:  

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