Python en Español

Moderna web scraping en Python (Beautiful Soup y Selenium)

En este tutorial, hablaremos sobre el web scraping usando Python y cómo recopilar información de la páginas web usando varias bibliotecas para scraping de Python, como Beautiful Soup, Selenium y algunas otras herramientas mágicas como PhantomJS.

Aprenderás a analizar páginas web estáticas páginas dinámicas (contenido cargado de Ajax), iframes, a obtener elementos HTML específicos, a manejar cookies y mucho más.


En este tutorial utilizaremos Python 3.x. Así que comencemos.

 

Qué es Web Scraping con Python

En general, web scraping es el proceso por el que, extrayendo datos desde la web, tú puedes analizar esos datos y convertirlos en información útil

También, los datos obtenidos los puedes almacenar en una base de datos o en un formato tabular como CSV, XLS, etc. de tal manera que puedas acceder a la información fácilmente.

La información obtenida puede ser enviada a una libraría como NLTK y procesar posteriormente para entender a qué se refiere esa página.

En resumen, web scraping es la descarga de datos de la web en un formato legible para humanos, tal que puedas beneficiarte con ello.

 

Beneficios del Web Scraping

Te preguntarás, ¿por qué hacer web scraping si tengo Google? Bueno, no reinventamos la rueda aquí. El web scraping no es solo para la creación de motores de búsqueda.

Puedes obtener las páginas web de tu competidor y analizar los datos para ver con qué tipo de productos están satisfechos sus clientes a partir de sus respuestas. Todo esto GRATIS.

Una herramienta SEO exitosa como Moz, obtiene y rastrea toda la web y procesa los datos por ti para que puedas ver el interés de la gente y cómo competir con otros en su campo para estar en la cima.

Estos son solo algunos usos simples del web scraping. Los datos obtenidos significan ganar dinero :).

 


Instalar Beautiful Soup

Asumo que ya tienes experiencia en Python básico, así que instalemos nuestra primera biblioteca de web scraping de Python que es Beautiful Soup.

Para instalar Beautiful Soup, debes usar pip o puedes instalarlo desde la fuente.

Yo instalaré usando pip de esta manera:

$ pip install beautifulsoup4

Para verificar si está instalado o no, abre tu editor y escribe lo siguiente:

from bs4 import BeautifulSoup

Entonces lo hacemos correr:

$ python myfile.py

Si corre sin errores, entonces Beautiful Soup ha sido instaldo sin problemas. Ahora, veamos como usar Beautiful Soup.

 


Tu primer Web Scraper

Dale un vistazo a este ejemplo sencillo. Vamos a extraer el título de la página usando Beautiful Soup:


El resultado es:

web scraping en Python

Usamos la librería urlopen para conectarnos con la página web que queremos analizar, luego leemos el HTML devuelto usando el método html.read().

El HTML devuelto es transformado en un objeto Beautiful Soup que tiene una estructura jerárquica.

Eso significa que si necesitas extraer cualquier elemento HTML, solamente necesitas conocer las etiquetas que lo rodean, como veremos más adelante.

Manejando las excepciones HTTP

Por alguna razón urlopen puede devolver un error. Podría ser 404 si la página no existe o 500 si existe un error interno del servidor, entonces, debemos evitar la caída del script manejando la excepción de alguna manera como la siguiente:


Genial, ¿y si el servidor no funciona o si escribiste el dominio incorrectamente?

Manejando excepciones URL

Necesitamos manejar este tipo de excepciones también. Esta excepción es URLError, por lo que nuestro código será así:


Bueno, lo último que necesitamos verificar es la etiqueta devuelta. Podemos haber escrito una etiqueta incorrecta o tratar de borrar una etiqueta que no se encuentra en la página y esto devolverá el objeto None, por lo que debemos verificar si el objeto es None.

Esto se puede hacer usando una declaración condicional simple como esta:


Genial, nuestro scrapar está haciendo un buen trabajo. Ahora, podemos hacer extraer toda la página o una etiqueta específica.

¿Qué hay de una caza más profunda?

 

Hacer scraping de los elementos HTML usando la clase Attribute

Ahora intentemos ser selectivos haciendo scraping en algunos elementos HTML basados en sus clases CSS.

El objeto Beautiful Soup tiene una función llamada findAll que extrae o filtra elementos en función de sus atributos.

Podemos filtrar todos los elementos h2 cuya clase es widget-title:

tags = res.findAll("h2", {"class": "widget-title"})

Podemos usar una estructura for para iterar sobre ellos y hacer lo que sea con cada uno.

Nuestro código será así:


Este código devuelve todas las etiquetas h2 con una clase llamada widget-title donde estas etiquetas son los títulos de publicaciones de la página de inicio.

Usamos la función getText para imprimir solo el contenido interno de la etiqueta, pero si no usaste getText, terminarás con las etiquetas incluido todo lo que haya dentro de ellas.

Verifica la diferencia:

Cuando usamos getText():

Scraping utilizando gettext

Y cuando usamos sin getText():

Scraping sin gettext

 


Obtener las etiquetas HTML usando findAll

Vimos cómo findAll function filtra las etiquetas por clase, pero esto no es todo.

Para filtrar una lista de etiquetas, reemplaza la línea resaltada del ejemplo anterior con la siguiente línea:

tags = res.findAll("span", "a" "img")

Este código obtiene todas las etiquetas span, a e img del código HTML descargado.

Además, puede extraer etiquetas que tengan estas clases:

tags = res.findAll("a", {"class": ["url", "readmorebtn"]})

Este código extrae todas las etiquetas a que tienen las clases readmorebtn y url.

Puedes filtrar el contenido en función del texto interno, utilizando el argumento text de esta manera:

tags = res.findAll(text="Python Programming Basics with Examples")

La función findAll devuelve todos los elementos que coinciden con los atributos especificados, pero si deseas devolver un solo elemento, puedes usar el parámetro limit o usar la función find que solo devuelve el primer elemento.

 

Buscar el enésimo hijo usando Beautiful Soup

El objeto Beautiful Soup tiene muchas características potentes, puedes obtener elementos hijos directamente así:

tags = res.span.findAll("a")

Esta línea obtendrá el primer elemento span en el objeto Beautiful Soup y luego obtendrá todos los elementos a en ese span.

¿Qué pasa si necesitas conseguir el enésimo hijo?

Puedes usar la función select de esta manera:

tag = res.find("nav", {"id": "site-navigation"}).select("a")[3]

Esta línea obtiene el elemento nav con id site-navigation y luego tomamos la cuarta etiqueta a de ese elemento nav.

¡Beautiful Soup es una librería poderosa!

 

Encontrar etiquetas usando Regex

En un tutorial anterior, hablamos acerca de las expresiones regulares y vimos cuán poderoso es usar regex para identificar patrones comunes como correos electrónicos, URLs y mucho más.

Afortunadamente, Beautiful Soup tiene esta prestación, puedes pasar patrones de expresiones regulares para que coincidan con etiquetas específicas.

Imagina que deseas descargar algunos enlaces que coinciden con un patrón específico, como enlaces internos o enlaces externos específicos, o descargar algunas imágenes que residen en una ruta específica.

El motor Regex hace que sea muy fácil lograr tales trabajos.


Estas líneas encontrarán todas las imágenes PNG ubicadas en ../uploads/ y que empiezan con photo_.

Este es un ejemplo simple para mostrarte el poder de las expresiones regulares combinadas con Beautiful Soup.

 


Scraping JavaScript

Supongamos que la página que necesitas hacer scraping tiene otra página de carga que lo redirecciona a la página requerida y la URL no cambia o hay algunas piezas de la página descargada por las que carga su contenido dinámicamente con Ajax.

Nuestro scraper no cargará ningún contenido de estos ya que el scraper no ejecuta el JavaScript requerido para cargar ese contenido.

Normalmente tu navegador ejecuta el JavaScript y carga cualquier contenido. Y en realidad, eso es lo que haremos utilizando nuestra segunda biblioteca de depuración web de Python que se llama Selenium.

La biblioteca de Selenium no incluye su propio navegador. Necesita instalar un navegador de terceros (o un controlador Web) para poder funcionar. Esto además del navegador en sí, por supuesto.

Puedes elegir entre Chrome, Firefox, Safari o Edge.

Si instalas alguno de estos controladores, digamos Chrome, se abrirá una instancia del navegador y se cargará la página, luego podrás descargar o interactuar con tu página.

Usando ChromeDriver con Selenium

Primero, deberás instalar la librería Selenium de esta manera:

$ pip install selenium

Luego, descargas el controlador de Chrome de aquí en tu sistema.

Ahora, puedes cargar tu página de esta maner:


La salida se verá como esto:

Usando ChromeDriver con Selenium

Bastante sencillo, ¿verdad?

No interactuamos con los elementos de la página, por lo que aún no vimos el poder de Selenium. Solo aguarda.

 

Selenium Web Scraping

Puede que te guste trabajar con controladores de navegadores, pero hay mucha más gente que ejecuta código en segundo plano sin ver correr en acción.

Para este propósito, existe una increíble herramienta llamada PhantomJS que carga tu página y ejecuta su código sin abrir ningún navegador.

PhantomJS te permite interactuar con cookies de páginas descargadas y JavaScript sin dolores de cabeza.

Además, puedes usarlo igual que con Beautiful Soup para hacer scraping de páginas y elementos dentro de esas páginas.

Descarga PhantomJS de aquí y pónlo en tu PATH para que podamos usarlo como un controlador con Selenium.

Ahora, apliquemos nuestro trabajo de web scraping con Python usando Selenium con PhantomJS de la misma manera que lo hicimos con el controlador web de Chrome.


El resultado es:

Selenium Web Scraping

¡Increíble! Funciona muy bien.

Puedes acceder a los elementos de muchas maneras tales como:


Todas estas funciones devuelven solamente un elemento. Puedes devolver varios elementos de esta manera:

Selenium page_source

Puedes usar el poder de Beautiful Soup en el contenido devuelto desde Selenium, usando page_source así:


El resultado es:

Selenium page_source

Como puedes ver, PhantomJS hace esto súper fácil cuando hacemos web scraping en los elementos HTML. Veamos más.

 

Extraer el contenido de un iframe usando Selenium

Tu página extraída puede contener un iframe que contiene datos.

Si tratas de hacer scraping en una página que tiene un iframe, no podrás obtener el contenido del iframe, por lo que necesitarás la fuente del iframe.

Puedes usar Selenium para hacer scrape a los iframes al cambiar hacia el iframe que deseas analizar.


El resultado es:

Extraer el contenido de un iframe usando Selenium

Verifica el URL actual. Es el URL del iframe, no de la página original.

 


Extraer el contenido de un iframe usando Beautiful Soup

Puedes obtener el URL del iframe usando la función find. Luego, puedes hacer scrap en ese URL.


¡¡Increíble!! Aquí usamos otra técnica de web scraping con Python donde extraemos el contenido del iframe desde dentro de una página.

 

Gestionando las llamadas Ajax usando (Selenium+ PhantomJS)

Puedes usar Selenium para extraer contenido después de hacer llamadas Ajax.

Como hacer click en un botón que extrae el contenido que necesitas extraer. Revisa el siguiente ejemplo:


El resultado es:

Gestionando las llamadas Ajax usando

Aquí hemos extraído una página que contiene un botón, he hicimos click sobre ese botón, que a su vez hizo una llamada Ajax y obtuvimos el texto. Luego, grabamos un caputra de pantalla de esa página.

Hay una pequeña cosa que debemos considerar, es acerca del tiempo de espera.

Sabemos que la carga de la página no puede exceder de 2 segundos para ser totalmente descargada, pero es no es una buena solución, ya que el servidor puede tomar más tiempo o la conexión puede ser lenta, entre otras razones.

 

Esperar a que la llamada Ajax sea completada usando PhantomJS

La mejor solución es rvisar la existencia de un elemento HTML al final de la página. Si existe, eso significa que la llamada Ajax ha concluido satisfactoriamente.

Revisa este ejemplo:


El resultado es:

Espera a que se completen las llamadas Ajax

Aquí hacemos clic en un botón Ajax que realiza una llamada REST y devuelve el resultado JSON.

Comprobamos el texto del elemento div si es “HTTP 200 OK” con un tiempo de espera de 10 segundos, luego guardamos la página de resultados como una imagen como se muestra.

Puedes verificar muchas cosas como:

Cambio de URL con EC.url_changes()

Nueva ventana abierta usando EC.new_window_is_opened()

Cambios en el título usando EC.title_is()

Si tiene alguna redirección de página, puedes ver si hay un cambio en el título o en la URL para verificarla.

Hay muchas condiciones para verificar, solo tomamos un ejemplo para mostrarte cuánto poder tiene.

¡Buenísimo!

 

Manejando Cookies

Algunas veces, cuando escribes código Python para hacer web scraping, es muy importante tener cuidado con las cookies del sitio web que se está extrayendo.

Quizá necesites borrar las cookies o quizá necesites guardarlas en un archivo para usarlas en conexiones posteriores.

Existen muchos escenarios en los que necesitas gestionar las cookies.

Para recuperar cookies del sitio, puedes usar la función get_cookies(), de esta manera:


El resultado es:

Manejando Cookies

Para borrar cookies, puedes usar la función delete_all_cookies() de esta manera:


 

Web Scraping vs. Web Crawling

Hemos hablado sobre el web scraping con Python y cómo analizar páginas web, ahora algunas personas se confunden con scraping y crawling.

Web Scraping trata sobre el análisis de páginas web y la extracción de datos para cualquier fin, como vimos.

El Web crawling consiste en recolectar todos los enlaces que encuentre en una página y rastrearlos sin escala, y esto con el propósito de indexar, como lo hacen Google y otros motores de búsqueda.

El web scraping con Python es muy divertido, pero antes de que terminemos nuestra discusión, hay algunos puntos difíciles que pueden evitar el scrpaing como Google reCaptcha.

Google reCaptcha se vuelve más difícil ahora nuestra tarea, y hasta ahora, no se ha podido encontrar una buena solución en la cual confiar.

Espero que encuentres el tutorial útil. Sigue regresando.

Gracias.

Mokhtar Ebrahim
Estoy trabajando como administrador de sistemas Linux desde 2010. Soy responsable de mantener, proteger y solucionar problemas de servidores Linux para múltiples clientes de todo el mundo. Me encanta escribir guiones de shell y Python para automatizar mi trabajo.

4 thoughts on “Moderna web scraping en Python (Beautiful Soup y Selenium)

  1. Hola! Antes que nada felicitarte por este magnífico post, es de lo más completo que hay en español. Sólo una pregunta:
    ¿Cómo podría hacer para abrir 2 páginas cargadas con JS al mismo tiempo?
    Es decir, extraigo enlaces de una primera url y la guardo en una lista. luego recorro esa lista de enlaces para extraer información de dichas urls también cargadas con JS.
    Muchas gracias de antemano!

    1. ¡Muchas gracias!
      Para obtener enlaces de JavaScript, puede usar Selenium para simular clics, luego puede guardar el enlace eliminado y usar Beautiful Soup o cualquier biblioteca para raspar los datos.

      from selenium import webdriver

      import time

      browser = webdriver.PhantomJS()

      browser.get("https://your_url_here")

      browser.find_element_by_tag_name("button").click()

  2. Que excelente post! Del mas completo y mejor explicado que encontré!! Felicitaciones!

    Te voy a consultar algo que no encontré a lo largo del post, tengo que traerme info de los resultados de busqueda de una pagina, por ejemplo, en stackoverflow.com busco “aprender python” y necesito traerme los resultados de esa pagina, no logre hacerlo ya que no me muestra nada, me podrías ayudar con eso?

    1. ¡Muchas gracias!
      Puedes usar Selenium para lograr tu objetivo de la misma manera que lo hice en el artículo.

      from selenium import webdriver

      browser = webdriver.Chrome()

      browser.get("https://stackoverflow.com/tags")

      nav = browser.find_elements_by_class_name("post-tag")

      for i in range(len(nav)):

      print(nav[i].text)

      Saludos,

Deja un comentario

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