No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Guardando las noticias en archivos de texto

20/21
Recursos

Aportes 86

Preguntas 33

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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 鈥渆xcept鈥, 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:

  • Titulo
  • Resumen
  • Cuerpo
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 asno 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()

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()

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()

Lo que genera el cambio de h2 a text-fill 茅s el decode.

response.content.decode(鈥渦tf-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 鈥渟equential 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 鈥榯itle鈥 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()

馃毄 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

Importante:

Cuando usamos el xpath de Body existen etiquetas dentro de <p></p> como <strong>, <u>, etc. Estos no se incluyen en el archivo de texto porque son etiquetas anidadas que no est谩n en <p>. Para solucionar esto podemos usar /descendant-or-self:: * al final de la linea, pero como nuestro objetivo es solo conseguir el texto usamos 鈥//text鈥 para incluir todos los campos dentro de la etiqueta <p>, quedando as铆:

//div[@class="html-content"]/p//text()'

Esto genera otro error en el archivo.txt agregando un salto de linea en todos las etiquetas que pudo haber tenido dentro de <p>. Para solucionar esto podemos usar el siguiente c贸digo:

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)

Detectar谩 si la linea termina con un punto y agregar谩 un salto de linea, si no es as铆, le seguir谩 adjuntando de manera continua al texto.

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=鈥渘ews V_Title_Img鈥漖/a/@href
Titulo = //div[@class=鈥渕b-auto鈥漖/h2/span/text()
Resumen = //div[@class=鈥渓ead鈥漖/p/text()
Cuerpo = //div[@class=鈥渉tml-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()

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= 鈥渃ol-8 order-2 d-flex flex-column鈥漖/div/h2/span/text()'
XPATH_SUMMARY = '//div[@class= 鈥渓ead鈥漖/p/text()'
XPATH_BODY = 鈥//div[@class= 鈥渉tml-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 鈥渢ime鈥 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=鈥渕b-auto鈥漖/text-fill/span/text()鈥

Code on January - 2022

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()

馃懢

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()



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 鈥渉2鈥

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()

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()'```

![](

hola, a mi no me genera los archivos, solo crea la carpeta, alguien podria ayudarme?

Por si les salen links duplicados en su scrapeo, encontr茅 una forma de quitar los repetidos utilizando numpy (np), 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 鈥淯nboundLocalError: local variable 鈥榯itle鈥 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

![](

La consola me deja el siguiente error: ![](

Hice peque帽as modificaciones.
Cambiar la ruta para guardar los .txt dentro de la carpeta data. Para que el .gitignore pueda ignorar todo el contenido scrapeado.
Una funci贸n para hacer el response al parse, para no repetir c贸digo, ya que en ambas funciones se escribe dos veces buena parte del c贸digo.

Pueden haber m谩s cosas por hacer, creo que lo tomar茅 como base para un NLP, ya de ah铆 le hago m谩s mejoras. Excelente curso.

Este es mi c贸digo:
https://github.com/israelyance/scraper-xpath-larepublica.co

Actualmente la pagina cambio su estructura.

XPATH_LINK_TO_ARTICLE = '//h2[not(@class)]/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()```

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?

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.