Parse HTML like a Pro: Dominio del Web Scraping con Python y Regex
Suciu Dan el 13 abr 2023

La cantidad de datos disponibles en Internet ha crecido en las últimas décadas. Los seres humanos consumen estos datos con fines muy diversos, desde intereses personales hasta investigación empresarial.
Sin embargo, si estos datos no se devuelven formateados, como XML o JSON, puede resultar difícil o imposible leerlos mediante aplicaciones informáticas. Aquí es donde entra en juego la técnica del web scraping.
El web scraping es el proceso de recopilación y procesamiento de datos en bruto de Internet. Estos datos se analizan y se utilizan para diversos fines, como la inteligencia de precios, la investigación de mercados, el entrenamiento de modelos de IA, el análisis de sentimientos, las auditorías de marca y las auditorías de SEO.
Uno de los aspectos clave del web scraping es el análisis sintáctico de HTML. Para ello se pueden utilizar varias 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 regex y Python. También discutiremos algunos de los desafíos y soluciones alternativas que vienen con web scraping.
Al final del artículo, comprenderá perfectamente el tema y las distintas herramientas y técnicas disponibles.
Análisis Regex básico
La mayoría de los lenguajes de programación de uso general admiten regex. Puede utilizar expresiones regulares en una amplia variedad de lenguajes de programación, como Python, C, C++, Java, Rust, OCaml y JavaScript.
Here’s what a regex rule for extracting the value from the <title> tag looks like:
<title>(.*?)</title>
Asusta, ¿verdad? Ten en cuenta que esto es el principio. Pronto nos adentraremos en la madriguera del conejo.
Para este artículo, estoy usando 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)
Puede ejecutar este código ejecutando 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 regex. La función `re.search()` busca un patrón específico dentro de una cadena. El primer argumento es el patrón regex, y el segundo argumento es la cadena en la que estamos buscando.
The regex pattern in this example is "<title>(.*?)</title>". It consists of several parts:
- <title>: This is a literal string, it will match the characters "<title>" exactly.
- (.*?): Se trata de un grupo de captura, denotado por paréntesis. El carácter . coincide con cualquier carácter simple (excepto una nueva línea), y el cuantificador * significa que coincide con 0 o más del carácter precedente. Además, ? hace que * no sea avaro, lo que significa que se detendrá en cuanto encuentre la etiqueta de cierre.
- </title>: This is also a literal string, it will match the characters "</title>" exactly.
La función re.search() devuelve un objeto coincidente 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 título de apertura y cierre.
Este texto se asignará a la variable title, y la salida será "Scraping".
Análisis Regex avanzado
Extraer los datos de una sola etiqueta HTML no es tan útil. Te da una idea de lo que puedes hacer con expresiones regulares, pero no puedes usarlo en una situación del mundo 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 utilizar esta expresión regular:
([0-9,]+) proyectos
La expresión regular coincidirá con cualquier cadena que empiece 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, denotado por los paréntesis; los corchetes [0-9,] coinciden con cualquier dígito de 0 a 9 y el carácter `,`; el cuantificador + significa coincidir con 1 o más del carácter precedente.
- proyectos: Es una cadena literal, coincidirá exactamente con "proyectos".
Es hora de poner la regla a prueba. 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 usando el método urlopen de la librería urllib para hacer una petición GET a la web pypi.org. Leemos la respuesta en la variable html. Ejecutamos la regla regex contra el contenido HTML e imprimimos el primer grupo coincidente.
Ejecute el código con el comando `python main.py` y compruebe la salida: mostrará el número de proyectos del sitio.
Extracción de enlaces
Ahora que tenemos un simple scraper que puede obtener el documento HTML de un sitio, vamos a jugar 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 que se encuentre dentro de ellos, en este caso, los caracteres ' o "; el cuantificador ? significa que debe coincidir con cero o uno de los caracteres precedentes, lo que significa que el valor href puede estar encerrado entre " o ' o ninguno.
- ([^\'" >]+): se trata de un grupo de captura, denotado 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 coincidir con 1 o más del carácter precedente, 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 hemos terminado de escribir reglas regex: tenemos que extraer las imágenes. Usemos esta regla:
<img.*?src="(.*?)"
Esta expresión regular consta de varias partes:
- <img: This is a literal string, it will match the characters "<img" exactly.
- .*?: the .* match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this is used to match any character that appears before the src attribute in the <img> tag, and it allows the pattern to match any <img> tag regardless of the number of attributes it has.
- src=": es una cadena literal, coincidirá exactamente con los caracteres "src=".
- (.*?): this is a capturing group, denoted by the parentheses; the .*? match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this group captures the src value of the <img> tag.
- ": es una cadena literal, coincidirá exactamente con el carácter ".
Pongámoslo a prueba. Sustituye el fragmento de código anterior por éste:
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")
La salida de este código mostrará una lista con todos los enlaces de imágenes de la página 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, considere el siguiente ejemplo de código en el que intentamos extraer el texto del h2 utilizando regex:
<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>
Compare the first <h2> tag with the second one. You may notice the second <h2> is not properly closed, and the code has </p> instead of </h2>. Let’s update the snippet with this:
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")
Vamos a ejecutar el código y comprobar la salida:
Primer subtítulo
Falta el texto de la segunda etiqueta de encabezamiento. Esto ocurre porque la regla regex no coincide con la etiqueta de encabezamiento no cerrada.
Una solución a este problema es utilizar una biblioteca como BeautifulSoup, que permite navegar y buscar en la estructura de á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 las etiquetas malformadas y la salida tiene este aspecto:
Primer subtítulo
Segundo subtítulo
Este enfoque es más robusto a 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 abstrae las complejidades del web scraping y le permite extraer fácilmente los datos que necesita sin preocuparse de la estructura HTML subyacente.
Con WebScrapingAPI, puede extraer datos de cualquier sitio web con una simple llamada a la API, y gestiona automáticamente los cambios en la estructura HTML.
Reflexiones finales
El análisis sintáctico de datos con expresiones regulares puede ser una potente herramienta para extraer datos de sitios web.
En este artículo, hemos discutido los fundamentos de las expresiones regulares, cómo usarlas para analizar HTML y algunos de los desafíos que puedes encontrar al usarlas. También hemos visto cómo librerías como BeautifulSoup pueden usarse 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 utilizando una librería más robusta como BeautifulSoup.
El web scraping puede ser una tarea que consume mucho tiempo, pero con las herramientas adecuadas, puede ser fácil y eficiente. Si está buscando una solución de web scraping que le ahorre tiempo y esfuerzo, pruebe WebScrapingAPI.
Ofrecemos una prueba gratuita de 14 días, en la que puede probar nuestro servicio y comprobar las ventajas de utilizar una API de web scraping.
Noticias y actualidad
Manténgase al día de las últimas guías y noticias sobre raspado web suscribiéndose a nuestro boletín.
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artículos relacionados

Sumérjase en el papel transformador de los datos financieros en la toma de decisiones empresariales. Comprender los datos financieros tradicionales y la importancia emergente de los datos alternativos.


Descubra cómo extraer y organizar datos de forma eficaz para el raspado web y el análisis de datos mediante el análisis sintáctico de datos, las bibliotecas de análisis sintáctico HTML y los metadatos de schema.org.


¿Son los selectores XPath mejores que los selectores CSS para el web scraping? Conozca los puntos fuertes y las limitaciones de cada método y tome la decisión correcta para su proyecto.
