Web Scraping con Python y BeautifulSoup

Antes de comenzar con el tutorial para hacer web scraping con Python haré una introducción sobre qué es el scraping y las diversas formas de extraer contenido de las páginas web, por ejemplo la más recomendada es utilizando una API (Application Programming Interface).

El scraping o en este caso web scraping es un conjunto de técnicas utilizadas para extraer el contenido de forma autómata de páginas web de un modo seguro, cuyo origen se remonta al Web Crawler, aquellos motores de búsqueda como Lycos o Altavista (ahora de Yahoo) para rastrear e indexar páginas web.

Es necesario tener un conocimiento medio sobre la estructura HTML y saber analizarla a través del DOM ya que, aunque tu conocimiento en Python esté al nivel de un ninja necesitarás saber qué vas a rastrear.

Extracción a través de HTML:

  • Meta (keywords, description, author, etc.)
  • Etiquetas (h1, p, span, etc.)
  • Enlaces (a)
  • CSS (id, class)

Para obtener un resultado positivo deberás de tener las habilidades básicas en Python, que es el lenguaje utilizado para este tutorial. En el caso contrario te invito a que eches un vistazo a nuestro Tutorial de Python 3 gratuito o recurras a software, extensiones web o APIs que puedan ayudarte.

Para qué sirve el Web Scraping

Como he comentado al principio sirve para rastrear contenido web por medio de spiders (arañas) que son programas para conectar y rastrear la urls que queremos analizar y posteriormente, almacenarlas en el formato que mejor nos convenga.

El análisis y la estructuración de estos datos junto a otras fuentes permite a muchas empresas crear productos y servicios financieros, logísticos, medio ambiente, gubernamentales y defensa entre otros, ya que este campo está enmarcado en el Big Data y la Inteligencia Artificial.

Casos de uso conocido en la web

  • Inteligencia de negocio (BI)
  • Recursos Humanos
  • Marketing y competencias de mercado
  • Tendencias
  • Comparadores de precios
  • Buscadores
  • Comunicación (meteorología, resultados deportivos, bolsa)

Qué es Beautiful Soup

Se trata de una biblioteca externa de Python para analizar documentos HTML y XML incluido el formato incorrecto, es decir etiquetas que no están cerradas, etc. Si quieres indagar más puedes ver toda su documentación en su página.

En este tutorial utilizaremos bs4 y lo instalaremos desde nuestra shell. pip es un gestor de paquetes que por defecto está incluido en Python desde la versión 2.7.9 +.

pip install bs4

La librería urllib es un paquete que reúne diferentes módulos del los que utilizaremos urllib.request, encargado de realizar las peticiones HTTP por get, post, put, etc. En el caso de que estés desarrollando en la nube con Colab, Kaggle o AWS con importarlas basta.

Configuración del entorno

Una vez instaladas las librerías y configurado nuestro entorno de desarrollo procedemos a crear un documento llamado myscraper.py e importar las librerías.

from bs4 import BeautifulSoup
import urllib.request

A continuación haremos una petición HTTP por get pasándole como parámetro la url que queremos rastrear. En este caso extraeremos todas las urls del artículo Web Scraping de Wikipedia y el número de enlaces que contiene el documento.

¿Es legal? al final del tutorial hablaremos sobre la legalidad del web scraping y sus límites pero en este caso, no utilizaremos datos sensibles ni nos apropiaremos del contenido.

url = 'https://es.wikipedia.org/wiki/Web_scraping'
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')

Extracción del contenido del sitio web

Si inspeccionamos la página de Wikipedia y nos dirigimos al título nos mostrará el ID de la etiqueta.

Buscar el ID de una etiqueta

Ahora gracias a Beautiful Soup encontraremos el elemento por su etiqueta y el ID.

title = soup.find(id='firstHeading')
print('Artículo:', soup.title.string)
# Salida
Artículo: Web scraping

También podemos ir directamente a la etiqueta title que nos devolverá el título de la página de la siguiente forma.

print('Título del sitio:', soup.title.string)
# Salida
Título del sitio: Web scraping - Wikipedia, la enciclopedia libre

Ahora vamos a ver el tipo de urls que posiblemente nos encontremos y veamos como crear un filtro básico para segmentarlas:

  • #tag
  • /slug
  • http://dominio.com/slug
  • https://dominio.com/slug

Además, posiblemente veas javascript y otro tipo de enlaces que no nos interese así que también lo filtraremos. Primero le diremos a soup que encuentre la etiqueta a donde href sea True.

# Número de enlaces encontrados
count = 0
# Tipo de etiqueta
tag = soup('a', href=True)

Aplicaremos el filtro dentro del bucle for para aquellos enlaces que cuyo índice [0] contenga la letra h. Se puede crear una regla más compleja y es recomendable para proyectos mas robustos que este ejercicio.

# Filtro para https, tags y slugs
indx = 'h'

for link in tag:
    link = link.get('href')
    # Filtro indx según primer caracter
    if link[0] == indx:
        print(link)
        count += 1
print('Número de enlaces:', count)

Por último, he creado la variable count para que nos devuelva el número de enlaces que hemos extraído. La he metido dentro del bucle por una razón específica y es que cuando BeautifulSoup rastrea lo hace en toda la página. Si aplicamos el filtro a enlaces que contengan http o https, por lógica queremos saber el número que contenga este filtro y no todos.

Veamos el código completo que podréis descargarlo al final desde GitHub:

import urllib.request
from bs4 import BeautifulSoup

# Petición/lectura
url = 'https://es.wikipedia.org/wiki/Web_scraping'
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')

# Título del sitio en formato str
title = soup.find(id='firstHeading')

print('Título del sitio:', soup.title.string)
print('Artículo:', title.string, '\n')

# Número de enlaces
count = 0
# Tipo de etiqueta
tag = soup('a', href=True)
# Filtro para https, tags y slugs
indx = 'h'

urls = []
for link in tag:
    link = link.get('href')
    # Filtro indx según primer caracter
    if link[0] == indx:
        urls.append(link)
        count += 1
            
# Imprimimos todas las urls
for url in urls:
    print('URL:', url)
print('\nNúmero de enlaces:', count)
# Salida

Título del sitio: Web scraping - Wikipedia, la enciclopedia libre
Artículo: Web scraping

URL: https://web.archive.org/web/20170729001446/https://sitelabs.es/web-scraping-introduccion-y-herramientas/
URL: https://sitelabs.es/web-scraping-introduccion-y-herramientas/
URL: https://web.archive.org/web/20170729001446/https://sitelabs.es/web-scraping-introduccion-y-herramientas/
URL: https://sitelabs.es/web-scraping-introduccion-y-herramientas/
URL: https://www.wikidata.org/wiki/Q665452
URL: https://academic.microsoft.com/v2/detail/2780183508
URL: https://www.wikidata.org/wiki/Q665452
........ (hemos omitido parte de los enlaces)........

Número de enlaces: 35

Con este resultado hemos cumplido con el objetivo de obtener los enlaces activos de la página y mostrar el número total que hemos extraído. Además, hemos alamcenado los datos en una lista para que posteriormente, sean convertidas a cualquier formato, por ejemplo en JSON o Excel.

Si haces una prueba modificando el filtro que hemos creado para obtener los valores sugeridos, verás que obtendrás el resultado que has aplicado. En el caso de que no haya resultados, con un try/except podemos solventarlo.

# Modificación de la regla
# Filtro para https, tags y slugs
indx = '/'

Y para que sea más visual y podamos identificar si es una url, slug o un tag podemos modificar el bucle para que imprima todas las urls de la lista.

# Imprimimos todas las urls
for url in urls:
    if url.startswith('http'):
        print('URL:', url)
    elif url.startswith('/'):
        print('SLUG:', url)
    else:
        print('TAG:', url)
Título del sitio: Web scraping - Wikipedia, la enciclopedia libre
Artículo: Web scraping 

SLUG: /wiki/Wikipedia:VER
SLUG: /wiki/Wikipedia:FF
SLUG: /wiki/Programa_inform%C3%A1tico
SLUG: /wiki/Sitio_web
SLUG: /wiki/World_Wide_Web
SLUG: /wiki/Hypertext_Transfer_Protocol
........ (hemos omitido parte de los enlaces)........

Número de enlaces: 116

Si queremos obtener todas las urls de la página, basta con suprimir o comentar el siguiente código que está dentro del bucle for.

# Filtro indx según primer caracter
    if link[0] == indx:

Legalidad del Web Scraping

El Web Scraping en España es totalmente legal ya que el problema no está en el uso de crawlers para el rastreo de páginas web, sino sobre qué hacemos con dicha información cuando está en nuestro poder. Sin embargo, fuera de España hay países que no lo permiten y la persona que pone en práctica este tipo de técnicas incurre en un delito.

Las páginas web donde predominan los perfiles de usuario como Facebook o LinkedIn están prohibido scrapearlas por la RGPD vigente del 27 de abril de 2016.

También el mundo del comercio electrónico se blinda en sus condiciones o términos de uso como pueden ser Amazon o AliExpress, donde scrapear su contenido como pueden ser artículos, imágenes o contenido sensible pueden llevarte a los tribunales.

Puntos a tener en cuenta

  • Competencia desleal
  • Delitos de propiedad intelectual
  • Reglamento General de Protección de Datos (RGPD)

Es importante tener en cuenta estos puntos y entender el límite y la legalidad del mismo. Si vas a hacer scraping asegúrate de saber que no estás incurriendo en un delito, y que el propietario no tenga en sus términos y condiciones la prohibición del mismo.

Por último, debes de tener claro qué vas a hacer con el contenido y de cómo vas a utilizarlo. El uso legal y de forma inteligente del web scraping puede darte muchas ventajas frente al de tus competidores más directos.

Conclusión

Existen muchas formas de hacer scraping y no solo de enlaces como he utilizado en los ejemplos, sino sobre cualquier etiqueta con contenido o de las propias meta para capturar descripciones, keywords y demás.

Si tu interés vas mas allá y quieres especializarte en este campo, sería interesante que empezases con estructuras de datos y SQL. En Coursera hay diferentes cursos que te podrán ayudar como introducción.

Descarga el código del ejercicio en GitHub

¿Te ha parecido útil?