Guía para principiantes sobre Web Scraping en R (usando rvest) con ejemplo

Share on facebook
Share on twitter
Share on linkedin
Share on telegram
Share on whatsapp

Contenidos

Introducción

Los datos y la información en la web están creciendo exponencialmente. Hoy en día, todos usamos Google como nuestra primera fuente de conocimiento, ya sea para encontrar reseñas sobre un lugar para comprender un nuevo término. Toda esta información ya está disponible en la web.

Con la cantidad de datos disponibles en la web, abre nuevos horizontes de posibilidad para un Científico de datos. Creo firmemente que el web scraping es una habilidad imprescindible para cualquier científico de datos. En el mundo actual, todos los datos que necesita ya están disponibles en Internet; lo único que le impide usarlos es la capacidad de acceder a ellos. Con la ayuda de este artículo, también podrá superar esa barrera.

La mayoría de los datos disponibles en la web no están disponibles. Está presente en un formato no estructurado (formato HTML) y no se puede descargar. Por lo tanto, se requiere conocimiento y experiencia para utilizar estos datos para eventualmente construye un modelo útil.

En este artículo, lo guiaré a través del proceso de raspado web en R. Con este artículo, obtendrá experiencia para utilizar cualquier tipo de datos disponibles en Internet.

Tabla de contenido

  1. ¿Qué es Web Scraping?
  2. ¿Por qué necesitamos Web Scraping en Ciencia de los datos?
  3. Formas de extraer datos
  4. Prerrequisitos
  5. Raspando una página web usando R
  6. Analizar datos extraídos de la web

1. ¿Qué es Web Scraping?

El web scraping es una técnica para convertir los datos presentes en formato no estructurado (etiquetas HTML) en la web al formato estructurado al que se puede acceder y utilizar fácilmente.

Casi todos los lenguajes principales proporcionan formas de realizar raspado web. En este artículo, usaremos R para extraer los datos de los largometrajes más populares de 2016 del IMDb sitio web.

Obtendremos una serie de funciones para cada uno de los 100 largometrajes populares lanzados en 2016. Además, veremos los problemas más comunes que uno podría enfrentar al extraer datos de Internet debido a la falta de coherencia en el sitio web. codifique y observe cómo resolver estos problemas.

Si se siente más cómodo usando Python, le recomendaré que lea esta guía para comenzar con el raspado web con Python.

2. ¿Por qué necesitamos Web Scraping?

Estoy seguro de que las primeras preguntas que deben haber surgido en su cabeza hasta ahora es «¿Por qué necesitamos web scraping»? Como dije antes, las posibilidades con el web scraping son inmensas.

Para brindarle conocimientos prácticos, vamos a extraer datos de IMDB. Algunas otras aplicaciones posibles para las que puede utilizar el web scraping son:

  • Extracción de datos de clasificación de películas para crear motores de recomendación de películas.
  • Extraer datos de texto de Wikipedia y otras fuentes para crear sistemas basados ​​en PNL o entrenar modelos de aprendizaje profundo para tareas como el reconocimiento de temas del texto dado.
  • Extraer datos de imágenes etiquetadas de sitios web como Google, Flickr, etc. para entrenar modelos de clasificación de imágenes.
  • Recolección de datos de sitios de redes sociales como Facebook y Twitter para realizar tareas de análisis de sentimientos, minería de opiniones, etc.
  • Extraer opiniones y comentarios de los usuarios de sitios de comercio electrónico como Amazon, Flipkart, etc.

3. Formas de extraer datos

Hay varias formas de extraer datos de la web. Algunas de las formas populares son:

  • Copiar y pegar humanos: Esta es una forma lenta y eficiente de extraer datos de la web. Esto implica que los propios humanos analicen y copien los datos en el almacenamiento local.
  • Coincidencia de patrones de texto: Otro enfoque simple pero poderoso para extraer información de la web es mediante el uso de funciones de coincidencia de expresiones regulares de los lenguajes de programación. Puede obtener más información sobre las expresiones regulares aquí.
  • Interfaz API: Muchos sitios web como Facebook, Twitter, LinkedIn, etc. proporcionan API públicas y / o privadas a las que se puede llamar utilizando el código estándar para recuperar los datos en el formato prescrito.
  • Análisis DOM: Mediante el uso de navegadores web, los programas pueden recuperar el contenido dinámico generado por los scripts del lado del cliente. También es posible analizar páginas web en un árbol DOM, según los programas que pueden recuperar partes de estas páginas.

Usaremos el enfoque de análisis de DOM durante el transcurso de este artículo. Y confíe en los selectores CSS de la página web para encontrar los campos relevantes que contienen la información deseada. Pero antes de comenzar, hay algunos requisitos previos que se necesitan para extraer datos de manera competente de cualquier sitio web.

4. Requisitos previos

Los requisitos previos para realizar el raspado web en R se dividen en dos grupos:

  • Para comenzar con el web scraping, debe tener conocimientos prácticos del lenguaje R. Si recién está comenzando o desea repasar los conceptos básicos, le recomiendo encarecidamente seguir esta ruta de aprendizaje en R. Durante el transcurso de este artículo, usaremos el paquete ‘rvest’ en R escrito por Hadley Wickham. Puede acceder a la documentación del paquete rvest aquí. Asegúrese de tener este paquete instalado. Si aún no tiene este paquete, puede seguir el siguiente código para instalarlo.
install.packages('rvest')
  • Agregar conocimiento de HTML y CSS será una ventaja adicional. Una de las mejores fuentes que pude encontrar para aprender HTML y CSS es esta. He observado que la mayoría de los científicos de datos no son muy sólidos con conocimientos técnicos de HTML y CSS. Por lo tanto, usaremos un software de código abierto llamado Selector Gadget que será más que suficiente para que cualquiera pueda realizar el web scraping. Puede acceder y descargar la extensión Selector Gadget aquí. Asegúrese de tener instalada esta extensión siguiendo las instrucciones del sitio web. He hecho lo mismo. Estoy usando Google Chrome y puedo acceder a la extensión en la barra de extensión en la parte superior derecha.

ws1-8735302

Con esto, puede seleccionar las partes de cualquier sitio web y obtener las etiquetas relevantes para acceder a esa parte simplemente haciendo clic en esa parte del sitio web. Tenga en cuenta que esta es una manera de aprender HTML y CSS y hacerlo manualmente. Pero para dominar el arte del web scraping, le recomiendo encarecidamente que aprenda HTML y CSS para comprender y apreciar mejor lo que está sucediendo bajo el capó.

4. Raspado de una página web con R

Ahora, comencemos a buscar en el sitio web de IMDb los 100 largometrajes más populares lanzados en 2016. Puede acceder a ellos aquí.

#Loading the rvest package
library('rvest')

#Specifying the url for desired website to be scraped
url <- 'http://www.imdb.com/search/title?count=100&release_date=2016,2016&title_type=feature'

#Reading the HTML code from the website
webpage <- read_html(url)

Ahora, extraeremos los siguientes datos de este sitio web.

  • Rango: El rango de la película de 1 a 100 en la lista de las 100 películas más populares estrenadas en 2016.
  • Título: El título del largometraje.
  • Descripción: La descripción del largometraje.
  • Tiempo de ejecución: La duración del largometraje.
  • Género: El género del largometraje,
  • Clasificación: La calificación de IMDb del largometraje.
  • Metascore: El metascore en el sitio web de IMDb para el largometraje.
  • Votos: Votos emitidos a favor del largometraje.
  • Ingresos_brutos_en_Mil: Las ganancias brutas del largometraje en millones.
  • Director: El director principal del largometraje. Tenga en cuenta que, en el caso de varios directores, tomaré solo el primero.
  • Actor: El actor principal del largometraje. Tenga en cuenta que, en el caso de varios actores, tomaré solo el primero.

Aquí hay una captura de pantalla que contiene cómo están organizados todos estos campos.

ws2-7967289

Paso 1: Ahora, comenzaremos raspando el campo Rango. Para eso, usaremos el gadget selector para obtener los selectores CSS específicos que encierran las clasificaciones. Puede hacer clic en la extensión en su navegador y seleccionar el campo de clasificación con el cursor.

ws3-1491123

Asegúrese de que todas las clasificaciones estén seleccionadas. Puede seleccionar algunas secciones de clasificación más en caso de que no pueda obtenerlas todas y también puede deseleccionarlas haciendo clic en la sección seleccionada para asegurarse de que solo tenga las secciones resaltadas que desea raspar para ese momento. .

Paso 2: Una vez que esté seguro de haber realizado las selecciones correctas, debe copiar el selector de CSS correspondiente que puede ver en la parte inferior central.

ws4-7288393

Paso 3: Una vez que conozca el selector de CSS que contiene las clasificaciones, puede usar este simple código R para obtener todas las clasificaciones:

#Using CSS selectors to scrape the rankings section
rank_data_html <- html_nodes(webpage,'.text-primary')

#Converting the ranking data to text
rank_data <- html_text(rank_data_html)

#Let's have a look at the rankings
head(rank_data)

[1] "1." "2." "3." "4." "5." "6."

Paso 4: Una vez que tenga los datos, asegúrese de que se vean en el formato deseado. Estoy preprocesando mis datos para convertirlos a formato numérico.

#Data-Preprocessing: Converting rankings to numerical
rank_data<-as.numeric(rank_data)

#Let's have another look at the rankings
head(rank_data)

[1] 1 2 3 4 5 6

Paso 5: Ahora puede borrar la sección del selector y seleccionar todos los títulos. Puede inspeccionar visualmente que todos los títulos estén seleccionados. Realice las adiciones y eliminaciones necesarias con la ayuda de su cursor. Yo he hecho lo mismo aquí.

ws5-2013313

Paso 6: Nuevamente, tengo el selector de CSS correspondiente para los títulos: .lister-item-header a. Usaré este selector para raspar todos los títulos usando el siguiente código.

#Using CSS selectors to scrape the title section
title_data_html <- html_nodes(webpage,'.lister-item-header a')

#Converting the title data to text
title_data <- html_text(title_data_html)

#Let's have a look at the title
head(title_data)

[1] "Sing"          "Moana"         "Moonlight"     "Hacksaw Ridge"

[5] "Passengers"    "Trolls"

Paso 7: En el siguiente código, he hecho lo mismo para el raspado: descripción, tiempo de ejecución, género, calificación, metapuntuacion, votos, ingresos brutos en mil, datos de director y actor.

#Using CSS selectors to scrape the description section
description_data_html <- html_nodes(webpage,'.ratings-bar+ .text-muted')

#Converting the description data to text
description_data <- html_text(description_data_html)

#Let's have a look at the description data
head(description_data)

[1] "nIn a city of humanoid animals, a hustling theater impresario's attempt to save his theater with a singing competition becomes grander than he anticipates even as its finalists' find that their lives will never be the same."

[2] "nIn Ancient Polynesia, when a terrible curse incurred by the Demigod Maui reaches an impetuous Chieftain's daughter's island, she answers the Ocean's call to seek out the Demigod to set things right."

[3] "nA chronicle of the childhood, adolescence and burgeoning adulthood of a young, African-American, gay man growing up in a rough neighborhood of Miami."

[4] "nWWII American Army Medic Desmond T. Doss, who served during the Battle of Okinawa, refuses to kill people, and becomes the first man in American history to receive the Medal of Honor without firing a shot."

[5] "nA spacecraft traveling to a distant colony planet and transporting thousands of people has a malfunction in its sleep chambers. As a result, two passengers are awakened 90 years early."

[6] "nAfter the Bergens invade Troll Village, Poppy, the happiest Troll ever born, and the curmudgeonly Branch set off on a journey to rescue her friends.

#Data-Preprocessing: removing 'n'
description_data<-gsub("n","",description_data)

#Let's have another look at the description data 
head(description_data)

[1] "In a city of humanoid animals, a hustling theater impresario's attempt to save his theater with a singing competition becomes grander than he anticipates even as its finalists' find that their lives will never be the same."

[2] "In Ancient Polynesia, when a terrible curse incurred by the Demigod Maui reaches an impetuous Chieftain's daughter's island, she answers the Ocean's call to seek out the Demigod to set things right."

[3] "A chronicle of the childhood, adolescence and burgeoning adulthood of a young, African-American, gay man growing up in a rough neighborhood of Miami."

[4] "WWII American Army Medic Desmond T. Doss, who served during the Battle of Okinawa, refuses to kill people, and becomes the first man in American history to receive the Medal of Honor without firing a shot."

[5] "A spacecraft traveling to a distant colony planet and transporting thousands of people has a malfunction in its sleep chambers. As a result, two passengers are awakened 90 years early."

[6] "After the Bergens invade Troll Village, Poppy, the happiest Troll ever born, and the curmudgeonly Branch set off on a journey to rescue her friends."

#Using CSS selectors to scrape the Movie runtime section
runtime_data_html <- html_nodes(webpage,'.text-muted .runtime')

#Converting the runtime data to text
runtime_data <- html_text(runtime_data_html)

#Let's have a look at the runtime
head(runtime_data)

[1] "108 min" "107 min" "111 min" "139 min" "116 min" "92 min"

#Data-Preprocessing: removing mins and converting it to numerical

runtime_data<-gsub(" min","",runtime_data)
runtime_data<-as.numeric(runtime_data)

#Let's have another look at the runtime data
head(runtime_data)

[1] 1 2 3 4 5 6

#Using CSS selectors to scrape the Movie genre section
genre_data_html <- html_nodes(webpage,'.genre')

#Converting the genre data to text
genre_data <- html_text(genre_data_html)

#Let's have a look at the runtime
head(genre_data)

[1] "nAnimation, Comedy, Family "

[2] "nAnimation, Adventure, Comedy "

[3] "nDrama "

[4] "nBiography, Drama, History "

[5] "nAdventure, Drama, Romance "

[6] "nAnimation, Adventure, Comedy "

#Data-Preprocessing: removing n
genre_data<-gsub("n","",genre_data)

#Data-Preprocessing: removing excess spaces
genre_data<-gsub(" ","",genre_data)

#taking only the first genre of each movie
genre_data<-gsub(",.*","",genre_data)

#Convering each genre from text to factor
genre_data<-as.factor(genre_data)

#Let's have another look at the genre data
head(genre_data)

[1] Animation Animation Drama     Biography Adventure Animation

10 Levels: Action Adventure Animation Biography Comedy Crime Drama ... Thriller

#Using CSS selectors to scrape the IMDB rating section
rating_data_html <- html_nodes(webpage,'.ratings-imdb-rating strong')

#Converting the ratings data to text
rating_data <- html_text(rating_data_html)

#Let's have a look at the ratings
head(rating_data)

[1] "7.2" "7.7" "7.6" "8.2" "7.0" "6.5"

#Data-Preprocessing: converting ratings to numerical
rating_data<-as.numeric(rating_data)

#Let's have another look at the ratings data
head(rating_data)

[1] 7.2 7.7 7.6 8.2 7.0 6.5

#Using CSS selectors to scrape the votes section
votes_data_html <- html_nodes(webpage,'.sort-num_votes-visible span:nth-child(2)')

#Converting the votes data to text
votes_data <- html_text(votes_data_html)

#Let's have a look at the votes data
head(votes_data)

[1] "40,603"  "91,333"  "112,609" "177,229" "148,467" "32,497"

#Data-Preprocessing: removing commas
votes_data<-gsub(",","",votes_data)

#Data-Preprocessing: converting votes to numerical
votes_data<-as.numeric(votes_data)

#Let's have another look at the votes data
head(votes_data)

[1]  40603  91333 112609 177229 148467  32497

#Using CSS selectors to scrape the directors section
directors_data_html <- html_nodes(webpage,'.text-muted+ p a:nth-child(1)')

#Converting the directors data to text
directors_data <- html_text(directors_data_html)

#Let's have a look at the directors data
head(directors_data)

[1] "Christophe Lourdelet" "Ron Clements"         "Barry Jenkins"

[4] "Mel Gibson"           "Morten Tyldum"        "Walt Dohrn"

#Data-Preprocessing: converting directors data into factors
directors_data<-as.factor(directors_data)

#Using CSS selectors to scrape the actors section
actors_data_html <- html_nodes(webpage,'.lister-item-content .ghost+ a')

#Converting the gross actors data to text
actors_data <- html_text(actors_data_html)

#Let's have a look at the actors data
head(actors_data)

[1] "Matthew McConaughey" "Auli'i Cravalho"     "Mahershala Ali"

[4] "Andrew Garfield"     "Jennifer Lawrence"   "Anna Kendrick"

#Data-Preprocessing: converting actors data into factors
actors_data<-as.factor(actors_data)

Pero quiero que siga de cerca lo que sucede cuando hago lo mismo con los datos de Metascore.

#Using CSS selectors to scrape the metascore section
metascore_data_html <- html_nodes(webpage,'.metascore')

#Converting the runtime data to text
metascore_data <- html_text(metascore_data_html)

#Let's have a look at the metascore 
data head(metascore_data)

[1] "59        " "81        " "99        " "71        " "41        "

[6] "56        "

#Data-Preprocessing: removing extra space in metascore
metascore_data<-gsub(" ","",metascore_data)

#Lets check the length of metascore data
length(metascore_data)

[1] 96

Paso 8: La longitud de los datos de metascore es 96 mientras estamos raspando los datos de 100 películas. La razón por la que esto sucedió es que hay 4 películas que no tienen los campos Metascore correspondientes.

ws6-2924905

Paso 9: Es una situación práctica que puede surgir al raspar cualquier sitio web. Desafortunadamente, si simplemente agregamos NA a las últimas 4 entradas, se asignará NA como Metascore para las películas 96 a 100, mientras que en realidad, faltan los datos para algunas otras películas. Después de una inspección visual, encontré que faltaba el Metascore para las películas 39, 73, 80 y 89. Escribí la siguiente función para solucionar este problema.

for (i in c(39,73,80,89)){

a<-metascore_data[1:(i-1)]

b<-metascore_data[i:length(metascore_data)]

metascore_data<-append(a,list("NA"))

metascore_data<-append(metascore_data,b)

}

#Data-Preprocessing: converting metascore to numerical
metascore_data<-as.numeric(metascore_data)

#Let's have another look at length of the metascore data

length(metascore_data)

[1] 100

#Let's look at summary statistics
summary(metascore_data)

Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's

23.00   47.00   60.00   60.22   74.00   99.00       4

Paso 10: Lo mismo sucede con la variable Bruto que representa los ingresos brutos de esa película en millones. He usado la misma solución para trabajar a mi manera:

#Using CSS selectors to scrape the gross revenue section
gross_data_html <- html_nodes(webpage,'.ghost~ .text-muted+ span')

#Converting the gross revenue data to text
gross_data <- html_text(gross_data_html)

#Let's have a look at the votes data
head(gross_data)

[1] "$269.36M" "$248.04M" "$27.50M"  "$67.12M"  "$99.47M"  "$153.67M"

#Data-Preprocessing: removing '$' and 'M' signs
gross_data<-gsub("M","",gross_data)

gross_data<-substring(gross_data,2,6)

#Let's check the length of gross data
length(gross_data)

[1] 86

#Filling missing entries with NA
for (i in c(17,39,49,52,57,64,66,73,76,77,80,87,88,89)){

a<-gross_data[1:(i-1)]

b<-gross_data[i:length(gross_data)]

gross_data<-append(a,list("NA"))

gross_data<-append(gross_data,b)

}

#Data-Preprocessing: converting gross to numerical
gross_data<-as.numeric(gross_data)

#Let's have another look at the length of gross data
length(gross_data)

[1] 100

summary(gross_data)

Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's

0.08   15.52   54.69   96.91  119.50  530.70      14

Paso 11: Ahora hemos eliminado con éxito las 11 funciones de las 100 películas más populares estrenadas en 2016. Combinémoslas para crear un marco de datos e inspeccionar su estructura.

#Combining all the lists to form a data frame
movies_df<-data.frame(Rank = rank_data, Title = title_data,

Description = description_data, Runtime = runtime_data,

Genre = genre_data, Rating = rating_data,

Metascore = metascore_data, Votes = votes_data,                                                             Gross_Earning_in_Mil = gross_data,

Director = directors_data, Actor = actors_data)

#Structure of the data frame

str(movies_df)

'data.frame':            100 obs. of  11 variables:

$ Rank                : num  1 2 3 4 5 6 7 8 9 10 ...

$ Title               : Factor w/ 99 levels "10 Cloverfield Lane",..: 66 53 54 32 58 93 8 43 97 7 ...

$ Description         : Factor w/ 100 levels "19-year-old Billy Lynn is brought home for a victory tour after a harrowing Iraq battle. Through flashbacks the film shows what"| __truncated__,..: 57 59 3 100 21 33 90 14 13 97 ...

$ Runtime             : num  108 107 111 139 116 92 115 128 111 116 ...

$ Genre               : Factor w/ 10 levels "Action","Adventure",..: 3 3 7 4 2 3 1 5 5 7 ...

$ Rating              : num  7.2 7.7 7.6 8.2 7 6.5 6.1 8.4 6.3 8 ...

$ Metascore           : num  59 81 99 71 41 56 36 93 39 81 ...

$ Votes               : num  40603 91333 112609 177229 148467 ...

$ Gross_Earning_in_Mil: num  269.3 248 27.5 67.1 99.5 ...

$ Director            : Factor w/ 98 levels "Andrew Stanton",..: 17 80 9 64 67 95 56 19 49 28 ...

$ Actor               : Factor w/ 86 levels "Aaron Eckhart",..: 59 7 56 5 42 6 64 71 86 3 ...

Ahora ha raspado con éxito el sitio web de IMDb para los 100 largometrajes más populares lanzados en 2016.

6. Analizar datos extraídos de la Web

Una vez que tenga los datos, puede realizar varias tareas como analizar los datos, hacer inferencias a partir de ellos, entrenar modelos de aprendizaje automático sobre estos datos, etc. Seguí creando una visualización interesante a partir de los datos que acabamos de extraer. Siga las visualizaciones y responda las preguntas que se dan a continuación. Publique sus respuestas en la sección de comentarios a continuación.

library('ggplot2')

qplot(data = movies_df,Runtime,fill = Genre,bins = 30)

ws7-7852055

Pregunta 1: Según los datos anteriores, ¿qué película de qué género tuvo el tiempo de ejecución más largo?

ggplot(movies_df,aes(x=Runtime,y=Rating))+
geom_point(aes(size=Votes,col=Genre))

ws8-2948438

Pregunta 2: Según los datos anteriores, en el tiempo de ejecución de 130-160 minutos, ¿qué género tiene los votos más altos?

ggplot(movies_df,aes(x=Runtime,y=Gross_Earning_in_Mil))+
geom_point(aes(size=Rating,col=Genre))

ws9-4512146

Pregunta 3: Según los datos anteriores, en todos los géneros, qué género tiene las ganancias brutas promedio más altas en tiempo de ejecución de 100 a 120.

Notas finales

Creo que este artículo le habría dado una comprensión completa del web scraping en R. Ahora, también tiene una idea clara de los problemas que podría encontrar y cómo puede solucionarlos. Como la mayoría de los datos en la web están presentes en un formato no estructurado, el web scraping es una habilidad muy útil para cualquier científico de datos.

Además, puede publicar las respuestas a las tres preguntas anteriores en la sección de comentarios a continuación. ¿Disfrutaste leyendo este artículo? Comparta sus opiniones conmigo. Si tiene alguna duda / pregunta, no dude en enviarla a continuación.

Suscribite a nuestro Newsletter

No te enviaremos correo SPAM. Lo odiamos tanto como tú.