Volver al blog
Guías
Mihai MaximLast updated on Mar 31, 20268 min read

Analiza HTML como un profesional: domina el web scraping con Python y expresiones regulares

Analiza HTML como un profesional: domina el web scraping con Python y expresiones regulares

La cantidad de datos disponibles en Internet ha aumentado en las últimas décadas. Las personas utilizan estos datos para una gran variedad de fines, desde intereses personales hasta investigación empresarial.

Sin embargo, si estos datos no se presentan en un formato específico, como XML o JSON, puede resultar difícil o imposible leerlos mediante aplicaciones de software. Aquí es donde entra en juego la técnica del web scraping.

El web scraping es el proceso de recopilar y procesar datos sin procesar de Internet. Estos datos se analizan y se utilizan para diversos fines, como inteligencia de precios, investigación de mercado, entrenamiento de modelos de IA, análisis de sentimiento, auditorías de marca y auditorías de SEO.

Uno de los aspectos clave del web scraping es el análisis de HTML. Esto se puede hacer utilizando diversas herramientas, como BeautifulSoup para Python, Cheerio para NodeJS y Nokogiri para Ruby.

Las expresiones regulares (regex) son una secuencia de caracteres que definen un patrón de búsqueda.

En este artículo, exploraremos cómo analizar un documento HTML utilizando expresiones regulares y Python. También abordaremos algunos de los retos y soluciones alternativas que conlleva el web scraping.

Al final del artículo, tendrás una comprensión completa del tema y de las diferentes herramientas y técnicas disponibles.

Análisis básico con expresiones regulares

La mayoría de los lenguajes de programación de uso general admiten expresiones regulares. Puedes utilizar expresiones regulares en una amplia variedad de lenguajes de programación, incluidos Python, C, C++, Java, Rust, OCaml y JavaScript.

Así es como se ve una regla de expresiones regulares para extraer el valor de la etiqueta <title>:

<title>(.*?)</title>

Da miedo, ¿verdad? Ten en cuenta que esto es solo el principio. Pronto nos adentraremos en el laberinto.

Para este artículo, estoy utilizando Python 3.11.1. Tomemos esta regla y pongámosla en código. Crea un archivo llamado main.py y pega este fragmento:

import re

html = "<html><head><title>Scraping</title></head></html>"

title_search = re.search("<title>(.*?)</title>", html)

title = title_search.group(1)

print(title)

Puedes ejecutar este código con el comando `python main.py`. El resultado que verás como salida es la palabra «Scraping».

En este ejemplo, estamos utilizando el módulo `re` para trabajar con expresiones regulares. La función `re.search()` busca un patrón específico dentro de una cadena. El primer argumento es el patrón de expresión regular, y el segundo argumento es la cadena en la que estamos buscando.

El patrón de expresión regular en este ejemplo es «<title>(.*?)</title>». Consta de varias partes:

  • <title>: Se trata de una cadena literal; coincidirá exactamente con los caracteres «<title>».
  • (.*?): Se trata de un grupo de captura, indicado por paréntesis. El carácter . coincide con cualquier carácter único (excepto un salto de línea), y el cuantificador * significa que debe coincidir con 0 o más del carácter precedente. Además, ? hace que el * no sea codicioso, lo que significa que se detendrá tan pronto como encuentre la etiqueta de cierre.
  • </title>: Esta es también una cadena literal; coincidirá exactamente con los caracteres «</title>».

La función re.search() devuelve un objeto de coincidencia si se encuentra una coincidencia, y el método group(1) se utiliza para extraer el texto coincidente con el primer grupo de captura, que es el texto entre las etiquetas de apertura y cierre de title.

Este texto se asignará a la variable title, y el resultado será «Scraping».

Análisis avanzado de expresiones regulares

Extraer los datos de una sola etiqueta HTML no es muy útil. Te da una idea de lo que puedes hacer con expresiones regulares, pero no puedes utilizarlo en una situación real.

Echemos un vistazo al sitio web de PyPI, el índice de paquetes de Python. En la página de inicio, se muestran cuatro estadísticas: el número de proyectos, el número de versiones, el número de archivos y el número de usuarios.

Queremos extraer el número de proyectos. Para ello, podemos usar esta expresión regular:

([0-9,]+) projects

La expresión regular coincidirá con cualquier cadena que comience con uno o más dígitos, opcionalmente separados por comas, y termine con la palabra «projects». Así es como funciona:

  • ([0-9,]+): Se trata de un grupo de captura, indicado por los paréntesis; los corchetes [0-9,] coinciden con cualquier dígito del 0 al 9 y con el carácter `,`; el cuantificador + significa que debe coincidir con 1 o más del carácter precedente.
  • projects: Se trata de una cadena literal, que coincidirá exactamente con «projects».

Es hora de poner a prueba la regla. Actualiza el código `main.py` con este fragmento:

import urllib.request

import re

response = urllib.request.urlopen("https://pypi.org/")

html = response.read().decode("utf-8")

matches = re.search("([0-9,]+) projects", html)

projects = matches.group(1)

print(projects)

Estamos utilizando el método urlopen de la biblioteca urllib para realizar una solicitud GET al sitio web pypi.org. Leemos la respuesta en la variable html. Aplicamos la regla de expresión regular al contenido HTML e imprimimos el primer grupo coincidente.

Ejecuta el código con el comando `python main.py` y comprueba el resultado: mostrará el número de proyectos del sitio.

Extracción de enlaces

Ahora que tenemos un sencillo scraper capaz de obtener el documento HTML de un sitio, juguemos un poco con el código.

Podemos extraer todos los enlaces con esta regla:

href=[\'"]?([^\'" >]+)

Esta expresión regular consta de varias partes:

  • href=: es una cadena literal, coincidirá exactamente con los caracteres «href=».
  • [\'"]?: los corchetes [] coinciden con cualquier carácter dentro de ellos; en este caso, los caracteres ' o "; el cuantificador ? significa que coincide con cero o uno de los caracteres anteriores, es decir, el valor de href puede estar entre comillas dobles, comillas simples o no tener ninguna.
  • ([^\'" >]+): se trata de un grupo de captura, indicado por los paréntesis; el ^ dentro de los corchetes significa negación, coincidirá con cualquier carácter que no sea ', ", > o un espacio; el cuantificador + significa que debe coincidir con uno o más de los caracteres anteriores, lo que significa que el grupo capturará uno o más caracteres que coincidan con el patrón.

Extracción de imágenes

Una cosa más y ya casi habremos terminado de escribir las reglas de expresiones regulares: tenemos que extraer las imágenes. Usemos esta regla:

<img.*?src="(.*?)"

Esta expresión regular consta de varias partes:

  • <img: Se trata de una cadena literal; coincidirá exactamente con los caracteres «<img».
  • .*?: el .* coincide con cualquier carácter (excepto una nueva línea) 0 o más veces, y el cuantificador ? significa que debe coincidir con el menor número posible del carácter anterior; esto se utiliza para coincidir con cualquier carácter que aparezca antes del atributo src en la etiqueta <img>, y permite que el patrón coincida con cualquier etiqueta <img> independientemente del número de atributos que tenga.
  • src=": se trata de una cadena literal; coincidirá exactamente con los caracteres «src=».
  • (.*?): se trata de un grupo de captura, indicado por los paréntesis; .*? coincide con cualquier carácter (excepto un salto de línea) 0 o más veces, y el cuantificador ? significa que debe coincidir con el menor número posible del carácter precedente; este grupo captura el valor src de la etiqueta <img>.
  • ": esta es una cadena literal; coincidirá exactamente con el carácter "".

Pongámoslo a prueba. Reemplaza el fragmento de código anterior por este:

import urllib.request

import re

response = urllib.request.urlopen("https://pypi.org/")

html = response.read().decode("utf-8")

images = re.findall('<img.*?src="(.*?)"', html)

print(*images, sep = "\n")

El resultado de este código mostrará una lista con todos los enlaces de imágenes de la página de Pypi.

Limitaciones

El web scraping con expresiones regulares puede ser una herramienta poderosa para extraer datos de sitios web; sin embargo, también tiene sus limitaciones. Uno de los principales problemas del uso de expresiones regulares para el web scraping es que puede fallar cuando cambia la estructura del HTML.

Por ejemplo, consideremos el siguiente fragmento de código en el que intentamos extraer el texto del h2 utilizando expresiones regulares:

<html>

   <head>

       <title>Example Title</title>

   </head>

   <body>

       <h1>Page Title</h1>

       <p>This is a paragraph under the title</p>

       <h2>First Subtitle</h2>

       <p>First paragraph under the subtitle</p>

       <h2>Second Subtitle</p>

   </body>

</html>

Compara la primera etiqueta <h2> con la segunda. Es posible que observes que la segunda etiqueta <h2> no está correctamente cerrada, y que el código tiene </p> en lugar de </h2>. Actualicemos el fragmento con esto:

import re

html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"

headingTags = re.findall("<h2>(.*?)</h2>", html)

print(*headingTags, sep = "\n")

Ejecutemos el código y comprobemos el resultado:

First Subtitle

Falta el texto de la segunda etiqueta de encabezado. Esto ocurre porque la regla de expresiones regulares no coincide con la etiqueta de encabezado sin cerrar.

Una solución a este problema es utilizar una biblioteca como BeautifulSoup, que permite navegar y buscar en la estructura del árbol HTML, en lugar de depender de expresiones regulares. Con BeautifulSoup, puedes extraer el título de una página web así:

from bs4 import BeautifulSoup

html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"

soup = BeautifulSoup(html, 'html.parser')

for headingTag in soup.findAll('h2'):

   print(headingTag.text)

BeautifulSoup consigue extraer etiquetas malformadas y el resultado es el siguiente:

First Subtitle

Second Subtitle

Este enfoque es más robusto ante los cambios en la estructura HTML, ya que no depende de patrones específicos en el código HTML. Si te interesa saber más sobre BeautifulSoup, este artículo es una lectura perfecta.

Otra solución es utilizar una API de web scraping como WebScrapingAPI, que elimina las complejidades del web scraping y te permite extraer fácilmente los datos que necesitas sin preocuparte por la estructura HTML subyacente.

Con WebScrapingAPI, puedes extraer datos de cualquier sitio web con una simple llamada a la API, y esta gestiona automáticamente los cambios en la estructura HTML.

Reflexiones finales

El análisis de datos con expresiones regulares puede ser una herramienta poderosa para extraer datos de sitios web.

En este artículo, hemos abordado los conceptos básicos de las expresiones regulares, cómo utilizarlas para analizar HTML y algunos de los retos a los que te puedes enfrentar al usarlas. También hemos visto cómo bibliotecas como BeautifulSoup pueden utilizarse como solución alternativa.

Has aprendido a extraer datos de páginas web utilizando expresiones regulares y a mejorar la fiabilidad de tu código mediante el uso de una biblioteca más robusta, como BeautifulSoup.

El web scraping puede ser una tarea que requiere mucho tiempo, pero con las herramientas adecuadas, puede resultar fácil y eficiente. Si buscas una solución de web scraping que te ahorre tiempo y esfuerzo, prueba WebScrapingAPI.

Ofrecemos una prueba gratuita de 14 días, durante la cual podrás probar nuestro servicio y comprobar las ventajas de utilizar una API de web scraping.

Acerca del autor
Mihai Maxim, Desarrollador Full Stack @ WebScrapingAPI
Mihai MaximDesarrollador Full Stack

Mihai Maxim es desarrollador full stack en WebScrapingAPI, donde colabora en todas las áreas del producto y ayuda a crear herramientas y funciones fiables para la plataforma.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.