Gu铆a para analizar HTML con BeautifulSoup en Python

    Introducci贸n

    El web scraping consiste en recopilar informaci贸n mediante programaci贸n de varios sitios web. Si bien hay muchas bibliotecas y marcos en varios lenguajes que pueden extraer datos web, Python ha sido durante mucho tiempo una opci贸n popular debido a su gran cantidad de opciones para el web scraping.

    Este art铆culo le brindar谩 un curso intensivo sobre web scraping en Python con Beautiful Soup, una popular biblioteca de Python para analizar HTML y XML.

    El Web Scraping 脡tico

    El web scraping es omnipresente y nos proporciona datos como obtendr铆amos con una API. Sin embargo, como buenos ciudadanos de Internet, es nuestra responsabilidad respetar a los propietarios de los sitios de los que salimos. A continuaci贸n, se muestran algunos principios a los que debe adherirse un web scraper:

    • No reclame el contenido extra铆do como propio. Los propietarios de sitios web a veces dedican mucho tiempo a crear art铆culos, recopilar detalles sobre productos o recopilar otro contenido. Debemos respetar su labor y originalidad.
    • No scrapees un sitio web que no quiere ser scrapeado. Los sitios web a veces vienen con un robots.txt archivo: que define las partes de un sitio web que se pueden raspar. Muchos sitios web tambi茅n tienen T茅rminos de uso que pueden no permitir el scraping. Debemos respetar los sitios web que no quieren ser scrapeados.
    • 驴Existe ya una API disponible? Espl茅ndido, no es necesario que escribamos un scrapeador. Las API se crean para proporcionar acceso a los datos de forma controlada seg煤n lo definido por los propietarios de los datos. Preferimos usar API si est谩n disponibles.
    • Hacer solicitudes a un sitio web puede afectar el rendimiento de un sitio web. Un scrapeador web que realiza demasiadas solicitudes puede ser tan debilitante como un ataque DDOS. Debemos raspar de manera responsable para no causar ninguna interrupci贸n en el funcionamiento regular del sitio web.

    Una descripci贸n general de la hermosa sopa

    El contenido HTML de las p谩ginas web se puede analizar y extraer con Beautiful Soup. En la siguiente secci贸n, cubriremos aquellas funciones que son 煤tiles para raspar p谩ginas web.

    Lo que hace que Beautiful Soup sea tan 煤til son las innumerables funciones que proporciona para extraer datos de HTML. Esta imagen a continuaci贸n ilustra algunas de las funciones que podemos usar:

    Pong谩monos manos a la obra y veamos c贸mo podemos analizar HTML con Beautiful Soup. Considere la siguiente p谩gina HTML guardada en un archivo como doc.html:

    <html>
    <head>
      <title>Head's title</title>
    </head>
    
    <body>
      <p class="title"><b>Body's title</b></p>
      <p class="story">line begins
        <a href="http://example.com/element1" class="element" id="link1">1</a>
        <a href="http://example.com/element2" class="element" id="link2">2</a>
        <a href="http://example.com/avatar1" class="avatar" id="link3">3</a>
      <p> line ends</p>
    </body>
    </html>
    

    Los siguientes fragmentos de c贸digo se prueban en Ubuntu 20.04.1 LTS. Puede instalar el BeautifulSoup m贸dulo escribiendo el siguiente comando en la terminal:

    $ pip3 install beautifulsoup4
    

    El archivo HTML doc.html necesita estar preparado. Esto se hace pasando el archivo al BeautifulSoup constructor, usemos el shell interactivo de Python para esto, para que podamos imprimir instant谩neamente el contenido de una parte espec铆fica de una p谩gina:

    from bs4 import BeautifulSoup
    
    with open("doc.html") as fp:
        soup = BeautifulSoup(fp, "html.parser")
    

    Ahora podemos usar Beautiful Soup para navegar por nuestro sitio web y extraer datos.

    Del objeto de sopa creado en la secci贸n anterior, obtengamos la etiqueta de t铆tulo de doc.html:

    soup.head.title   # returns <title>Head's title</title>
    

    Aqu铆 hay un desglose de cada componente que usamos para obtener el t铆tulo:

    Beautiful Soup es poderoso porque nuestros objetos Python coinciden con la estructura anidada del documento HTML que estamos raspando.

    Para obtener el texto de la primera <a> etiqueta, ingrese esto:

    soup.body.a.text  # returns '1'
    

    Para obtener el t铆tulo dentro de la etiqueta del cuerpo del HTML (indicado por la clase “t铆tulo”), escriba lo siguiente en su terminal:

    soup.body.p.b     # returns <b>Body's title</b>
    

    Para documentos HTML profundamente anidados, la navegaci贸n puede volverse tediosa r谩pidamente. Afortunadamente, Beautiful Soup viene con una funci贸n de b煤squeda para que no tengamos que navegar para recuperar elementos HTML.

    B煤squeda de elementos de etiquetas

    los find_all() El m茅todo toma una etiqueta HTML como argumento de cadena y devuelve la lista de elementos que coinciden con la etiqueta proporcionada. Por ejemplo, si queremos todos a etiquetas en doc.html:

    soup.find_all("a")
    

    Veremos esta lista de a etiquetas como salida:

    [<a class="element" href="http://example.com/element1" id="link1">1</a>, <a class="element" href="http://example.com/element2" id="link2">2</a>, <a class="element" href="http://example.com/element3" id="link3">3</a>]
    

    A continuaci贸n, se muestra un desglose de cada componente que usamos para buscar una etiqueta:

    Tambi茅n podemos buscar etiquetas de una clase espec铆fica proporcionando el class_ argumento. Usos hermosos de la sopa class_ porque class es una palabra clave reservada en Python. Busquemos por todos a etiquetas que tienen la clase “elemento”:

    soup.find_all("a", class_="element")
    

    Como solo tenemos dos enlaces con la clase “elemento”, ver谩 este resultado:

    [<a class="element" href="http://example.com/element1" id="link1">1</a>, <a class="element" href="http://example.com/element2" id="link2">2</a>]
    

    驴Y si quisi茅ramos obtener los enlaces incrustados dentro de la a etiquetas? Recuperemos un enlace href atributo usando el find() opci贸n. Funciona igual que find_all() pero devuelve el primer elemento coincidente en lugar de una lista. Escriba esto en su caparaz贸n:

    soup.find("a", href=True)["href"] # returns http://example.com/element1
    

    los find() y find_all() Las funciones tambi茅n aceptan una expresi贸n regular en lugar de una cadena. Detr谩s de escena, el texto se filtrar谩 utilizando la expresi贸n regular compilada search() m茅todo. Por ejemplo:

    import re
    
    for tag in soup.find_all(re.compile("^b")):
        print(tag)
    

    La lista al iterar, recupera las etiquetas que comienzan con el car谩cter b que incluye <body> y <b>:

    <body>
     <p class="title"><b>Body's title</b></p>
     <p class="story">line begins
           <a class="element" href="http://example.com/element1" id="link1">1</a>
     <a class="element" href="http://example.com/element2" id="link2">2</a>
     <a class="element" href="http://example.com/element3" id="link3">3</a>
     <p> line ends</p>
     </p></body>
     <b>Body's title</b>
    

    Hemos cubierto las formas m谩s populares de obtener etiquetas y sus atributos. A veces, especialmente para p谩ginas web menos din谩micas, solo queremos el texto de ellas. 隆Veamos c贸mo podemos conseguirlo!

    Obtener el texto completo

    los get_text() La funci贸n recupera todo el texto del documento HTML. Consigamos todo el texto del documento HTML:

    soup.get_text()
    

    Su salida deber铆a ser as铆:

    Head's title
    
    
    Body's title
    line begins
          1
    2
    3
     line ends
    

    A veces, los caracteres de nueva l铆nea se imprimen, por lo que su salida tambi茅n puede verse as铆:

    "nnHead's titlennnBody's titlenline beginsn    1n2n3n line endsnn"
    

    Ahora que sabemos c贸mo usar Beautiful Soup, 隆vamos a crear un sitio web!

    Beautiful Soup en acci贸n: Scraping a Book List

    Ahora que dominamos los componentes de Beautiful Soup, es hora de poner en pr谩ctica nuestro aprendizaje. Construyamos un scraper para extraer datos de https://books.toscrape.com/ y gu谩rdelo en un archivo CSV. El sitio contiene datos aleatorios sobre libros y es un gran espacio para probar sus t茅cnicas de web scraping.

    Primero, cree un nuevo archivo llamado scraper.py. Importemos todas las bibliotecas que necesitamos para este script:

    import requests
    import time
    import csv
    import re
    from bs4 import BeautifulSoup
    

    En los m贸dulos mencionados anteriormente:

    • requests – realiza la solicitud de URL y obtiene el HTML del sitio web
    • time – limita la cantidad de veces que raspamos la p谩gina a la vez
    • csv – nos ayuda a exportar nuestros datos extra铆dos a un archivo CSV
    • re – nos permite escribir expresiones regulares que ser谩n 煤tiles para elegir texto seg煤n su patr贸n
    • bs4 – tuyo de verdad, el m贸dulo de scrape para analizar el HTML

    Tu tendr铆as bs4 ya instalado, y time, csvy re son paquetes integrados en Python. Necesitar谩s instalar el requests m贸dulo directamente as铆:

    $ pip3 install requests
    

    Antes de comenzar, debe comprender c贸mo est谩 estructurado el HTML de la p谩gina web. En tu navegador, vayamos a http://books.toscrape.com/catalogue/page-1.html. A continuaci贸n, haga clic con el bot贸n derecho en los componentes de la p谩gina web que desea eliminar y haga clic en el inspeccionar para comprender la jerarqu铆a de las etiquetas como se muestra a continuaci贸n.

    Esto le mostrar谩 el HTML subyacente de lo que est谩 inspeccionando. La siguiente imagen ilustra estos pasos:

    Al inspeccionar el HTML, aprendemos c贸mo acceder a la URL del libro, la imagen de la portada, el t铆tulo, la calificaci贸n, el precio y m谩s campos del HTML. Escribamos una funci贸n que raspe un elemento de un libro y extraiga sus datos:

    def scrape(source_url, soup):  # Takes the driver and the subdomain for concats as params
        # Find the elements of the article tag
        books = soup.find_all("article", class_="product_pod")
    
        # Iterate over each book article tag
        for each_book in books:
            info_url = source_url+"/"+each_book.h2.find("a")["href"]
            cover_url = source_url+"/catalogue" + 
                each_book.a.img["src"].replace("..", "")
    
            title = each_book.h2.find("a")["title"]
            rating = each_book.find("p", class_="star-rating")["class"][1]
            # can also be written as : each_book.h2.find("a").get("title")
            price = each_book.find("p", class_="price_color").text.strip().encode(
                "ascii", "ignore").decode("ascii")
            availability = each_book.find(
                "p", class_="instock availability").text.strip()
    
            # Invoke the write_to_csv function
            write_to_csv([info_url, cover_url, title, rating, price, availability])
    

    La 煤ltima l铆nea del fragmento anterior apunta a una funci贸n para escribir la lista de cadenas extra铆das en un archivo CSV. Agreguemos esa funci贸n ahora:

    def write_to_csv(list_input):
        # The scraped info will be written to a CSV here.
        try:
            with open("allBooks.csv", "a") as fopen:  # Open the csv file.
                csv_writer = csv.writer(fopen)
                csv_writer.writerow(list_input)
        except:
            return False
    

    Como tenemos una funci贸n que puede raspar una p谩gina y exportar a CSV, queremos otra funci贸n que rastree el sitio web paginado, recopilando datos de libros en cada p谩gina.

    Para hacer esto, veamos la URL para la que estamos escribiendo este scrapeador:

    "http://books.toscrape.com/catalogue/page-1.html"
    

    El 煤nico elemento variable en la URL es el n煤mero de p谩gina. Podemos formatear la URL de forma din谩mica para que se convierta en una URL semilla:

    "http://books.toscrape.com/catalogue/page-{}.html".format(str(page_number))
    

    Esta URL con formato de cadena con el n煤mero de p谩gina se puede obtener mediante el m茅todo requests.get(). Entonces podemos crear un nuevo BeautifulSoup objeto. Cada vez que obtenemos el objeto de sopa, se verifica la presencia del bot贸n “siguiente” para que podamos detenernos en la 煤ltima p谩gina. Realizamos un seguimiento de un contador para el n煤mero de p谩gina que se incrementa en 1 despu茅s de raspar una p谩gina con 茅xito.

    def browse_and_scrape(seed_url, page_number=1):
        # Fetch the URL - We will be using this to append to images and info routes
        url_pat = re.compile(r"(http://.*.com)")
        source_url = url_pat.search(seed_url).group(0)
    
       # Page_number from the argument gets formatted in the URL & Fetched
        formatted_url = seed_url.format(str(page_number))
    
        try:
            html_text = requests.get(formatted_url).text
            # Prepare the soup
            soup = BeautifulSoup(html_text, "html.parser")
            print(f"Now Scraping - {formatted_url}")
    
            # This if clause stops the script when it hits an empty page
            if soup.find("li", class_="next") != None:
                scrape(source_url, soup)     # Invoke the scrape function
                # Be a responsible citizen by waiting before you hit again
                time.sleep(3)
                page_number += 1
                # Recursively invoke the same function with the increment
                browse_and_scrape(seed_url, page_number)
            else:
                scrape(source_url, soup)     # The script exits here
                return True
            return True
        except Exception as e:
            return e
    

    La funci贸n de arriba, browse_and_scrape(), se llama de forma recursiva hasta que la funci贸n soup.find("li",class_="next") devoluciones None. En este punto, el c贸digo raspar谩 la parte restante de la p谩gina web y saldr谩.

    Para la pieza final del rompecabezas, iniciamos el flujo de scrapeado. Definimos el seed_url y llama al browse_and_scrape() para obtener los datos. Esto se hace bajo el if __name__ == "__main__" bloquear:

    if __name__ == "__main__":
        seed_url = "http://books.toscrape.com/catalogue/page-{}.html"
        print("Web scraping has begun")
        result = browse_and_scrape(seed_url)
        if result == True:
            print("Web scraping is now complete!")
        else:
            print(f"Oops, That doesn't seem right!!! - {result}")
    

    Si desea obtener m谩s informaci贸n sobre if __name__ == "__main__" block, consulte nuestra gu铆a sobre c贸mo funciona.

    Puede ejecutar el script como se muestra a continuaci贸n en su terminal y obtener el resultado como:

    $ python scraper.py
    
    Web scraping has begun
    Now Scraping - http://books.toscrape.com/catalogue/page-1.html
    Now Scraping - http://books.toscrape.com/catalogue/page-2.html
    Now Scraping - http://books.toscrape.com/catalogue/page-3.html
    .
    .
    .
    Now Scraping - http://books.toscrape.com/catalogue/page-49.html
    Now Scraping - http://books.toscrape.com/catalogue/page-50.html
    Web scraping is now complete!
    

    Los datos extra铆dos se pueden encontrar en el directorio de trabajo actual bajo el nombre de archivo allBooks.csv. Aqu铆 hay una muestra del contenido del archivo:

    http://books.toscrape.com/a-light-in-the-attic_1000/index.html,http://books.toscrape.com/catalogue/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg,A Light in the Attic,Three,51.77,In stock
    http://books.toscrape.com/tipping-the-velvet_999/index.html,http://books.toscrape.com/catalogue/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg,Tipping the Velvet,One,53.74,In stock
    http://books.toscrape.com/soumission_998/index.html,http://books.toscrape.com/catalogue/media/cache/3e/ef/3eef99c9d9adef34639f510662022830.jpg,Soumission,One,50.10,In stock
    

    隆Buen trabajo! Si desea ver el c贸digo del web scraper como un todo, puede encontrarlo en GitHub.

    Conclusi贸n

    En este tutorial, aprendimos la 茅tica de escribir buenos web scrapers. Luego usamos Beautiful Soup para extraer datos de un archivo HTML usando las propiedades del objeto de Beautiful Soup, y varios m茅todos como find(), find_all() y get_text(). Luego, creamos un scraper que recupera una lista de libros en l铆nea y la exporta a CSV.

    El web scraping es una habilidad 煤til que ayuda en diversas actividades, como extraer datos como una API, realizar un control de calidad en un sitio web, verificar URL rotas en un sitio web y m谩s. 驴Cu谩l es el pr贸ximo scrapeador que vas a construir?

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *