Podemos decir que, a día de hoy, Internet se a vuelto un enorme centro de información. En Internet tenemos acceso a enormes cantidad de información del tipo que podamos imaginar, lo cual hubiera sido inimaginable en el pasado. Es por lo anterior que el Internet supone una gran fuente de datos a la hora de querer realizar un análisis sobre alguna cosa o tema. Por ello se han desarrollado herramientas y bibliotecas que nos facilitan la tarea de la recolección de datos desde internet y, en esta entrada, pasaremos a ver una de dichas bibliotecas que nos facilitan la tarea de la recolección de datos desde internet.
La herramienta a la que nos referimos que procederemos a ver en esta entrada, se trata de BeautifulSoup, una librería, en lenguaje python, que sirve para la extracción de datos de archivos html y xml. Y bueno, para instalar dicha librería es bien sencillo, ya que podemos instalarla desde pip:
$ pip install beautifulsoup4
También requeriremos de la librería request y para guardar datos podemos hacer uso de pandas, por lo cual vamos a instalarlas también:
$ pip install requests pandas
Ahora importamos nuestras librerías en un nuevo script de python:
#Importamos las librerías necesarias
import pandas as pd
import requests
from bs4 import BeautifulSoup as bso
Para trabajar, en esta ocasión, vamos a utilizar la pagina de cnn en español para extraer datos de ella, con ayuda de BeautifulSoup, para lo cual vemos que la url de resultados de términos de búsqueda de la pagina pasa una frase o palabra a dicha url. Por lo anterior podemos buscar muchas cosas en la misma solamente cambiando el parámetro s en la url en cuestión. Es por lo anterior que utilizaremos el parámetro de búsqueda de ucrania, quedando la url como sigue:
https://cnnespanol.cnn.com/?s=ucrania
Ahora vamos a definir nuestra url en un string y realizamos una petición a la url para verificar que exista dicha url, y la respuesta la guardamos como un objeto de beautifulsoup, en caso de que no se haya podido encontrar la url salimos del programa:
#Definimos nuestra URL con la que trabajaremos en un principio
url = 'https://cnnespanol.cnn.com/?s=ucrania'
#Hacemos petición a URL y creamos nuestra "sopa"
print('Tratando de realizar la petición a la URL...\n')
try:
r = requests.get(url, verify=True)
soup = bso(r.text, 'lxml')
except:
print('No se ha podido obtener la URL de la búsqueda o información de la misma.\n')
exit()
Creamos un dataframe en pandas, con los nombres de las columnas a partir de una lista, donde vamos a vaciar la información que obtengamos de la pagina web:
#Creamos un dataframe con solo los nombres de columnas
cNames = ['Título', 'Autor', 'Fecha', 'Texto', 'URL']
df = pd.DataFrame(columns=cNames)
Para poder continuar necesitamos analizar un poco más a profundidad la pagina de cnn en español. Primeramente nos damos cuenta de que tiene una clase de enlaces llamada “page-numbers” para enumerar las paginas que resultan de todos los artículos relacionados con el termino de búsqueda.
También podemos ver que existen enlaces con el termino “video” en ellas que no contienen mucha información y es más un vídeo, por lo cual no nos servirán demasiado y tendremos que descartarlas.
Estas paginas contienen el termino vídeo en la url, por lo que descartamos los enlaces que contengan dicho termino y no los analizaremos u obtendremos datos de ellos.
Ahora podemos definir variables para hacer los loops por las paginas de nuestra pagina web a analizar y tratar de obtener el numero total de paginas de búsqueda en la pagina que vamos a scrapear; en caso de no poder obtener el numero de paginas salimos del programa:
#Definimos algunas variables para hacer loops
i = 0
j = 0
print('Intentando obtener el número total de páginas relacionadas a la búsqueda...\n')
try:
tPages = int(soup.find_all("a", class_="page-numbers")[-2].get_text()) - 1
except:
print('No se ha podido obtener el número de páginas.\n')
exit()
Iniciamos el primer bucle de nuestra aplicación de scrapping y lo primero que vamos a hacer es revisar si ya se hizo el primer bucle, para en caso de ser así, actualizar los datos de url, en caso de no poder actualizar los datos de la url salimos del programa:
#Iniciamos el loop para las páginas
while i <= tPages:
#Recargamos los datos de petición si el loop ya hizo su primer bucle
if i > 0:
print('Intentando hacer petición a la URL de la siguiente página...\n')
try:
r = requests.get(url, verify=True)
soup = bso(r.text, 'lxml')
except:
print('No se ha podido completar la solicitud a la URL.\n')
exit()
Comenzamos con el segundo bucle del programa, que se encuentra anidado dentro del primer bucle en esta ocasión. Lo primero que hacemos dentro de este segundo bucle es tratar de obtener la información de las url de los artículos de donde vamos a tratar de obtener los datos de la pagina web, ya que este bucle hace loop a través de todos los artículos que se muestran en la pagina de resultados de la búsqueda:
#Comenzamos bucle por los artículos de la página
for j in soup.find_all('h2', class_='news__title'):
#Hacemos petición a la página del artículo
print('Intentando realizar petición a página de artículo...\n')
urld = j.find('a')['href']
try:
r1 = requests.get(urld, verify=True)
soup1 = bso(r1.text, 'lxml')
except:
print('No se ha podido realizar la petición al artículo.\n')
exit()
Si recordamos, teníamos que excluir las url que tuvieran el string “video” dentro de ellas, para lo cual utilizamos un if not, donde trataremos de obtener todos los datos que requiramos del articulo:
#Revisamos que el artículo no sea un vídeo
print('Revisando que no se trate de un artículo de vídeo...\n')
if not "video" in urld:
print('Extrayendo datos...\n')
try:
#Obtenemos tanto el titulo del articulo como el autor de dicho articulo
title = soup1.find_all('h1', class_='storyfull__title')[0].get_text()
autor = soup1.find_all('p', class_='storyfull__authors')[0].find('a').get_text()
#Obtenemos y damos formato a la fecha
fech = soup1.find_all('time', class_='storyfull__time')[0].get_text()
fech0 = fech.split(") ", 1)[1]
fecha = fech0.replace(',','')
Dentro de este if not, definimos un tercer bucle para obtener el texto del articulo a través de los tags de párrafo e irlos uniendo en un solo string:
#Definimos variables para el loop para extraer el texto
k = 0
n = 0
#Iniciamos con el loop para la extracción del texto
for k in soup1.find_all('p'):
if n == 0:
text = k.get_text()
n = 1
else:
text = text + '\n' + k.get_text()
Por ultimo, dentro de ese mismo if not, creamos una nueva fila con los datos extraídos dentro de nuestro dataframe, y si no podemos obtener los datos en general salimos del programa:
#Añadimos una nueva fila al dataframe con los datos extraidos de la pagina
df.loc[df.shape[0]] = [title, autor, fecha, text, urld]
except:
print('No se ha podido obtener datos del articulo.\n')
exit()
Con lo anterior terminamos el segundo bucle del programa, con lo cual solo nos resta terminar el primero. Para lo anterior solo tenemos que tratar de obtener la url de la siguiente pagina, y si no podemos, salir del programa, sin olvidarnos primero de escribir lo que tengamos en nuestro dataframe en un archivo de extensión xlsx:
print('Intentando obtener URL de la siguiente página...\n')
#Actualizamos la url
try:
url = soup.find_all('a', class_="page-numbers")[-1]["href"]
except:
print('No se ha podido obtener la nueva URL...\n')
print("Intentando escribir archivo de Excel con los datos que se pudieron obtener...\n")
if df.shape[0] >= 1:
df.to_excel("cnn_ucrania.xlsx")
exit()
i = i+1
Si completamos todos los bucles correctamente, o mejor dicho, si recorremos todas las paginas de resultado de la búsqueda, escribimos nuestro dataframe en un archivo xlsx y salimos del programa:
#Escribimos los datos a un archivo de excel
print("Intentando escribir archivo de excel con los datos...\n")
df.to_excel("cnn_ucrania.xlsx")
Después de esto ya solo quedaría darle formato a nuestro archivo xlsx en cualquier editor de hoja de calculo o si se analizaran los datos con pandas, proceder al análisis.
Para terminar la publicación solo me resta compartir vídeo de la ejecución del script y el resultado del mismo, así como enlace al script completo en mi github:
Enlace Github:
https://github.com/Goshujinsama88/cnn-beautifulsoup
Nota: La pagina de cnn en español tiene un error donde solo se cargan completamente bien las primeras 10 paginas del resultado de una búsqueda, por lo que a partir de la pagina 11, al no haber resultado de artículos, el script saldrá de la ejecución guardando lo que se contiene actualmente en el dataframe.