Código de scraping
@Emilio
@David
Nuestro equipo se ha encargado de diseñar algoritmos que permitan extraer de manera automática información referente a las decisiones judiciales hechas por los jueces de nuestro país. Específicamente, se ha estado trabajando en un algoritmo computacional (conocido como web scraping ) que permita capturar sentencias emitidas por los órganos judiciales de cada estado.
Ahora bien, nuestra labor ha encontrado varios obstáculos como, por ejemplo: la falta de transparencia en algunos estados que se traduce en la imposibilidad de encontrar las sentencias, a pesar de que la Ley de Transparencia obliga a que todas las sentencias deben hacerse públicas. No obstante, decidimos dejar de lado esta cuestión y enfocarnos exclusivamente en las sentencias que son públicas.
Varios estados han subido a la red las sentencias, sin embargo, cada estado tiene una forma diferente de presentar información. Esto, sin duda alguna, ha sido un reto, pues para recopilar las sentencias se ha tenido que diseñar más de un algoritmo, pues la heterogeneidad de las paginas impide usar el mismo.
En consecuencia, nuestro trabajo ha consistido en utilizar herramientas de programación como Python y sus diversas librerías para crear un algoritmo o web scraping capaz de entender el repositorio de las sentencias para que, de forma automatizada, pueda capturar sentencias e información relevante para alguna persona. El objetivo final es que cualquier usuario pueda acceder a la totalidad de sentencias ahora o en el futuro de una forma rápida y sencilla.
El principal reto que enfrenta el equipo son los diseños de los algoritmos. Estos deben estar hechos a la medida de la pagina web en la que se encuentran las sentencias. Se debe comentar que las páginas web no tienen una estructura clara, lo que dificulta el proceso para producir el algoritmo, en consecuencia, se necesita más tiempo para encontrar herramientas que permitan el correcto funcionamiento del algoritmo. Este proceso requiere tiempo y repetición, ya que a medida que el algoritmo se vuelve más complejo, es más difícil de encontrar errores en el proceso.
Dicho lo anterior, podemos afirmar que hemos conseguido recopilar, a través de nuestro web scraping, sentencias del estado de Coahuila sin ningún tipo de problema. Nuestra meta es que en las siguientes semanas y meses seamos capaces de recopilar sentencias y demás información de los estados que tienen su información pública.
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
import pandas as pd
def check_exists_by_xpath ( webdriver , xpath ):
"""
Regresa True si existe el elemento con el xpath dado, sino False
"""
try :
webdriver . find_element_by_xpath ( xpath )
except NoSuchElementException :
return False
return True
def selectOpc ( driver , xpathDiv1 , xpathDivOpc , pos ):
"""
Selecciona la opción localizada en la posición pos de xpathDivOpc
driver: driver del navegador
xpathDiv1:
"""
#Damos click para que aparezca el div de opciones
# Corrijo bug -- FDM
element = driver . find_element_by_xpath ( xpathDiv1 )
driver . execute_script ( "arguments[0].click();" , element )
#Div de opciones
opcs = driver . find_element_by_xpath ( xpathDivOpc ). find_elements_by_tag_name ( "div" )
#Damos clic a la opción
try :
driver . execute_script ( "arguments[0].click();" , opcs [ pos ])
except Exception as e :
print ( str ( e ))
print ( f "Element { opcs [ pos ] } not clickable" )
def getSizeByTag ( driver , xpath , tag ):
"""
Dada un xpath busca todos los elementos con el tag dado y regresa el tamaño
"""
return len ( driver . find_element_by_xpath ( xpath ). find_elements_by_tag_name ( tag ))
def doClick ( driver , xpath ):
try :
driver . find_element_by_xpath ( xpath ). click ()
except :
print ( f " { xpath } not clickable" )
def getElementsByTag ( driver , xpath , tag ):
element = driver . find_element_by_xpath ( xpath ). find_elements_by_tag_name ( tag )
return element
def checkTable ( driver , oneTable ):
rowsPath = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody'
btnCerrarVisor = '//*[@id="modalPDF"]/div/div/div[3]/button'
visorPDF = '//*[@id="ContentPlaceHolder1_framePDF"]'
rows = getElementsByTag ( driver , rowsPath , "tr" )
pdf_links = []
if oneTable :
nr = len ( rows )
else :
nr = len ( rows ) - 1
# Get table data with beautiful soup
html = driver . page_source
sopa = BeautifulSoup ( html , 'html.parser' )
tds = sopa . find_all ( "td" , { "style" : [ "width:7%;" , "width:15%;" , "width:9%;" , "width:12%;" , "width:2%;" ]})
titles = sopa . find_all ( "th" )
lista_ds = []
i = 0
while i < len ( tds ):
d = {}
d [ titles [ 1 ]. text ] = tds [ i ]. text
d [ titles [ 2 ]. text ] = tds [ i + 1 ]. text
d [ titles [ 3 ]. text ] = tds [ i + 2 ]. text
d [ titles [ 4 ]. text ] = tds [ i + 3 ]. text
d [ titles [ 5 ]. text ] = tds [ i + 4 ]. text
d [ titles [ 6 ]. text ] = tds [ i + 5 ]. text
d [ titles [ 7 ]. text ] = tds [ i + 6 ]. text
lista_ds . append ( d )
i += 7
df = pd . DataFrame . from_dict ( lista_ds )
j = 0
while ( j < len ( df [ 'Sentencia' ])):
rows = getElementsByTag ( driver , rowsPath , "tr" )
cols = rows [ j ]. find_elements_by_tag_name ( "td" )
nCol = len ( cols )
# for y in range(nCol-1):
# print(cols[y].get_attribute("innerHTML"))
#Abrir pdf para obtener link
try :
cols [ nCol - 1 ]. click ()
except Exception as e :
print ( str ( e ))
print ( f "Element { cols [ nCol - 1 ] } not clickable" )
#Esperamos a que cargue
time . sleep ( 4 )
pdf_link = driver . find_element_by_xpath ( visorPDF ). get_attribute ( 'src' )
pdf_links . append ( pdf_link )
#Cerrar el visor
time . sleep ( 4 )
doClick ( driver , btnCerrarVisor )
j += 1
#Boton descargar
#//*[@id="download"]
time . sleep ( 8 )
df [ titles [ 7 ]. text ] = pdf_links
print ( df )
return df
#Variar Tipo de Juzgado, Delitos-Modalidades, Criterios , Tipo de resolución
#Checar el comentario del primer for (i1)
driver = webdriver . Chrome ()
driver . maximize_window ()
driver . get ( "https://www.pjenl.gob.mx/SentenciasPublicas/Modulos/Penales.aspx" )
divJuzgado = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[1]/div/div/div[1]'
divOpcJuzgado = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[1]/div/div/div[2]/div'
divDelitos = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[2]/div/div/div[1]'
divOpcDelitos = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[2]/div/div/div[2]/div'
divCriterios = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[5]/div/div/div[1]'
divOpcCriterios = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[5]/div/div/div[2]/div'
divResolucion = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[6]/div/div/div[1]'
divOpcResolucion = '//*[@id="ContentPlaceHolder1_ctl00"]/div[2]/div/div[1]/div[6]/div/div/div[2]/div'
btnBuscar = '//*[@id="ContentPlaceHolder1_BtnBuscar"]'
tabla = '//*[@id="ContentPlaceHolder1_gdvPenal"]/thead/tr/th[2]'
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/tbody/tr'
lista_dfs = []
# Tipo de juzgado ----------------------------------------------------
selectOpc ( driver , divJuzgado , divOpcJuzgado , 0 )
t1 = getSizeByTag ( driver , divOpcJuzgado , "div" )
#Si ya es la versión final hay que cambiar el for para que empiece con 1.
#Empieza en 2 para probar
for i1 in range ( 2 , t1 ):
selectOpc ( driver , divJuzgado , divOpcJuzgado , i1 )
#Delitos - Modalidades ----------------------------------------------
selectOpc ( driver , divDelitos , divOpcDelitos , 0 )
t2 = getSizeByTag ( driver , divOpcDelitos , "div" )
time . sleep ( 1 )
#Las primeras 7 son Ver todos y opciones vacías
for i2 in range ( 7 , t2 ):
selectOpc ( driver , divDelitos , divOpcDelitos , i2 )
time . sleep ( 1 )
#Criterios Específicos ----------------------------------------------
selectOpc ( driver , divCriterios , divOpcCriterios , 0 )
t3 = getSizeByTag ( driver , divOpcCriterios , "div" )
for i3 in range ( t3 ):
selectOpc ( driver , divCriterios , divOpcCriterios , i3 )
#Tipo resolución --------------------------------------------------
selectOpc ( driver , divResolucion , divOpcResolucion , 0 )
t4 = getSizeByTag ( driver , divOpcResolucion , "div" )
for i4 in range ( 1 , t4 ):
selectOpc ( driver , divResolucion , divOpcResolucion , i4 )
#Hacemos consulta -----------------------------------------------
doClick ( driver , btnBuscar )
time . sleep ( 3 )
#Pendiente Checar si ya dio el resultado en lugar de poner 3 segundos
if check_exists_by_xpath ( driver , tabla ):
#Hay tabla
if check_exists_by_xpath ( driver , '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/ tbody/tr' ):
first = True
while True :
ind = getElementsByTag ( driver , indexList , "a" )
if ind [ - 1 ]. get_attribute ( "text" ) != "..." :
break
# First sirve para saber en que index empezar, porque solo las primeras 10 son de las forma [1,..., 10, "..."]
# En ese caso empezamos en 0.
# Las demás serán ["...", n, n+1, ..., n+9, "..."] entonces hay que empezar en 1, sino se regresa a las anteriores
if first :
i = 0
first = False
else :
i = 1
start = True
while ind [ - 1 ]. get_attribute ( "text" ) == "..." :
df1 = checkTable ( driver , False )
lista_dfs . append ( df1 )
# Por alguna razón después de recorrer la tabla cambia el html y el encabezado se hace parte de las filas y deja de existir el thead
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[22]/td/table/tbody/tr'
try :
ind = getElementsByTag ( driver , indexList , "a" )
ind [ i ]. click ()
except :
pass
if ( ind [ i ]. get_attribute ( "text" ) == "..." ):
break
i += 1
#Este es opcional, hay que buscar una manera de saber cuando ya cargó la página
time . sleep ( 30 )
driver . find_element_by_tag_name ( 'body' ). send_keys ( Keys . CONTROL + Keys . HOME )
#Después de que cambiamos de página el html regresa a lo normal y el thead ya existe de nuevo
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/tbody/tr'
ind = getElementsByTag ( driver , indexList , "a" )
time . sleep ( 30 )
#Ya estamos en la última página de sentencias
df2 = checkTable ( driver , False )
lista_dfs . append ( df2 )
else :
df3 = checkTable ( driver , True )
lista_dfs . append ( df3 )
print ( lista_dfs )
lista_dfs
df_final = pd . concat ( lista_dfs , ignore_index = True )
df_final
driver . quit ()
driver = webdriver . Chrome ()
driver . maximize_window ()
driver . get ( "https://www.pjenl.gob.mx/SentenciasPublicas/Modulos/Penales.aspx" )
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/tbody/tr'
print ( "Entra" )
if check_exists_by_xpath ( driver , '//*[@id="ContentPlaceHolder1_gdvPenal"]/thead/tr/th[2]' ):
#Hay tabla
if check_exists_by_xpath ( driver , '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/tbody/tr' ):
first = True
while True :
ind = getElementsByTag ( driver , indexList , "a" )
if ind [ - 1 ]. get_attribute ( "text" ) != "..." :
break
#First sirve para saber en que index empezar, porque solo las primeras 10 son de las forma [1,..., 10, "..."]
#En ese caso empezamos en 0.
#Las demás serán ["...", n, n+1, ..., n+9, "..."] entonces hay que empezar en 1, sino se regresa a las anteriores
if first :
i = 0
first = False
else :
i = 1
start = True
while ind [ - 1 ]. get_attribute ( "text" ) == "..." :
checkTable ( driver , False )
#Por alguna razón después de recorrer la tabla cambia el html y el encabezado se hace parte
# de las filas y deja de existir el thead
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[22]/td/table/tbody/tr'
ind = getElementsByTag ( driver , indexList , "a" )
ind [ i ]. click ()
if ( ind [ i ]. get_attribute ( "text" ) == "..." ):
break
i += 1
#Este es opcional, hay que buscar una manera de saber cuando ya cargó la página
time . sleep ( 30 )
driver . find_element_by_tag_name ( 'body' ). send_keys ( Keys . CONTROL + Keys . HOME )
#Después de que cambiamos de página el html regresa a lo normal y el thead ya existe de nuevo
indexList = '//*[@id="ContentPlaceHolder1_gdvPenal"]/tbody/tr[21]/td/table/tbody/tr'
ind = getElementsByTag ( driver , indexList , "a" )
time . sleep ( 30 )
#Ya estamos en la última página de sentencias
checkTable ( driver , False )
else :
checkTable ( driver , True )