Hola, les dejo la solución de los títulos y en mi caso también de los links.
Por algún motivo el response toma el tag h2 como text-fill.
Introducción al web scraping
¿Qué es el web scraping?
¿Por qué aprender web scraping hoy?
Python: el lenguaje más poderoso para extraer datos
Fundamentos de la web
Entender HTTP
¿Qué es HTML?
Robots.txt: permisos y consideraciones al hacer web scraping
XML Path Language
XML Path Language
Tipos de nodos en XPath
Expresiones en XPath
Predicados en Xpath
Operadores en Xpath
Wildcards en Xpath
In-text search en Xpath
XPath Axes
Resumen de XPath
Aplicando lo aprendido
Proyecto: scraper de noticias
Un proyecto para tu portafolio: scraper de noticias
Construcción de las expresiones de XPath
Obteniendo los links de los artículos con Python
Guardando las noticias en archivos de texto
Conclusiones
Cómo continuar tu ruta de aprendizaje
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 92
Preguntas 36
Hola, les dejo la solución de los títulos y en mi caso también de los links.
Por algún motivo el response toma el tag h2 como text-fill.
Excelente día veo que aun no hay solución al problema de no generar los archivos, esto se debe a un problema con el título de la noticia, lo que regresa es una lista vacía, y por eso entra en el “except”, sin embargo en la propia URL de la noticia se encuentra el título, por lo tanto e creado una pequeña función que extrae dicho título.
Es la solución que encontré, sin embargo seria bueno que alguien que sepa más del tema pueda ayudar con este error
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//a[contains(@class,"kicker")]/@href'
#XPATH_TITLE = Titulo
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY ='//div[@class="html-content"]//text()'
def get_title(link):
#separamos por "/" y nos quedamos con el ultimo que elemento
url = link.split('/')[-1]
#separamos por "-" y eliminamos el ultimo elemento
title_list=url.split('-')[:-1]
#Unimos lo anterior
title = " ".join(title_list)
return(title)
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = get_title(link)
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
print("as")
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Les comparto mi repositorio con mejoras en el proyecto del curso aquí.
Modularize el código y ahora se puede scrapear varios sitios.
¡Saludos!
PROBLEMA CARPETA SE CREA PERO NO SE GUARDAN LOS ARCHIVOS.
Todo funcionaba bien el momento de crear la carpeta, pero los archivos no se creaban dentro. El problema estaba en uno de los XPATH. Originalmente estaba usando las siguientes constantes:
XPATH_LINK_TO_ARTICLE = '//a[@class="globoeconomiaSect"]/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/h2/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
Después de buscar entre los aportes de la comunidad encontré un ejemplo que me ayudó.
Sin modificar mucho, realicé un cambio en la constante XPATH_TITLE y quedo de la siguiente manera.
#ANTES
XPATH_TITLE = '//div[@class="mb-auto"]/h2/span/text()'
#DESPUES
XPATH_TITLE = '//div[@class="mb-auto"]//span/text()'
Lo que hice fue remover la etiqueta h2 de la sentencia. Sin embargo hacer este ajuste implicó modificar el índice en la siguiente línea.
#Antes
title = parsed.xpath(XPATH_TITLE)[0]
#Despues
title = parsed.xpath(XPATH_TITLE)[1]
Este cambio se debe a que la nueva estructura de XPATH_TITLE devuelve dos elementos y el título es el segundo, por esta razón utilicé el índice [1].
…
Utilicé el código tal cual se desarrolló en la clase solo realice estos dos ajustes para que me funcionara.
RESUMEN:Guardando las noticias en archivos de texto
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Vamos a realizar una lógica para ir de cada link al sitio de cada noticia y de ahí extraer:
import requests
import lxml.html as html
# Lo usaremso para crear una carpeta
import os
# Lo usaremos para manipular fechas.
import datetime
# Creamos constantes
HOME_URL = 'https://www.larepublica.co/'
#Recuerda que tu Xpath puede variar.
XPATH_LINK_TO_ARTICLE = '//text-fill[not(@class)]/a/@href'
XPATH_LINK_TO_TITLE = '//div[@class="mb-auto"]/h2/a/text()'
XPATH_LINK_TO_SUMMARY = '//div[@class="wrap-post col-9"]/div/div[@class="lead"]/p/text()'
XPATH_LINK_TO_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
# Traigo el docuemnto html de la noticia.
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
#Quiero traer el título, el cuerpo y el resumen, hago una validación
# try -except, estoy haciendolo para los índices de la lista.
# Pueden haber noticias con nodos faltantes por lo que arrojará un error
try:
#Traemos el primer elemento de la lista.
title = parsed.xpath(XPATH_LINK_TO_TITLE)[0]
# No es deseable tener comillas en los títulos porque presentan un error en OS.
# Para solucionar esto, hacemos uso de que title es un str y del metodo replace() title = title
title = title.replace('\"','')
summary = parsed.xpath(XPATH_LINK_TO_SUMMARY)[0]
body = parsed.xpath(XPATH_LINK_TO_BODY)
except IndexError:
return
# Guardamos en un archivo
# with es un manejador contextual. Si algo sucede y el script se cierra, mantiene las cosas
# de manera segura y así no se corrompe el archivo.
with open(f'{today}/{title}.txt','w', encoding ='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
# Creamos las funcioens para ejecutar el script.
def parse_home():
# Creamos un bloque try para manejar los errores. Y manejar los Status Code.
try:
response = requests.get(HOME_URL)
# Aqui va la lógica para traer los links.
if response.status_code == 200:
# .content trae el HTML que necesita ser traducido con un decode para que python lo entienda
# en terminos de caracteres, me devuelve un string que no es más que el HTML crudo.
home = response.content.decode('utf-8')
# home = response.text
# print(home)
# En esta línea uso el parser html para transformar el contentido
# html a un archivo que sea de utilidad para las expresiones xpath
parsed = html.fromstring(home)
print(parsed)
# En esta línea estoy usando el archivo parseado con la función xpath y le paso por parámetro mi constante
# la cual almacena la expresión Xpath.
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# La línea de código me arroja un array vacío. Pero en google si lo enseña.
print(len(links_to_notices)) # Depende de tu Xpath y la página web.
print(type(links_to_notices)) # Tipo lista
print(links_to_notices)
# Traigo una fecha con la función fecha. Y Today, la fecha del dái de hoy. La variable today
# se almacena un objeto de tipo fecha, pero nos interesa más tener una cadena de caracteres que contenga la fecha
# en determinado formato que será guardado en la carpeta y con la función strftime logramos esto
today = datetime.date.today().strftime('%d-%m-%Y')
# Este condicional sirve para decirle que si no existe una carpeta con la fehca del día de hoy
# me cree una carpeta.
if not os.path.isdir(today):
os.mkdir(today)
# Creo la función para recorrer la lista de links y ejecuto en cada ciclo la función parse_notice()
for link in links_to_notices:
parse_notice(link,today)
else:
#Elevamos el error para ser capturado en el try-except, too lo que sea un error.
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Les compartomi codigo realizado en Abrl 15 (He visto difernecia entre varios compañeros en la estructura de la pagina). Como varios tuve problemas con los h2 que se solucionó reemplazando por text-fill. Así mismo tuve problemas con otras etiquetas que soluciones haciendo la expresión regular lo mas sencilla posible.
Saludos a todos.
<code>
import requests
import lxml.html as html
import os
import datetime
HOME_URL='https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE= '//text-fill/a[@class="economiaSect" or @class="empresasSect" or @class="ocioSect" or @class="globoeconomiaSect" or @class="analistas-opinionSect"]/@href'
XPATH_TITTLE='//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY='//div[@class ="lead"]/p/text()'
XPTAH_BODY='//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link,today):
try:
response=requests.get(link)
if response.status_code==200:
notice=response.content.decode('utf-8')
parsed=html.fromstring(notice)
try:
tittle=parsed.xpath(XPATH_TITTLE)[0]
print(tittle)
tittle=tittle.replace('\"','')
summary=parsed.xpath(XPATH_SUMMARY)[0]
body=parsed.xpath(XPTAH_BODY)
except IndexError:
return
with open(f'{today}/{tittle}.txt','w',encoding='utf-8') as f:
f.write(tittle)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response= requests.get(HOME_URL)
if response.status_code==200:
home=response.content.decode('utf-8')
parsed=html.fromstring(home)
links_to_notices=parsed.xpath(XPATH_LINK_TO_ARTICLE)
today=datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__=='__main__':
run()
02/2022
Para la solución removí el tag h2 del xpath titile, de resto todo continuo igual a la clase.
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//text-fill[not(@class)]/a/@href'
XPATH_TITTLE = '//div[@class="mb-auto"]//span/text()'
XPATH_SUMMARY = '//div[@class = "lead"]/p/text()'
XPATH_BODY = '//div[@class = "html-content"]/p[not (@class)]/text()'
def parse_notice(link,today):
try:
response=requests.get(link)
if response.status_code==200:
notice=response.content.decode('utf-8')
parsed=html.fromstring(notice)
try:
tittle=parsed.xpath(XPATH_TITTLE)[0]
print(tittle)
tittle=tittle.replace('\"','')
summary=parsed.xpath(XPATH_SUMMARY)[0]
body=parsed.xpath(XPATH_BODY )
except IndexError:
return
with open(f'{today}/{tittle}.txt','w',encoding='utf-8') as f:
f.write(tittle)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response= requests.get(HOME_URL)
if response.status_code==200:
home=response.content.decode('utf-8')
parsed=html.fromstring(home)
links_to_notices=parsed.xpath(XPATH_LINK_TO_ARTICLE)
today=datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__=='__main__':
run()
Funcional al 3/1/2023
saque lo del titulo del comentario de alonmar
y de Pamela Pimentel lo de remplazar el h2
import requests
import lxml.html as html
import os
import datetime
#Url de la pagina
HOME_URL = 'https://www.larepublica.co'
#url del articulo
XPATH_LINK_TO_ARTICLE = '//text-fill/a/@href'
#Extraer de cada articulo
XPATH_TITTLE='//div/text-fill/span/text()'
XPATH_SUMMARY='//*[@id="proportional-anchor-1"]/div/div/p/text()'
XPATH_BODY='//div/div[4]/p/text()'
def get_title(link):
#separamos por "/" y nos quedamos con el ultimo que elemento
url = link.split('/')[-1]
#separamos por "-" y eliminamos el ultimo elemento
title_list=url.split('-')[:-1]
#Unimos lo anterior
title = " ".join(title_list)
return(title)
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code==200:
notice = response.content.decode('utf-8') #Guarda el contenido
parsed = html.fromstring(notice)
try:
tittle = get_title(link) #obtiene el titulo
summary = parsed.xpath(XPATH_SUMMARY)[0] #get te summary
body = parsed.xpath(XPATH_BODY) #get the body
except IndexError as ie:
print(ie)
return
try:
with open(f'{today}/{tittle}.txt', 'w', encoding='utf-8') as f:
f.write(tittle)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
if p.endswith('.'):
f.write(p)
f.write('\n')
else:
f.write(p)
except:
print('no se pudo escribir')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
#Extraer links
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200: #si la conexion es correcta
home = response.content.decode('utf-8') #guarda en formato leible
parsed = html.fromstring(home) #parsea
links_to_notice = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notice)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notice:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
06 de noviembre 2021
Con esto me funcionó:
HOME_URL ='https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//text-fill/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
El resto, todo es igual 😃
Xpath a 14 de agosto de 2022
En Body se usa descendant-or-self:: * para poder extraer TODO el texto ya que las palabras resaltadas en algunos artículos no son tomadas en la extracción.
from string import printable
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = "//text-fill/a/@href"
XPATH_TITLE = "//div[@class='mb-auto']/h2/span/text()"
XPATH_SUMMARY = "//div[@class='lead']/p/text()"
XPATH_BODY = "//div[@class='html-content']/p/descendant-or-self::*/text()" #to do: check how to extract the u html
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8') #save the content of the page in a variable
parsed = html.fromstring(notice) #parse the html content
try:
title = parsed.xpath(XPATH_TITLE)[0] #get the title of the article
title.replace('\"', '') #remove the quotes from the title
title = title.replace('\'', '') #remove the quotes from the title
title = title.replace('\n', '') #remove the newlines from the title
title = title.replace('\t', '') #remove the tabs from the title
title = title.replace('\r', '') #remove the carriage returns from the title
title = title.strip() #remove the whitespaces from the title
final_title = title
title = title.replace('á', 'a').replace('é', 'e').replace('í', 'i').replace('ó', 'o').replace('ú', 'u') #replace the accents from the title
title = title.replace('Á', 'A').replace('É', 'E').replace('Í', 'I').replace('Ó', 'O').replace('Ú', 'U') #replace the accents from the title
title = title.replace('?', '').replace('¿', '').replace('!', '').replace('¡', '') #remove the question marks and exclamation marks from the title
title = title.replace(':', '').replace(';', '').replace(',', '').replace('.', '').replace('(', '').replace(')', '') #remove the colons, semicolons, commas, dots, parentheses and spaces from the title
title = title.replace('%', '').replace('$', '').replace('#', '').replace('@', '').replace('&', '').replace('*', '').replace('+', '').replace('=', '').replace('-', '').replace('_', '').replace('/', '').replace('\\', '').replace('|', '').replace('<', '').replace('>', '').replace('"', '').replace('\'', '') #remove the special characters from the title
summary = parsed.xpath(XPATH_SUMMARY)[0] #get the summary of the article
body = parsed.xpath(XPATH_BODY) #get the body of the article
except IndexError as ie:
print(ie)
return
try:
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as file:
file.write(final_title)
file.write('\n\n')
file.write(summary)
file.write('\n\n')
for p in body:
file.write(p)
file.write('\n')
except:
print('No se pudo escribir el archivo')
else:
raise ValueError(f'Error {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8') #save the content of the page in a variable
parsed = html.fromstring(home) #parse the html content
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE) #get the links to the articles
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y') #get the current date
if not os.path.isdir(today): #if the directory doesn't exist, create it
os.mkdir(today)
for link in links_to_notices: #for each link
parse_notice(link, today)
else :
raise ValueError(f'Error {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Hola a todos, os comparto como ha quedado mi codigo al final:
#el primer paso es importar todas las libreias que voy
#a utilizar:
import requests
from lxml import html #la funcion HTML sirve para convertir archivos de HTML a un tipo de archivo con el cual pueda aplicar XPath
import os #vamos a utilizar este modulo para crear una carpeta con la fecha de hoy
import datetime #nos permite traer la fecha de hoy
#despues debo crear las constantes que me llevaran
#al cuerpo, el titulo y el resumen de la noticia.
#contiene el link a la pagina principal de la republica
HOME_URL = 'https://www.larepublica.co'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/text-fill/a/@href'
XPATH_LINK_TO_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_LINK_TO_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_LINK_TO_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
#ahora voy a crear las funciones de este programa
#la funcion parse_home es la que se encarga de obtener
#los links a las noticias:
def parsed_notice(link, today):
#envuelvo en un bloque try en caso de que el status_code de mi
#peticion sea distinto a 200
try:
response = requests.get(link)#solicito respusta al link de la noticia
if response.status_code == 200:
#primero quiero traer el documento html de la noticia
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)#lo convierto a un documento para aplicar xpath(un HTML con superpoderes)
#el error contra el que me quiero proteger es si summary no tiene indices
#es decir, no existe, en ese caso lo que quiero es salirme de la fucnion porque no
#quiero noticias sin resumen
try:
#a estas variables pongo indices porque el resultado de aplicar xpath
#a un html con superpoderes (parsed) nos devuleve una lista que puede tener uno a varios elementos
#en este caso nosotros sabemos que el titulo, el resumen y el cuerpo
#son el elemento 1 de una lista (index 0)
title = parsed.xpath(XPATH_LINK_TO_TITLE)[0]
#ahora voy a manejar el posible error de que el titulo tenga comillas
#este link explica porque https://platzi.com/comentario/2198698/
title = title.replace('\"','')
summary = parsed.xpath(XPATH_LINK_TO_SUMMARY)[0]
#a body lo dejamos sin indice porque body es una lista de parrafos y quiero traerme la lista completa
body = parsed.xpath(XPATH_LINK_TO_BODY)
except IndexError:
return
#ahora vamos a guardar esta informacion. with es un gestor de contexto
#es como un try bloc, en caso de que algo salga mal hace que el
#archivo se cierre y no se corrampa. despues uso la funcion open para abrir archivo. Con esta fucnion
#busco la carpeta que se creo con la fecha de hoy y dentro de esa carpeta
#guardo un archivo que tiene como nombre el titulo de la noticia.
#dentro de la funcion open el primer parametro nos indica la ruta del archivo, el segundo
#nos dice que entramos en modo escritura, y el encoding nos permite guardar los caracteres especiales
#de manera correcta para que no haya errores. Finalmente, nombro todo esto como
# f
with open(f'{today}/{title}.txt','w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
#necesito el for porque el body es una lista de parrafos
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
#sin embargo, tendo que proteger mi codigo en caso
#de errores como el codigo 404
try:
#con la funcion de request me traigo el archivo
#HTML de la pagina
response = requests.get(HOME_URL)#con esto no solo obtengo el documentos HTML sino tambien http, como las cabeceras
if response.status_code == 200:
home = response.content.decode('utf-8')
#.content traer el archivo HTML y el .decode('utf-8')
#me ayuda a convertir los caracteres raros (ñ, tildes) en algo que
#python pueda entender
parsed = html.fromstring(home)
#toma el archivo html de home y lo convierte en un tipo de archivo
#a partir del cual yo puedo hacer XPath
#ahora lo que me falta es obtener una lista de los links
#que obtenga que he obtenido hasta ahora
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
#del modulo datetime traemos la funcion date y traemos
#la fecha de hoy. Hasta esta parte guardamos un objeto
#que nos da la fecha de hoy, pero lo que yo quiero es una string
#que me de la fecha en el formato dia/mes/año, par ello uso
#la funcion .strftime('%d-%m-%Y')
#os.path.isdir(today) trae un booleano cuyo valor dependen de si hay
#o no una carpeta con el nombre que hemos establecido (today) en la
#carpeta en la que estamos. Si esa carpeta no existe creamos esa carpeta. isdir() method in Python is
#used to check whether the specified path is an existing directory or not.
if not os.path.isdir(today):
#con esto creamos una carpeta con el nombre de la fecha de hoy si esa carpeta no existe
os.mkdir(today)
#por cada link en la lista vamos a entrar y extraer lo que queremos
for link in links_to_notices:
#esta funcion entra al link y extrae la infomacion que queremos
#y eso lo va a guardar usando la fecha de hoy.
parsed_notice(link, today)
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(ve)
#la siguiente es la funcion principal, la que se va a ejecutar
def run():
parse_home()
if __name__ == '__main__':
run()
Lo que genera el cambio de h2 a text-fill és el decode.
response.content.decode(“utf-8”)
Si lo quitan no realitzarà cambios
Les comparto mi código con la solución del ejercicio. Haciendo el análisis en la web, para sacar el titulo lo hice con //div[@class="mb-auto"]/h2/a/text()
, pero la respuesta era diferente y tuve que usar //div[@class="mb-auto"]/text-fill/a/text()
Vi en la sección de comentarios que también le paso a varios.
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
f.write('\n\n')
f.write(f'Más información en: {link}')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
link_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(link_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in link_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
comence sacando noticias y el what if en mi cabeza se encendio y termine sacando todas las url de las paginas que ponia
Los archivos no se pueden nombrar con caracteres como el ?, ¿ o el >, <, sin embargo, es posible que las noticias los tengan en sus títulos. Por esto se puede crear una simple solución con el mismo método que utiliza el profe cambiando las comillas por otros caracteres que nos interesen:
title = title.replace('\"', '')
title = title.replace('¿', '')
title = title.replace('?', '')
Yo decidi hacer como ejercicio extraer la informacion de los libros de la tienda de toscrape(pagina que recomendo el profe facundo para practicar) con categoria “sequential art” extrae(titulo, precio, existencia y calificacion):
import requests
import lxml.html as html
import os
#BOOKS DATA
XPATH_LINKS='//section/div/ol[@class="row"]/li/article[@class="product_pod"]/h3/a/@href'
XPATH_TITLE='//section/div/ol[@class="row"]/li/article[@class="product_pod"]/h3/a/text()'
XPATH_PRICE='//section/div/ol[@class="row"]/li/article[@class="product_pod"]/div[@class="product_price"]/p[@class="price_color"]/text()'
XPATH_AVAILABILITY='//section/div/ol[@class="row"]/li/article[@class="product_pod"]/div[@class="product_price"]/p[@class="instock availability"]/text()'
XPATH_RATE='//section/div/ol[@class="row"]/li/article[@class="product_pod"]/p/@class'
URL='http // books toscrape com/catalogue/category/books/sequential-art_5/'#platzi me pidio romper el codigo
URL_LIST= []
LINK_LIST=[]
TITLE_LIST=[]
PRICE_LIST=[]
AVAILABILITY_LIST=[]
RATE_LIST=[] #rate 1 to 5
def URL_PAGES():
for i in range(1,5):
x=i
y=(f'{URL}page-{x}.html')
URL_LIST.append(y)
def BOOK_INFO():
LINK=[]
TITLE=[]
PRICE=[]
AVAILABILITY=[]
RATE=[]
for a in range(len(URL_LIST)):
try:
response=requests.get(URL_LIST[a])
if response.status_code == 200:
home=response.content.decode('utf-8')
parsed = html.fromstring(home)
try:
link= parsed.xpath(XPATH_LINKS)
title= parsed.xpath(XPATH_TITLE)
for i in range(len(title)):
title[i]=title[i].replace('\"','')
price= parsed.xpath(XPATH_PRICE)
availability= parsed.xpath(XPATH_AVAILABILITY)
for i in range(len(availability)):
availability[i]=availability[i].strip('\n ')
rate= parsed.xpath(XPATH_RATE)
for i in range(len(rate)):
rate[i]=rate[i].replace('star-rating ','')
for x,n in enumerate(['One','Two','Three','Four','Five']):
if rate[i]==n:
rate[i]=x+1
except IndexError:
return
else:
raise ValueError(response.status_code)
except ValueError as ve:
print(ve)
LINK+=link
TITLE+=title
PRICE+=price
AVAILABILITY+=availability
RATE+=rate
LINK_LIST.append(LINK)
TITLE_LIST.append(TITLE)
PRICE_LIST.append(PRICE)
AVAILABILITY_LIST.append(AVAILABILITY)
RATE_LIST.append(RATE)
def run():
URL_PAGES()
BOOK_INFO()
print(TITLE_LIST[0][3])
print(PRICE_LIST[0][3])
print(AVAILABILITY_LIST[0][3])
print(RATE_LIST[0][3])
if __name__=='__main__':
run()
No me dejó correr el script, me aparece:
UnboundLocalError: local variable ‘title’ referenced before assignment
Les comparto como termine haciendo el reto.
Cosas a considerar:
Como muchos otros notaron, en el XPath que usamos para extraer el titulo en Python, hay que cambiar el h2
por text-fill
.
En el parse_home()
, cuando extraigamos los links puede hayan algunos que estén repetidos. Por esto, usamos el links_to_notice = list(set(links_to_notice))
para quitar los duplicados de la lista.
El set()
transforma la lista en un conjunto donde todos sus elementos son diferentes, despues el list()
lo vuelve a lista
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[@class="news V_Title_Img"]//a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p/text()'
def parse_notice(link,today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
print(IndexError)
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notice = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notice)
links_to_notice = list(set(links_to_notice))
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(f'{today}')
for link in links_to_notice:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Adicional a los aportes de los compañeros acerca del conflicto con <h2>, caracteres especiales como los dos puntos : no los permiten en el nombre del archivo.
import requests
import lxml.html as html
import os # Para crear las carpetas
import datetime # Para asignar nombres con fechas
HOME_URL = 'https://www.larepublica.co/'
#XPATH_LINKS_TO_ARTICLE = '//h2/a/@href'
XPATH_LINKS_TO_ARTICLE = '//text-fill/a/@href'
#XPATH_TITLE = '//div[@class="mb-auto"]/h2/span/text()'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8') # html de la noticia
parsed = html.fromstring(notice) # html con permisos para trabajar
# lectura de la noticia
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
title = title.replace(':', '')
title = title[0:80]
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
# Manejador contextual, permite si se llega a cerrar ele archivo de forma inesperada, mantiene todo seguro sin corromperse
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n \n')
f.write(summary)
f.write('\n \n')
for p in body:
f.write(p)
f.write('\n')
except IndexError:
return
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try: ## Para manejar los errores del servidor como por ejemplo el 404
response = requests.get(HOME_URL) # se obtiene todo el documento HTML junto con las cabeceras
if response.status_code == 200:
home = response.content.decode('utf-8') # .decode transforma todos los caracteres especiales de tal modo que se pueda leer
parsed = html.fromstring(home) # toma el contenido HTML y lo transforma en un documento especial para hacer scraper
links_to_notices = parsed.xpath(XPATH_LINKS_TO_ARTICLE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today): # si no existe el directorio con el nombre today
os.mkdir(today) # creación de la carpeta
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Una de las soluciones para los titulos que descrubri fue usando esta expresion:
//div[@class='mb-auto']/*[2]/span/text()
Al colocar el /*[2] , seleciono el segundo nodo del div que seria el “h2”
Codigo Completo:
import requests
import lxml.html as html
import os
import datetime
#get url page
HOME_URL = "https://www.larepublica.co/"
#expressions
XPATH_LINK_TO_ARTICLE = "//div/a[contains(@class,'kicker')]/@href"
XPATH_TITLE = "//div[@class='mb-auto']/*[2]/span/text()"
XPATH_SUMARY = "//div[@class='lead']/p/text()"
XPATH_BODY = "//div[@class='html-content']/p[not(@class)]/text()"
def parse_notice(link,today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
sumary = parsed.xpath(XPATH_SUMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(sumary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
#extrayendo las url
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices= parsed.xpath(XPATH_LINK_TO_ARTICLE)
#obtener la fecha de hoy
today = datetime.date.today().strftime('%d, %m, %Y')
#verificar si no existe una carpeta por la fecha
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
🚩 SOLUCION: OSError [Errno 22] 🚩
Me ocurría cuando los títulos de noticias llevaban uno de los 3 simbolos: ? ¿ o \ (Inválidos para Windows)
Lo solucioné colocando luego del title lo siguiente:
for e in '?¿\"':
title=title.replace(e,'')
pueden colocar luego de esto un
print(title)
para verificar que se hayan guardado todos los títulos.
Saludos
//div[@class="html-content"]/p//text()'
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
if p.endswith('.'):
f.write(p)
f.write('\n')
else:
f.write(p)
Lo que hice yo fue una especie de menú en el cual le especificas la categoría de noticia que quieres ver y te trae las noticias de esa categoría de manera paginada y puedes escoger la que gustes para leer
13 julio 2022
obtuve satisfactoriamente 10 archivos con el siguietne código, usenlo:
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE= '//text-fill/a/@href' # text-fill instead of h2 !!
XPATH_TITLE='//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY='//div[@class="lead"]/p/text()'
XPATH_BODY='//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8') # html of the notice
parsed = html.fromstring(notice) # better html -> [list]
try:
title = parsed.xpath(XPATH_TITLE)[0] # [0] just the first element of the [list]
title.replace('\"','') # deteling (")
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError: # in case title/summary/body does not exist -> jump the notice
return
# _with_ is an driver contextual driver
with open(f'{today}/{title}.txt','w',encoding='utf-8') as f:
f.write(title)
f.write('\n\n\n\n')
f.write(summary)
f.write('\n\n\n\n')
for p in body:
f.write(p)
f.write('\n\n')
else:
raise ValueError(f'Error {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices=parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
# print(f' # of URLs : {len(links_to_notices)}')
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today): # if does not directory named today? ( T or F)
os.mkdir(today) # generating a directory with today's date
for link in links_to_notices: # generating filess
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print (ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Para los que tienen el problema de que no se guarda nigún documento 😃 entre los comentarios hay explicaciones más detalladas. En resumen es cómo interpreta el XPATH de python con el de Chrome. h2 por text-fill
XPATH_TITLE = '//div/h2[@style]/span/text()'
Despues
XPATH_TITLE = '//div/text-fill/span/text()'
Lo logre
⭐️⭐️⭐️⭐️⭐️
a esta fecha , por si alguien necesita los xpath del ejercicio:
solo por decir que en lugar de tomar o hacer text-fill como algunos han sugerido, yo simplemente hice // para buscar el span y luego en el archivo python tome el [1] del array del titulo.
XPATH_LINK_TO_ARTICLE = '//div[@class="col mb-4"]/div[@class="news V_Title_Img"]/a[position()=1]/@href'
XPATH_TITLE = '//div[@class = "row OpeningPostNormal"]/div[@class = "col-8 order-2 d-flex flex-column"]/div[@class = "mb-auto"]//span/text()'
XPATH_SUMMARY = '//div[@class = "row article-wrapper"]/div[@class = "wrap-post col-9"]/div[position() = 1]/div[@class = "lead"]/p/text()'
XPATH_BODY = '//div[@class="row article-wrapper"]/div[@class="wrap-post col-9"]/div[position()=1]/div[@class="html-content"]/p/text()'
Al día de hoy el scraping se tiene que hacer con:
Links = //div[@class=“news V_Title_Img”]/a/@href
Titulo = //div[@class=“mb-auto”]/h2/span/text()
Resumen = //div[@class=“lead”]/p/text()
Cuerpo = //div[@class=“html-content”]/p/text()
Me costó un poco realizar mi código porque lo que me servía en el navegador no me funcionaba en Python. Como tal considero que lo único que debo dejar en la sección de comentarios es el lenguaje de XPath. No me ayudo mucho el hecho de estar revisando el título de las noticias en la página principal xd.
XPATH_LINK_TO_ARTICLE = '//text-fill/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]//span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
Les dejo por aquí esta función que les ayudará a quitar de los titulos todos aquellos carácteres que les darían error al guardar su noticia:
def remove_characters(str):
characters = (
'\"',
'?',
'|',
'\\',
'', #You need this line because the double backslash takes the next element. Try removing it
'/',
':',
'<',
'>',
'*'
)
for character in characters:
str = str.replace(character, '')
return str
Este Es un código de la clase de web scraping con Python y Xpath. Descarga información un sitio de noticias de colombia.
Utiliza un ambiente virtual en la consola de comandos, xpath y otras herramientas.++ Sustituí h2 en el path for text-fill++ y así logré que funcione.
import requests #librería para el scraping
import lxml.html as html #libreria para el scraping
import os #libreria para crear una carpeta
import datetime #libreria para timestamps, se combinará con OS.
HOME_URL = 'https://larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//text-fill[not(@class)]/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="news economiaSect"]/div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0] #tomar el primer título en la lista de titles del path
title = title.replace('\"','') #eliminar las comillas de un título en caso de que existan para evitar errores
summary = parsed.xpath(XPATH_SUMMARY)[0] #tomar el primer resumen
body = parsed.xpath(XPATH_BODY) #tomar los parrafos de Body.
except IndexError: #en caso de que no haya resumen o body, regresa a la función en vez de marcar un error.
return
with open(f'{today}/{title}.txt','w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices) #se imprime para ver como progresa
today = datetime.date.today().strftime('%d-%m-%y')#funcion trae una fecha, today trae la de hoy y strftime le da un formato deseado.
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Es uno de los proyectos que más me han costado. Les comparto funcionando al 16/junio/2021:
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//text-fill/a[@class="economiaSect" or @class="empresasSect" or @class="ocioSect" or @class="globoeconomiaSect" or @class="analistas-opinionSect"]/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_news(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
print(title)
title = title.replace('\"','')
title = title.replace('/','-')
title = title.replace('|','-')
title = title.replace(':','-')
title = title[0:80]
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt','w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_news = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_news)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_news:
parse_news(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//h2[@class="headline"]/a/@href'
XPATH_TITLE = '//h1[@class]="headline"]/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class"articlewrapper "]/p[not(@class)]/text()'
def parse_notice(link,today):
try:
response = requests.get(link)
if response.status_code ==200:
notice = response.content.decode('utf-8')
parsed = html.fronstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.reolace('\"','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
whit open('{today}')/{title}.txt' , 'w' , encoding='utf-8') as
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body,
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.contect.decode('utf-8')
parsed = html.fronstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()```
Les comparto mi código hoy 22/10/2020
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[contains(@class, "V")]/a[contains(@class, "kicker")]/@href'
XPATH_TITLE = '//h2[not(@class)]/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
title = title.replace('#','')
title = title.replace('|','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Scraper del El Espectador v.2
https://www.elespectador.com/
con imagen del articulo
import requests
import lxml.html as html
import os
import datetime
BASE_URL = 'https://www.elespectador.com'
HOME_URL = 'https://www.elespectador.com/noticias/'
XPATH_LINKS = '//h2/a/@href'
XPATH_TITLE = '//div[contains(@class,"field--name-title")][1]/h1/text()'
XPATH_RESUMEN = '//div[contains(@class,"field--name-field-teaser")][1]//p/text()'
XPATH_IMAGE = '//div[contains(@class,"file-image")]/img/@src'
XPATH_BODY = '//div[contains(@class,"content_nota")]/p/text()'
def parse_notice(link, today):
try:
response = requests.get(BASE_URL+link)
if response.status_code==200:
notice = response.content.decode('utf-8');
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
resumen = parsed.xpath(XPATH_RESUMEN)[0]
image = parsed.xpath(XPATH_IMAGE)[0]
content = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt','w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(resumen)
f.write('\n\n')
f.write(image)
f.write('\n\n')
for p in content:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code==200:
home = response.content.decode('utf-8');
parsed = html.fromstring(home)
links = parsed.xpath(XPATH_LINKS)
#print(links)
today = datetime.date.today().strftime('%d-%m-%y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Tuve un error con los titulos que tenian “/” en una parte que decia 24/7, hice unos cambios y me funciono. Simplemente aplique replace a “/” por “-”. Les dejo mi código.
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//h2[@class="headline"]/a/@href'
XPATH_TITLE = '//h1[@class="headline"]/a/text()'
XPAHT_SUMMARY = '//div[@class="lead"]/p/text()'
XPAHT_BODY = '//div[@class="articleWrapper "]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
title = title.replace('/', '-')
summary = parsed.xpath(XPAHT_SUMMARY)[0]
body = parsed.xpath(XPAHT_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
raise(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_noticies = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_noticies)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_noticies:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
En mi caso en algunos casos me reconoció el h2 en los títulos de la noticia. Así lo resolví.
try:
title = html_Notice.xpath(xpath_Title)[0]
except IndexError:
title = html_Notice.xpath(xpath_Title_h2)[0]
Como el titulo venia con espacios al principio y al final así lo resolvi.
title = title.replace('\"', '').strip()
.strip(), elimina espacios blancos en los textos.
Excelente curso me ha gustado mucho y he aprendido como recolectar información de internet para luego procesarla con pandas o otro método y poder sacar información valiosa.
Este es un script un poquito mas reutilizable basado en clases
import requests
import lxml.html as html
import os
import datetime
# Constantes
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[@id="vue-container"]//a/@href'
XPATH_TITLE = '//h2/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
class ScraperNewspaper:
today = datetime.date.today().strftime('%d-%m-%Y')
title = ''
def write_log(self, message):
with open('./log.txt', 'a', encoding='utf-8') as f:
f.write(str(message))
f.write('\n')
def parse_notice_link(self, home_url, xpath_link_to_article):
try:
response = requests.get(home_url)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
# open('./test.html', 'wb').write(response.content)
links_to_notices = parsed.xpath(xpath_link_to_article)
# print(len(links_to_notices))
return links_to_notices
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
self.write_log(ve)
print(ve)
return (ve)
except ConnectionRefusedError as cre:
self.write_log(cre)
print(cre)
def len_links(self, links_to_notices):
print(len(links_to_notices))
return len(links_to_notices)
def parse_notice(self, link, xpath_title, xpath_body, xpath_summary='', xpath_image=''):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
self.title = title = parsed.xpath(xpath_title)[0]
title = title.replace('\"', '')
summary = [parsed.xpath(xpath_summary)[
0] if xpath_summary else '']
image = [parsed.xpath(xpath_image)[0]
if xpath_image else '']
body = parsed.xpath(xpath_body)
return title, body, summary, image
except IndexError:
return "***Error***", "***Error***", "***Error***", "***Error***"
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
self.write_log(ve)
print(ve)
return (f"{ve}", "***Error***", "***Error***", "***Error***")
except ConnectionRefusedError as cre:
self.write_log(cre)
print(cre)
def write_notice_file(self, title, body, summary, image, today):
try:
title = str(title)
summary = str(summary)
image = str(image)
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
if summary:
f.write('\n\n')
f.write(summary)
if image:
f.write('\n\n')
f.write(image)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
except Exception as te:
self.write_log("Error de esritura"+str(te))
print("Error de esritura"+str(te))
return 0
def mkdir(self, today):
if not os.path.isdir(today):
os.mkdir(today)
@staticmethod
def run():
print('Iniciando proceso de scraping')
scraper = ScraperNewspaper()
links_to_notices = scraper.parse_notice_link(
HOME_URL, XPATH_LINK_TO_ARTICLE)
scraper.len_links(links_to_notices)
scraper.mkdir(scraper.today)
for link in links_to_notices:
title, body, summary, image = scraper.parse_notice(
link, XPATH_TITLE, XPATH_BODY, XPATH_SUMMARY)
scraper.write_notice_file(
title, body, summary, image, scraper.today)
print('Proceso terminado')
if __name__ == '__main__':
ScraperNewspaper.run()
Aca dejo mi codigo para un dettallle que no se si el profe facundo se dio de cuenta pero dentro de los body el codigo no reconoce los textos que esten modificado como en negritas esto se soluciona modificando el xpath del body y usando p.strip()
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//text-fill[not(@class)]/a/@href'
XPATH_TITLE = '//div/h2/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p//text()' # Ajuste en la expresión XPath
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code ==200:
notice= response.content.decode('utf-8')
parsed= html.fromstring(notice)
try:
title= parsed.xpath(XPATH_TITLE)[0]
title= title.replace('\"','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p.strip()) # Utilizar strip() para eliminar espacios en blanco alrededor de cada párrafo
f.write('\n')
else:
raise ValueError(f'Error:{response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error:{response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()```
Aqui la clase es mas de Python que de webscraping, ahh! web scraping con python!!
En la linea 16 es un Error poner unicammente el link porque recordemos que esa variable es una lista de hrefs, es dicir; no es la URL completa, no entiendo como funciono entonces?
Esta es una versión del scrapper del curso pero utilizando hilos para optimizar la velocidad del programa, solo extrae links y titulo de noticias pero sin problema puede usarse para extraer más información 😃
import requests
import lxml.html as html
import os
import datetime
import threading
LINK_EXPRESSION = '//a[@class="globoeconomiaSect"]/@href'
TITLE = "//title/text()"
def save_data(link,hoy):
try:
response = requests.get(link)
if response.status_code == 200:
try:
parsed = html.fromstring(response.content.decode('utf-8')).xpath(TITLE)[0].replace('"','').replace("'",'').replace('https://www','').replace('#','').replace('|','')
except IndexError:
return
finally:
with open(f'{hoy}/{parsed}.txt', 'w+', encoding='utf-8') as noticia:
noticia.write(parsed + ' ==> ' + link + '\n')
else:
raise ValueError(f'Ocurrió un error durante la conexión a este sitio: {response.status_code}')
except ValueError as ve:
print(ve)
def get_links(url):
try:
parse = html.fromstring(requests.get(url).content.decode('utf-8')).xpath(LINK_EXPRESSION)
if len(parse) > 0:
hoy = datetime.datetime.now().strftime('%d-%m-%y')
if not os.path.isdir(hoy):
os.mkdir(hoy)
for link in parse:
thread = threading.Thread(target=save_data, args=(link, hoy,))
thread.start()
else:
raise ValueError(f'No se encontraron links en {url}')
except ValueError as ve:
print(ve)
def run():
get_links('https://www.larepublica.co/')
run()
5/3/2023
En mi caso al ejecutar scraper me arroja un error debido a que el título contenia caracteres especiales, lo solucioné con import re quedando ese apartado del código de la siguiente forma:
try:
title = parsed.xpath(XPATH_TITLE)[0]
print(title)
** title = re.sub(r'[^\w\s-]', '', title).strip()
title = re.sub(r'[-\s]+', '_', title)**
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
Les dejo los Xpath a diciembre del 2022:
XPATH_LINK_TO_ARTICLE = '//h2/a/@href’
XPATH_TITLE = '//div[@class= “col-8 order-2 d-flex flex-column”]/div/h2/span/text()'
XPATH_SUMMARY = '//div[@class= “lead”]/p/text()'
XPATH_BODY = ‘//div[@class= “html-content”]/p/text()’
Ojo, los Xpath son correctos pero esta el problema del h2 del titulo que no permite grabar los archivos. Veremos
Para los que les sale el siguiente error:
AttributeError: type object 'datetime.time' has no attribute 'today'
Reemplacen “time” por datetime.
Les va a quedar así:
today = datetime.datetime.today().strftime('%d,%m,%Y')
Si no les crea los archivos puede ser por el error en el titulo, a mi me sirvio poner en XPATH_TITLE = ‘//div[@class=“mb-auto”]/text-fill/span/text()’
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICULE = '//div[@class = "V_Title"]/text-fill/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parce_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}' )
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICULE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parce_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
👾
el link no funciona https://platzi.com/clases/data/
Hola les paso mi codigo con otra pagina de un diario de argentina que tiene la particularidad que el link donde se alojan los titulos para ingresar a la noticia esta incompleto con lo cual cree una variable para agregar ese dato a los links y poder tomar las noticias
import requests
import lxml.html as html
import os
import datetime
from requests.models import Response
HOME_URL = 'https://www.infobae.com/'
XPATH_LINK_TO_ARTICLE = '//a[@class="cst_ctn"]/@href'
XPATH_TITLE = '//h1[@class="article-headline"]/text()'
XPATH_SUMMARY = '//h2[@class="article-subheadline"]/text()'
XPATH_BODY = '//div[@class="nd-body-article"]/p[@class="paragraph"]/text()'
root = 'https://www.infobae.com'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
print(title)
title = title.replace('\"', "")
title = title.replace('?', "")
title = title.replace('¿', "")
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError: # puede que hay noticias que no tienen summary o algo entonces con esto salgo
print("as")
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200: #para saber el estado de la pagina con .status_code
home = response.content.decode('utf-8') # response.content responde el documnto html de la respuesta y decode modifica los caracteres para que no de error
parsed = html.fromstring(home) #esta lina toma el contenido de html lo transforma en un documento especial para usar xpath
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE) #obtiene una lista de todo el resultadod de aplicar xpath
links_to_notices =[root + x for x in links_to_notices]
#print(len(links_to_notices))
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y') #te nos trae la fecha today la de hoy, con strftime nos da el formato de como la queremos
if not os.path.isdir(today): #estoy preguntando si no exite os.path nos trae un T o F si esta today si no esta la creamos
os.mkdir(today)
for link in links_to_notices: # a partit de cada link entro en cada nota y extraigo la info
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Hola a todos,
Cuando copio el código que muestra Facundo en la clase, no me crea los archivos. Coloque print en varios puntos y encontre que las expresiones title, summary y body no me arrojan ningun valor (ninguna de las tres). Les dejo el codigo para ver si ustedes pueden encontrar porque esta fallando, yo no pude encontrar la falla
import requests
import lxml.html as html
import os #sirve para crear carpetas con el dia de la fecha
import datetime # sirve para traer la fecha de hoy
HOME_URL ='https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE= '//h2[@class= "headline"]/a/@href'
XPATH_TITLE= '//div[@class= "mb-auto"]/h2/span[1]/text()'
XPATH_SUMMARY= '//div[@class="lead"]/p/text()'
XPATH_BODY= '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link,today):
try:
response= requests.get(link)
if response.status_code == 200:
notice=response.content.decode('utf-8')
parsed= html.fromstring(notice)
try:
title= parsed.xpath(XPATH_TITLE)[0]
print(title)
#saco las posibles comillas de las cadenas del titutlo
title=title.replace('\"','')
#[0] es para traer siempre el primer valor que es el que buscamos
summary= parsed.xpath(XPATH_SUMMARY)[0]
print(summary)
body= parsed.xpath(XPATH_BODY)
print(body)
except IndexError:
return
#with manejador contextual, si se cierra por alguna circunstacia,
#este pad me mantiene todo de manera segura
#fx open me permite abrir archivos
#open(nombre de archivo o ruta, tipo wrm, codificacion siemrpe utf 8)
with open(f'{today}/{title}.txt', 'w', encoding= 'utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
#raise para lanzar errores
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
#salvamos los errores que van a ocurrir
try:
#traigo la pagina que yo quiero, en este caso la republica
response= requests.get(HOME_URL)
#print(response) 200 then we are going well
#if status_code(look status code in lesson 1 PLATZI 200 ----> ok)
if response.status_code == 200:
#solo traigo los links, response.content= devuelve el html
#decode es un metodo que ayuda a trasnformar caracteres especiales en algo para python
home= response.content.decode('utf-8')
#tomamos el html y lo llevamos a un doc especial para hacer xpath
parsed= html.fromstring(home)
links_to_notices= parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
#datetime para fechas, datye para traer fechas,today
#strftime convierte en cadena de caracteres
today=datetime.date.today().strftime('%d-%m-%Y')
#os.paths.isdir trae true or false dependiendo si en la arpeta que estoy existe o no
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
#raise= para elevar un error
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Que curso más hermoso, gracias Facundo.
Les comparto mi scraper, yo lo hice con el sitio de noticias el País México. Le puse los nombres a los archivos como los maneja el link, porque había muchos problemas con los títulos de las noticias:
import requests
import lxml.html as html
import os
import datetime
import re
# LINK DE LA PÁGINA
HOME_URL = "https://elpais.com"
MEXICO = "/mexico"
"""
Códigos XPath
"""
XPATH_LINK_TO_ARTICLE = '//h2[@class="c_h headline | color_gray_ultra_dark ' + \
'font_secondary width_full headline_md c_h__md "]/a/@href'
XPATH_TITLE = '//h1[@class="a_t | font_secondary color_gray_ultra_dark "]'+ \
'/text()'
XPATH_SUMMARY = '//h2[@class="a_st font_secondary color_gray_dark "]/text()'
XPATH_BODY = '//div[@class="a_b article_body | color_gray_dark"]/p/text()'
XPATH_DATE = '//a[@class="a_ti"]/text()'
XPATH_PLACE = '//span[@class="a_pl | capitalize color_black"]/text()'
def extract_name_from_link(link):
result = re.search(r'\d+/(.*)+.html', link)
string2 = result.group()
string2 = string2[3:]
string2 = string2[:-5]
return string2
#Función para sacar noticias.
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
title = title.replace('|', '')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
date = parsed.xpath(XPATH_DATE)[0]
place = parsed.xpath(XPATH_PLACE)[0]
except IndexError:
return
with open(f'data/{today}/{extract_name_from_link(link)}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(date)
f.write('\n\n')
f.write(place)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
#Función para traer links.
def parse_home():
try:
response = requests.get(HOME_URL + MEXICO)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
#Fecha en de hoy en strings
today = datetime.date.today().strftime('%d-%m-%Y')
#Para ver si la carpeta exista.
if not os.path.isdir(today):
os.mkdir('data/' + today)
for link in links_to_notices:
if link.startswith('https'):
parse_notice(link, today)
else:
parse_notice(HOME_URL + link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
A dia de hoy use estos xpath para conseguir 41 noticias sustanciales
XPATH_LINK_TO_ARTICLE = (
'//div/a[contains(@href, "www.larepublica.co")]/@href'
)
XPATH_TITLE = '//div[@class="mb-auto"]//a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'```
, se me hizo una solucion rápida y sencilla para no batallar si scrapean de más jaja
import numpy as np
lista_sin_repetidos = np.unique(lista_de_elementos_scrapeados)
Yo no pude utilizar el diario la república pero use uno de mi país y me paso algo bien chistoso. Seguí todos los pasos y me lanzaba error la consola y al final después de una hora me di cuenta que era porque en una noticia habían símbolos que no reconocía como el «» al final con un title.replace se soluciono XD
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.lacuarta.com/'
XPATH_LINK_TO_ARTICLE = '//figcaption[@class="m-top-15"]/h4/a/@href'
XPATH_TITTLE = '//div[@class="main-wrapper"]/h1/text()'
XPATH_SUMMARY = '//h2[@class="text-center"]/text()'
XPATH_BODY = '//div[@class="col-md-11 col-lg-11 col-md-offset-1 col-lg-offset-1 nota-interior-tx p-top-30"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITTLE)[0]
title = title.replace('\"','')
title = title.replace('«','')
title = title.replace('»','')
title = title.replace(':','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
#print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()
Que increíble, cada punto del código explicado muy bien.
Creo que algo que le vendria super bien al proyecto seria usar threads para aumentar el performance :’)
alguien sabe como arreglar el “UnboundLocalError: local variable ‘title’ referenced before assignment”? :c
SI lo hago con Mac, ¿Cómo sería el with open? ya que f’ no lo toma
En Perú
import requests
import lxml.html as html
import os, datetime
HOME_URL = 'https://www.larepublica.pe/'
XPATH_LINK_TO_LAST_NEWS = '//div[@class="wrapper_items"]/ul/li/a/@href'
XPATH_TITLE = '//h1[@class="DefaultTitle"]/text()'
XPATH_SUMMARY = '//h2[@class="DefaultSubtitle"]/text()'
XPATH_BODY = '//div[@class="page-internal-content"]/section/p/text()'
def parse_notice(link, today):
try:
response = requests.get(HOME_URL + link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError as ie:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
pass
except ValueError as ve:
pass
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_LAST_NEWS)
# for i in links_to_notices:
# print(i)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Increible lo que se puede hacer.
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//h2[@class="headline"]/a/@href'
XPATH_TITLE = '//h1[@class="headline"]/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="articleWrapper "]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Erro: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == '__main__':
run()```
Scraper del El Espectador v.1
https://www.elespectador.com/
import requests
import lxml.html as html
import os
import datetime
BASE_URL = 'https://www.elespectador.com'
HOME_URL = 'https://www.elespectador.com/noticias/'
XPATH_LINKS = '//h2/a/@href'
XPATH_TITLE = '//div[contains(@class,"field--name-title")][1]/h1/text()'
XPATH_BODY = '//div[contains(@class,"field--name-field-teaser")][1]//p/text()'
def parse_notice(link, today):
try:
response = requests.get(BASE_URL+link)
if response.status_code==200:
notice = response.content.decode('utf-8');
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"','')
content = parsed.xpath(XPATH_BODY)[0]
except IndexError:
return
with open(f'{today}/{title}.txt','w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(content)
f.write('\n\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code==200:
home = response.content.decode('utf-8');
parsed = html.fromstring(home)
links = parsed.xpath(XPATH_LINKS)
#print(links)
today = datetime.date.today().strftime('%d-%m-%y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links:
parse_notice(link,today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
Espectacular este curso
Fenomeno !! crack
]/a/@href'
XPATH_TITLE_ARTICLE = '//h2[not(@class)]/a/text()'
XPATH_SUMMARY_ARTICLE = '//div[@class="lead"]/p/text()'
XPATH_BODY_ARTICLE = '//div[@class="html-content "]/p[not(@class)]/text()'
esas modificaciones tuve que hacer para por lo menos crear unas noticias
si haces un print en esta parte del código
for link in links_to_notice:
parse_notice(link, today)
#print(f'fecha: {today} link: {link}')
y compruebas la estructura en los links que imprime sabrás como reformular los xpath para que no ingrese al return y solo cree la carpeta con la fecha
Saludos
No puedo obtener los titulos, el xpath funciona perfectamente en la consola de chrome pero el programa no lo trae y tampoco arroja ningun error, se termina de ejecutar y me devuelve la carpeta con la fecha vacia, les dejo mi codigo por si me pueden ayudar.
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div/h2/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/h2/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_new(link, today):
try:
response = requests.get(link)
if response.status_code == 200:
new = response.content.decode('utf-8')
parsed2 = html.fromstring(new)
try:
title = parsed2.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
title = title.replace('?', '')
title = title.replace('¿', '')
title = title.replace('!', '')
title = title.replace('¡', '')
title = title.replace(':', '')
title = title.replace('"', '')
summary = parsed2.xpath(XPATH_SUMMARY)[0]
body = parsed2.xpath(XPATH_BODY)
except IndexError:
return
with open(f'.{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('utf-8')
parsed = html.fromstring(home)
links_to_news = parsed.xpath(XPATH_LINK_TO_ARTICLE)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today): #pregunto si ya existe una carpeta con la fecha de hoy
os.mkdir(today)
for link in links_to_news:
parse_new(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()```
que práctica tan buena , el secreto está eb sacar bien las expresiones 😀
Me encantó todo lo que aprendí en el curso, es súper útil 🙌 :hea
Pregunta 2 del archivo de Notion.
Les comparto mi codigo para la pagina de infobae.
import requests
import lxml.html as html
import os
import datetime
import time
HOME_URL = 'https://www.infobae.com'
XPATH_LINK_TO_ARTICLE = '//a[@data-pb-field="headlines.basic"]/@href'
XPATH_TITLE = '//div[@class="row"]/header/h1/text()'
XPATH_SUMMARY = '//div[@class="row"]/header/span[@class="subheadline"]/text()'
XPATH_BODY = '//div[@id="article-content"]/div[@class="row pb-content-type-text"]//text()'
def parse_notice(link, today):
'''Funcion que guarda los titulos, subtitulos y cuerpos de una noticia en un archivo'''
try:
response = requests.get(HOME_URL + link)
if response.status_code == 200:
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\"', '')
title = title.replace('?', '')
title = title.replace('¿', '')
title = title.replace('!', '')
title = title.replace('¡', '')
title = title.replace(':', '')
title = title.replace('"', '')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
for p in body :
p = p.strip(' \n')
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
if not p.isspace():
f.write(p)
if p.endswith('.') or p.endswith(':'):
f.write('\n')
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
except requests.exceptions.ConnectionError:
time.sleep(0.1)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
'''Traemos el documento html de la pagina como tipo string'''
home = response.content.decode('utf-8')
'''Convertimos el documento string a un documento tipo html al cual poder realizarle xpath'''
parsed = html.fromstring(home)
'''Creo una lista de links hacia las noticias'''
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
'''Creamos una carpeta donde guardar las noticias de la fecha'''
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link, today)
else:
raise ValueError(f'Error: {response.status_code}')
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
A fecha 01-05-21 tuve varios problemas para hacer que el script funcione, pero con la ayuda de varios aportes he logrado conseguir generar los archivos.
Comparto el script por si a alguien le sirve. Disculpen los comentarios , no se nada de phyton, aprendi lo basico para este curso.
Facundo tiene un nivel de programación increíble. Muy buen curso. También gracias a la comunidad de Platzi por compartir la solución a ciertos errores 😃
Buen día
Veo que todos tuvimos el inconveniente al utilizar el tag h2 y fue cambiando por text-fill.
El día que realice esta practica tuve el inconveniente que había una noticia con un titular largo y a la hora de crear el archivo tenia un error. Por lo que corte el title y lo coloque para que tuviera un tamaño máximo de 80 caracteres.
#Extraer el titulo, el resumen y el cuerpo
title = parsed.xpath(XPATH_TITLE)[0]
#Eliminar las comillas de los titulos y acortar los titulos
title = title.replace('\"','')
title = title[0:80]
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
Hola alguno me podria ayudar, saben si hay una forma de traer no los links de hoy sino todos los historicos de este mismo periódico ??
Termine el proyecto 😄
Le añadí unos fstring para darle un pequeño formato a los documentos txt.
Ahora que tenemos los links de las noticias vamos a crear un script con la finalidad de ir a cada link y extraer el titulo, el resumen y el cuerpo. Para ello necesitaremos dos librerias que vienen instaladas en el core de Python, OS y datetima. El modulo OS lo vamos a utilizar para crear una carpeta con la fecha de hoy, mientras que datetime lo vamos a utilizar para traer la fecha de hoy.
El paso numero 1 consiste en crear una variable que contenga el nombre de la carpeta donde vamos a guardar todas las noticias, es decir, la fecha de hoy. Para ello usamos el metodo today() de la funcion date del modulo datetime, nos quedaria algo tal que asi: datetime.data.today(). Esto nos trae la fecha que quedara guardado en un objeto, sin embargo, nosotros no queremos un objeto, nosotros queremos uns string con el formato (dia/mes/año), es por ello que añadimos: strftime(’%d-%m.%Y), y si lo juntamos todo queda esto:
today = datetime.date.today().strftime('%d-%m-&Y')
y cuadno lo añadimos a la funcion parse_home() queda esto:
import requests
from lxml import html
import os
import datetime
HOME_URL = 'https://www.larepublica.co'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/text-fill/a/@href'
XPATH_LINK_TO_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_LINK_TO_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_LINK_TO_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def pearse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('rtf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(ve)
today = datetime.date.today().strftime('%d-%m-%y')
def run():
pearse_home()
if __name__ == '__main__':
run()
El segundo paso consiste en crear una carpeta que tenga como nombre la fecha de hoy simepre y cuando esta carpeta no exista ya. Para conseguir esto crearemos un if statement que creara la carpeta solo si esta no existe ya y ejecutara una funcion que nos permitira acceder a cada noticia y extraer el titulo, resumen y cuerpo.
Nuestro if statement quedara asi:
#os.path.isdir(today) nos retorna un booleano cuyo valor dependera de si hay o no una carpeta que tenga como nombre la fecha de hoy
if not os.path.isdir(today):
#en caso de que dicha carpeta no exista, crearemos una
os.makdir(today)
#ahora vamos a ejecutar una funcion que nos permite obtener el titulo, el resumen y el cuerpo de cada noticia, para despues guardarlo en la carpeta con el nombre de la fecha de hoy.
for link in links_to_notices:
parsed_notice(link, today)
Una vez juntamos esto con el paso anterior nos queda lo siguiente:
import requests
from lxml import html
import os
import datetime
HOME_URL = 'https://www.larepublica.co'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/text-fill/a/@href'
XPATH_LINK_TO_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_LINK_TO_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_LINK_TO_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def pearse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('rtf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(ve)
today = datetime.date.today().strftime('%d-%m-%y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parsed_notice(link, today)
def run():
pearse_home()
if __name__ == '__main__':
run()
Genial, ahora en el tercer paso solo nos falta crear la funcion parsed_notice() para obtener la informacion que queriamos. Dentro de esta funcion vamos a crear un bloque try para protegernos en caso de que cuando hagomos la peticion al servidor este nos devuelva un status_code distinto a 200 (no todo ok).
#la funcion recibe como parametros el link a cada noticia y la fehca de hoy para poder guardar lo informacion extraida la carpeta que tiene ese nombre.
def parsed_notice(link, today):
try:
#solicito respuesta al link de la noticia
response = request.get(link)
if resonse.status_code == 200:
pass
else:
raise ValueError(f'ERROR: {response.status_code}')
except valueError as ve:
print(ve)
Una vez ya hemos protegido nuestro codigo nos falta desarrollar la logica del programa si la solicitud al servidor no nos da ningun error:
def parsed_notice(link, today):
try:
response = request.get(link)
if resonse.status_code == 200:
#convertimos el contenido de la noticia en un lenguaje que python pueda enternder
notice = response.content.decode('utf-8)
#lo convertimos a un html con superpoderes en el que podamos usar expresiones XPath
parsed = html.fromstring(notice)
else:
raise ValueError(f'ERROR: {response.status_code}')
except valueError as ve:
print(ve)
Como ya tenemos el contenido de una peticion al servidor en un html en el que podemos aplicar XPath tenemos que tener lo siguiente en cuenta: cuando aplico XPath a este html con superpoderes(parsed) es una lista de elementos. Entonces, cuadno aplico XPath a ese documento para obtener el titulo, el resultado es una lista cuyo primer elemento es el titulo (index=0). Lo mismo con el resumen, cuando aplico XPath me devuleve una lista cuyo primer elemento es el resumen (index=0), sin embargo, no todas las noticias tienen resumen, con lo cual, puede que me de un IndexError. Para prevenir esto uso un bloque try. Finalmente, con el cuerpo no usare indices puesto que el cuerpo esta constituido por varios parrafos y yo los quiero a todos. La funcion quedaria asi:
def parsed_notice(link, today):
try:
response = request.get(link)
if resonse.status_code == 200:
notice = response.content.decode('utf-8)
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TO_TITLE)[0]
#el nombre del archivo que contenga la noticia va a tener como nombre el titulo, con lo cual, si el titulo de alguna noticia tiene comillas windows no nos dejara poner ese nombre. Es por debemos eliminar las noticias. En esta pregunta del foro se explica mejor porque elminamos las comillas https://platzi.com/comentario/2198698/
title = title.replace('\"','')
summary = parsed.xpath(XPATH_TO_SUMMARY)[0]
body = parsed.xpath(XPATH_TO_BODY)
except IndexError:#en caso de que halla error retornamos la funcion
return
else:
raise ValueError(f'ERROR: {response.status_code}')
except valueError as ve:
print(ve)
Ahora lo que tenemos que hacer es guardar toda la informacion que hemos obtenido y guardarla en un archivo de texto, para ello hacemos lo siguiente:
def parsed_notice(link, today):
try:
response = request.get(link)
if resonse.status_code == 200:
notice = response.content.decode('utf-8)
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TO_TITLE)[0]
title = title.replace('\"','')
summary = parsed.xpath(XPATH_TO_SUMMARY)[0]
body = parsed.xpath(XPATH_TO_BODY)
except IndexError:
return
#En esta line de codigo lo que hacemos es abrir la carpeta que tiene como nombre la fecha de hoy y creamos un documento con el nombre del titulo de cada noticia. 'w' indica que estamos en modo escritura y encoding convierte el arhcivo en algo que python pueda entender. Finalmente, todo eso lo guardamos con el nombre f
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
#ahora lo que tenemos que hacer es en el archivo de texto que acabamos de crear escribir el titulo, el resumen y el cuerpo
#escribimos el titulo
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
#como el body esta formado por distintos parrafos necesito un loop para escribir todos los parrafos
for parrafo in body:
f.write(parrafo)
f.write('\n\n')
else:
raise ValueError(f'ERROR: {response.status_code}')
except valueError as ve:
print(ve)
El script final queda asi:
import requests
from lxml import html
import os
import datetime
HOME_URL = 'https://www.larepublica.co'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/text-fill/a/@href'
XPATH_LINK_TO_TITLE = '//div[@class="mb-auto"]/text-fill/span/text()'
XPATH_LINK_TO_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_LINK_TO_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parsed_notice(link, today):
try:
response = request.get(link)
if resonse.status_code == 200:
notice = response.content.decode('utf-8)
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TO_TITLE)[0]
title = title.replace('\"','')
summary = parsed.xpath(XPATH_TO_SUMMARY)[0]
body = parsed.xpath(XPATH_TO_BODY)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for parrafo in body:
f.write(parrafo)
f.write('\n\n')
else:
raise ValueError(f'ERROR: {response.status_code}')
except valueError as ve:
print(ve)
def pearse_home():
try:
response = requests.get(HOME_URL)
if response.status_code == 200:
home = response.content.decode('rtf-8')
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(ve)
today = datetime.date.today().strftime('%d-%m-%y')
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parsed_notice(link, today)
def run():
pearse_home()
if __name__ == '__main__':
run()
La recomendación es hacer print() para ir debuggeando los paths que buscamos, ya que cambia el nombre de algunas etiquetas cuando vemos el código html en la terminal, así que es mejor corroborar si la etiqueta que encontramos con el inspector de Chrome es la misma que aparece en la terminal. En este link se explica mas a detalle. Click aquí
Gracias
Podemos obtener la fecha así también
today = datetime.now().strftime('%d-%m-%Y')
💡No olviden enviarlo a GitHub y mostrarlo como una habilidad más.
Amigos, a alguien más le aparece el siguiente error:
no module named: "requests"
aún ya habiendo instalado el módulo?
Para verificar las librerías instaladas las verifico con el comando: pip list y en efecto me aparece requests instalado. Lo mismo me pasa con lxml.
A la hora de correr el código me crea el directorio pero no me crea ningún archivo.
Ya probé aparte el código de crear y escribir archivos y lo ejecuto perfectamente.
Tuve también el problema de las H2 y lista vacía, ademas de algunos caracteres especiales dejo mi solución
import requests
import lxml.html as html
import os
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[@class="V_Title"]/a/@href'
XPATH_TITLE = '//div[@class="mb-auto"]/text-fill/a/text()'
XPATH_SUMMARY = '//div[@class="lead"]/p/text()'
XPATH_BODY = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link, today):
try:
response=requests.get(link)
if response.status_code==200:
# html from notice
notice = response.content.decode('utf-8')
parsed = html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title = title.replace('\n','').replace('?','').replace('/"','').replace('¿','').replace('/','').replace('\"','')
summary = parsed.xpath(XPATH_SUMMARY)[0]
body = parsed.xpath(XPATH_BODY)
except IndexError as ev:
print(f'ERR:{ev}')
return
with open(f'{today}/{title}.txt','w',encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in body:
f.write(p)
f.write('\n')
else:
raise ValueError(f'ERROR: {response.status_code}')
except ValueError as ve:
print(f'ERROR {ve}')
def parse_home():
try:
response = requests.get(HOME_URL)
if(response.status_code==200):
home = response.content.decode('utf-8')
# convert response to xpath can undertand
parsed = html.fromstring(home)
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
#If not exist a folder with the name Today
if not os.path.isdir(today):
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today)
else:
raise ValueError(f'ERROR:{response.status_code}')
except ValueError as ve:
print(f'ERROR:{ve}')
def run():
parse_home()
if __name__=="__main__":
run()
Vamos!!
import requests
import lxml.html as html
import os #Create directory for saving
import datetime
HOME_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLE = '//div[@class="news V_Title_Img"]/a/@href'
XPATH_TITLE = '//h1[@class="globoeconomiaSect"]/i/text()'
XPATH_SUMMARY= '//div[@class="lead"]/p/text()'
XPATH_DESCRIPTION = '//div[@class="html-content"]/p[not(@class)]/text()'
def parse_notice(link,today):
try:
response = requests.get(link)
if response.status_code ==200:
notice = response.content.decode('utf-8')
parsed= html.fromstring(notice)
try:
title = parsed.xpath(XPATH_TITLE)[0]
title= title.replace('\"','') #DELETE COMILLAS SIMPLES
summary = parsed.xpath(XPATH_SUMMARY)[0]
description = parsed.xpath(XPATH_DESCRIPTION)
except IndexError:
return
with open(f'{today}/{title}.txt', 'w', encoding='utf-8') as f:
f.write(title)
f.write('\n\n')
f.write(summary)
f.write('\n\n')
for p in description:
f.write(p)
f.write('\n')
else:
raise ValueError(f'Error:{response.status_code}')
except ValueError as ve:
print(ve)
def parse_home():
try:
response = requests.get(HOME_URL)
if response.status_code ==200:
home = response.content.decode("utf-8") #Python can read and save
parsed = html.fromstring(home) # transform in a special document where i can use xpath
links_to_notices = parsed.xpath(XPATH_LINK_TO_ARTICLE)
# print(links_to_notices)
today = datetime.date.today().strftime('%d-%m-%Y')
if not os.path.isdir(today): #If it does not exist a dir with today
os.mkdir(today)
for link in links_to_notices:
parse_notice(link,today) #Each link i will execute a function that will take information such as title, summary, description.
else:
raise ValueError(f"Error: {response.status_code}")
except ValueError as ve:
print(ve)
def run():
parse_home()
if __name__ == "__main__":
run()
¡Buen curso! Falto que se guardara en una base de datos y esta se actualizara cada día con nuevas noticias en un sitio web.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?