Construyendo un Web Scraper con Python en 30 minutos
Mis habilidades en Python son básicas, así que si estás aquí sin muchas habilidades en codificación, espero que esta guía te ayude a adquirir más conocimientos y comprensión.
El proyecto perfecto para principiantes
Para obtener datos para proyectos de ML, IA o ciencia de datos, a menudo dependerá de bases de datos, API o conjuntos de datos CSV listos para usar. Pero, ¿qué pasa si no puede encontrar un conjunto de datos que desee utilizar y analizar? Ahí es donde entra en juego un raspador web.
Trabajar en proyectos es fundamental para consolidar los conocimientos adquiridos. Cuando comencé este proyecto, estaba un poco abrumado porque realmente no sabía nada.
Seguir con él, encontrar respuestas a mis preguntas en Stack Overflow y muchas pruebas y errores me ayudaron a comprender realmente cómo funciona la programación: cómo funcionan las páginas web, cómo usar bucles y cómo crear funciones y mantener los datos limpios. Hace que la construcción de un raspador web sea el proyecto perfecto para principiantes para cualquiera que se esté iniciando en Python.
Que cubriremos
Esta guía lo guiará a través de la comprensión de las páginas web HTML, la creación de un raspador web con Python y la creación de un DataFrame
con pandas. Cubrirá la calidad de los datos, la limpieza de datos y la conversión de tipos de datos, completamente paso a paso y con instrucciones, código y explicaciones sobre cómo funciona cada pieza. ¡Espero que codifiques y disfrutes!
Descargo de responsabilidad
Los sitios web pueden restringir o prohibir la extracción de datos de su sitio web. Los usuarios pueden estar sujetos a ramificaciones legales dependiendo de dónde y cómo intente recopilar información. Los sitios web suelen describir esto en sus términos de uso y en su robots.txt
archivo encontrado en su sitio, que generalmente se parece a esto: [www.example.com/robots.txt](http://www.[site].com/robots.txt.)
. Así que raspe responsablemente y respete el robots.txt
.
¿Qué es Web Scraping?
Raspado web Consiste en recopilar datos disponibles en sitios web. Esto se puede hacer manualmente por un humano o usando un bot.
A Bot es un programa creado por usted que le ayuda a extraer los datos que necesita mucho más rápido que la mano y los ojos de un ser humano.
¿Qué vamos a raspar?
Es esencial identificar el objetivo de su raspado desde el principio. No queremos extraer ningún dato que en realidad no necesitemos.
Para este proyecto, extraeremos datos de las 1,000 películas principales de IMDb, específicamente las 50 películas principales de esta página. Aquí está la información que recopilaremos de cada lista de películas:
- El título
- El año en que fue lanzado
- Cuanto dura la pelicula
- Calificación de IMDb de la película
- El Metascore de la película
- ¿Cuántos votos obtuvo la película?
- Las ganancias brutas de la película en EE. UU.
¿Cómo funcionan los Web Scrapers?
Los web scrapers recopilan datos del sitio web de la misma manera que lo haría un humano: van a una página web del sitio web, obtienen los datos relevantes y pasan a la siguiente página web, solo que mucho más rápido.
Cada sitio web tiene una estructura diferente. Estas son algunas cosas importantes en las que pensar al crear un raspador web:
- ¿Cuál es la estructura de la página web que contiene los datos que busca?
- ¿Cómo llegamos a esas páginas web?
- ¿Necesitará recopilar más datos de la página siguiente?
La URL
Para empezar, veamos la URL de la página que queremos raspar.
Notamos algunas cosas sobre la URL:
?
actúa como un separador: indica el final de la ruta del recurso URL y el inicio de los parámetrosgroups=top_1000
especifica de qué se tratará la página&ref_adv_prv
nos lleva a la página anterior o siguiente. La referencia es la página en la que estamos actualmente.adv_nxt
yadv_prv
hay dos valores posibles, traducidos a avanzar a la página siguiente y avanzar a la página anterior.
Cuando navega hacia adelante y hacia atrás por las páginas, notará que solo cambian los parámetros. Tenga en cuenta esta estructura, ya que es útil saberlo mientras construimos el raspador.
El HTML
HTML significa Lenguaje de marcado de hipertexto, y la mayoría de las páginas web se escriben con él. Esencialmente, HTML es cómo dos computadoras se comunican entre sí a través de Internet, y los sitios web están qué ellos dicen.
Cuando accede a una URL, su computadora envía una solicitud al servidor que aloja el sitio. Se puede ejecutar cualquier tecnología en ese servidor (JavaScript, Ruby, Java, etc.) para procesar su solicitud. Finalmente, el servidor devuelve una respuesta a su navegador; a menudo, esa respuesta será en forma de una página HTML para que la muestre su navegador.
HTML describe la estructura de una página web de forma semántica y originalmente incluía señales para la apariencia del documento.
Inspeccionar HTML
Los usuarios de Chrome, Firefox y Safari pueden examinar la estructura HTML de cualquier página haciendo clic con el botón derecho del mouse y presionando la opción Inspeccionar.
Aparecerá un menú en la parte inferior o en el lado derecho de su página con una lista larga de todas las etiquetas HTML que contienen la información que se muestra en la ventana de su navegador. Si estás en Safari (foto de arriba), querrás presionar el botón a la izquierda de la barra de búsqueda, que parece un objetivo. Si está en Chrome o Firefox, hay un pequeño cuadro con un ícono de flecha en la parte superior izquierda que usará para inspeccionar.
Una vez que haga clic, si mueve el cursor sobre cualquier elemento de la página, notará que se resaltará junto con las etiquetas HTML en el menú con el que están asociados, como se ve arriba.
Saber cómo leer la estructura básica de la página HTML de una página es importante para que podamos recurrir a Python para que nos ayude a extraer el HTML de la página.
Instrumentos
Las herramientas que vamos a utilizar son:
- Responder (opcional) es un entorno de programación informática interactivo y sencillo que se utiliza a través de su navegador web. Recomiendo usar esto solo para propósitos de código si aún no tiene un IDE. Si usa Repl, asegúrese de estar usando el entorno Python.
- Peticiones nos permitirá enviar solicitudes HTTP para obtener archivos HTML
- Hermosa Sopa nos ayudará a analizar los archivos HTML
- pandas nos ayudará a reunir los datos en un
DataFrame
para limpiarlo y analizarlo - NumPy agregará soporte para funciones matemáticas y herramientas para trabajar con matrices
Ahora, codifiquemos
Puede seguir a continuación dentro de su entorno Repl o IDE, o puede ir directamente a el código completo aquí . ¡Divertirse!
Importar herramientas
Primero, importaremos las herramientas que necesitaremos para que podamos usarlas para ayudarnos a construir el raspador y obtener los datos que necesitamos.
|_+_|Películas en inglés
Es muy probable que cuando ejecutemos nuestro código para extraer algunas de estas películas, obtengamos los nombres de las películas traducidos al idioma principal del país en el que se originó.
Utilice este código para asegurarse de que obtengamos títulos traducidos al inglés de todas las películas que extraemos:
validación de formulario js vainilla|_+_|
Solicitar contenido de la URL
Obtenga el contenido de la página que estamos viendo solicitando la URL:
|_+_|Desglose de las solicitudes de URL:
import requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np
es la variable que creamos y asignamos la URL aheaders = {'Accept-Language': 'en-US, en;q=0.5'}
es la variable que creamos para almacenar nuestrourl = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' results = requests.get(url, headers=headers)
acciónurl
es el método que usamos para capturar el contenido de la URL. Elresults
parte le dice a nuestro raspador que nos traiga inglés, basado en nuestra línea de código anterior.
Usando BeautifulSoup
Haga que el contenido que tomamos sea fácil de leer usando request.get
:
Rompiendo BeautifulSoup Down:
requests.get(url, headers=headers)
es la variable que creamos para asignar el métodoheaders
to, que especifica un formato de resultados deseado utilizando el analizador HTML; esto permite que Python lea los componentes de la página en lugar de tratarla como una cadena largaBeautifulSoup
imprimirá lo que hemos tomado en un formato de árbol más estructurado, lo que facilitará su lectura.
Los resultados de la impresión se verán más ordenados, así:
|_+_|Rotura |_+_| abajo:
soup = BeautifulSoup(results.text, 'html.parser') print(soup.prettify())
es la variable que usaremos para almacenar todos lossoup
contenedores con una clase deBeatifulSoup
- el
print(soup.prettify())
El método extrae todos losWe need to figure out what distinguishes each of these from other
div
contenedores que tienen undiv
atributo declass
de lo que hemos almacenado en nuestra sopa variable.
Prepárate para extraer cada artículo
¡Nos están perdiendo los ingresos brutos! Si miras la segunda película, la han incluido allí.
Algo a tener en cuenta siempre al crear un raspador web es la idea de que no toda la información que busca estará disponible para que la recopile.
En estos casos, debemos asegurarnos de que nuestro raspador web no deje de funcionar o se rompa cuando llegue a los datos faltantes y construyamos en torno a la idea de que simplemente no sabemos si eso sucederá o no.
Entrar en cada lister-item
div
Cuando tomamos cada uno de los elementos que necesitamos en un solo mode-advanced
lister-item mode-advanced
contenedor, necesitamos que el raspador pase al siguiente div
lister-item mode-advanced
contenedor y coge esos elementos de la película también. Y luego debe pasar a la siguiente y así sucesivamente, 50 veces para cada página. Para que esto se ejecute, tendremos que envolver nuestro raspador en un bucle for.
Rompiendo el |_+_| círculo:
- Un bucle
lister-item mode-advanced
se utiliza para iterar sobre una secuencia. Nuestra secuencia es cada contenedormovie_div = soup.find_all('div', class_='lister-item mode-advanced')
div que almacenamos en**find_all**
movie_div
es el nombre de la variable que ingresa cadadiv
. Puede nombrar esto como quiera (lister-item mode-advanced
,find_all()
,div
,class
), y no cambiará la función del bucle.
Se puede leer así:
|_+_|Extrae el título de la película.
Comenzando con el nombre de la película, busquemos su línea HTML correspondiente usando inspeccionar y haciendo clic en el título.
Vemos que el nombre está contenido dentro de una etiqueta de anclaje, lister-item mode-advanced
. El lister-item mode-advanced
etiqueta. Este `` es el tercero de los lister-item mode-advanced
s anidados en el contenedor de la primera película.
Desglosando títulos:
div
es la variable que usaremos para almacenar los datos del título que encontremoslister-item mode-advanced
es lo que se usa en nuestro bucle for; se usa para iterar cada vezdiv
y#initiate the for loop #this tells your scraper to iterate through #every div container we stored in move_div for container in movie_div:
es notación de atributo y le dice al raspador que acceda a cada una de esas etiquetas**for**
le dice al raspador que tome el texto anidado en la etiqueta ``for
le dice al raspador que tome lo que encontramos y almacenamos enlister-item mode-advanced
y agregarlo a nuestra lista vacía llamadamovie_div
, que creamos al principio
Extraer año de lanzamiento
Busquemos el año de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el año.
Vemos que estos datos se almacenan dentro de container
etiqueta que contiene el título de la película. La notación de puntos, que usamos para encontrar los datos del título (div
), funcionó porque era la primera x
La etiqueta que queremos es la segunda etiqueta, tenemos que usar un método diferente.
En cambio, podemos decirle a nuestro raspador que busque por la marca distintiva del segundo ''. Usaremos el loop
método , que es similar a banana
excepto que solo devuelve la primera coincidencia.
Desglosando años:
cheese
es la variable que usaremos para almacenar los datos del año que encontremosfor in : OR in our case... for each lister-item mode-advanced div container: scrape these elements
es lo que usamos en nuestro. This tag is nested within a header tag,
bucle: se usa para iterar cada veztag is nested within a
es la notación de atributo, que le dice al raspador que acceda a esa etiquetadiv
es un método que usaremos para acceder a esta `` etiquetaname = container.h3.a.text titles.append(name)
es la etiqueta distintiva que queremosname
le dice al raspador que tome lo que encontramos y almacenamos encontainer
y agregarlo a nuestra lista vacía llamada.h3
(que creamos al principio)
Extraer la duración de la película
Busque la duración de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el total de minutos.
Los datos que necesitamos se pueden encontrar en una etiqueta con una clase de .a
. Como hicimos con el año, podemos hacer algo similar:
Rompiendo el tiempo:
.text
es la variable que usaremos para almacenar los datos de tiempo que encontremostitles.append(name)
es lo que usamos en nuestroname
bucle: se usa para iterar cada veztitles
es un método que usaremos para acceder a esta `` etiquetatag below the
es la etiqueta distintiva que queremos.h3.a
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allítag after the `h3` tag. Since the
le dice al raspador que tome ese texto en la etiqueta ``find()
le dice al raspador que tome lo que encontramos y almacenamos enfind_all()
y agregarlo a nuestra lista vacía llamadayear = container.h3.find('span', class_='lister-item-year').text years.append(year)
(que creamos al principio)
Extraer calificaciones de IMDb
Busque la calificación de IMDb de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en la calificación de IMDb.
Ahora, nos centraremos en extraer la calificación de IMDb. Los datos que necesitamos se pueden encontrar en un year
etiqueta. Como no veo ningún otro container
etiquetas, podemos usar la notación de atributos (notación de puntos) para capturar estos datos.
Desglosando las calificaciones de IMDb:
for
es la variable que usaremos para almacenar los datos de calificaciones de IMDB que encuentre.h3
es lo que usamos en nuestro.find()
bucle: se usa para iterar cada vez(‘span’, class_ = ‘lister-item-year’)
es una notación de atributo que le dice al raspador que acceda a esa etiquetayears.append(year)
le dice al raspador que tome ese texto- El
year
El método convierte el texto que encontramos en un flotante, que es un decimal years
le dice al raspador que tome lo que encontramos y almacenamos enruntime
y agregarlo a nuestra lista vacía llamadaruntime = container.find('span', class_='runtime').text if container.p.find('span', class_='runtime') else '' time.append(runtime)
(que creamos al principio)
Extraer Metascore
Busque la calificación de Metascore de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el número de Metascore.
Los datos que necesitamos se pueden encontrar en una etiqueta `` que tiene una clase que dice runtime
.
Antes de decidirnos por eso, debe notar que, por supuesto, un 96 para Parasite muestra una calificación favorable, pero ¿son favorables los demás? Si resaltas el Metascore de la siguiente película, verás que JoJo Rabbit tiene una clase que dice container
. Dado que estas etiquetas son diferentes, sería seguro decirle al raspador que use solo la clase for
al raspar:
Desglosando Metascores:
.find()
es la variable que usaremos para almacenar los datos de calificación de Metascore que encuentre(‘span’, class_ = ‘runtime’)
es lo que usamos en nuestroif container.p.find(‘span’, class_=’runtime’) else ‘-’
bucle: se usa para iterar cada vez.text
es un método que usaremos para acceder a esta `` etiquetatime.append(runtime)
es la etiqueta distintiva que queremosruntime
le dice al raspador que tome ese textotime
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allí- El
**
El método convierte el texto que encontramos en un número entero. **
le dice al raspador que tome lo que encontramos y almacenamos enimdb = float(container.strong.text) imdb_ratings.append(imdb)
y agregarlo a nuestra lista vacía llamadaimdb
(que creamos al principio)
Extraer votos e ingresos brutos
Finalmente llegamos a los dos últimos elementos que necesitamos extraer, pero dejamos los más difíciles para el final.
Aquí es donde las cosas se ponen un poco complicadas. Como se mencionó anteriormente, debería haber notado que cuando miramos la primera película de esta lista, no vemos un número de ingresos brutos. Cuando miramos la segunda película de la lista, podemos ver ambas.
Echemos un vistazo al código HTML de la segunda película y partamos de ahí.
Tanto los votos como los brutos están resaltados a la derecha. Después de mirar los votos y los contenedores brutos para la película # 2, ¿qué notas?
Android: barra de calificación
Como puede ver, ambos están en una etiqueta `` que tiene un container
atributo que es igual a for
y un .strong
atributo que contiene los valores del número distintivo que necesitamos para cada uno.
¿Cómo podemos obtener los datos del segundo si los parámetros de búsqueda del primero son los mismos? ¿Cómo le decimos a nuestro raspador que se salte el primero y raspe el segundo?
Este requirió mucho esfuerzo mental, toneladas de café y un par de noches para descubrirlo. Así es como lo hice:
|_+_|Rompiendo votos y brutos abajo:
.text
es una variable completamente nueva que usaremos para almacenar tanto los votos como las etiquetas `` brutas ''float()
es lo que usamos en nuestroimdb_ratings.append(imdb)
bucle para iterar cada vezimdb
es el método que usaremos para obtener las dos `` etiquetasimdb_ratings
es cómo podemos tomar los atributos de esa etiqueta específica.metascore favorable
es la variable que usaremos para almacenar los votos que encontremos en elmetascore mixed
etiquetametascore
le dice al raspador que entre enm_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score)
etiqueta y toma los primeros datos de la lista, que son los votos porquem_score
viene primero en nuestro código HTML (las computadoras cuentan en binario; comienzan a contar en 0, no en 1)container
le dice al raspador que tome ese textofor
le dice al raspador que tome lo que encontramos y almacenamos en.find()
y agregarlo a nuestra lista vacía llamada(‘span’, class_ = ‘metascore’)
(que creamos al principio).text
es la variable que usaremos para almacenar el bruto que encontremos enif container.find(‘span’, class_=’metascore’) else ‘-’
etiquetaint()
le dice al raspador que entre enmetascores.append(m_score)
etiqueta y tome el segundo dato de la lista, que esm_score
porquemetascores
ocupa el segundo lugar en nuestro código HTMLname
dice si la longitud denv
es mayor que uno, luego busque el segundo dato almacenado. Pero si los datos almacenados endata-value
no es mayor que uno, es decir, si falta el valor bruto, coloque un guión allínv = container.find_all('span', attrs={'name': 'nv'}) vote = nv[0].text votes.append(vote) grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
le dice al raspador que tome lo que encontramos y almacenamos ennv
y agregarlo a nuestra lista vacía llamadacontainer
(que creamos al principio)
Su código ahora debería verse así:
|_+_|Veamos lo que tenemos hasta ahora
Ahora que le hemos dicho a nuestro raspador qué elementos raspar, usemos el for
función para imprimir cada lista a la que hemos enviado nuestros datos extraídos:
Nuestras listas se ven así
|_+_|Hasta ahora todo va bien, pero todavía no hemos llegado a ese punto. Necesitamos limpiar un poco nuestros datos. Parece que tenemos algunos elementos no deseados en nuestros datos: signos de dólar, find_all()
s, (‘span’, attrs = ‘name’ : ’nv’)
s, comas, paréntesis y espacio en blanco adicional en los Metascores.
Construyendo un DataFrame con pandas
El siguiente orden del día es construir un vote
con pandas para almacenar los datos que tenemos muy bien en una tabla para comprender realmente lo que está sucediendo.
Así es como lo hacemos:
|_+_|Desglosando nuestro marco de datos:
nv
es lo que llamaremos nuestronv[0]
nv
así es como inicializamos la creación de unvotes
con pandas- Las claves de la izquierda son los nombres de las columnas.
- Los valores de la derecha son nuestras listas de datos que hemos extraído
Ver nuestro DataFrame
Podemos ver cómo se ve todo simplemente usando el .text
función en nuestro votes.append(vote)
—Que llamamos vote
—Al final de nuestro programa:
Nuestro DataFrame de pandas se ve así
Calidad de los datos
Antes de embarcarse en proyectos como este, debe saber cuáles son sus criterios de calidad de datos, es decir, qué reglas o restricciones deben seguir sus datos. Aquí hay unos ejemplos:
- Restricciones de tipo de datos: Los valores en sus columnas deben ser de un tipo de datos particular: numérico, booleano, fecha, etc.
- Restricciones obligatorias : Algunas columnas no pueden estar vacías
- Patrones de expresión regular: T campos ext que tienen que estar en un patrón determinado, como números de teléfono
¿Qué es la limpieza de datos?
Limpieza de datos es el proceso de detección y corrección o eliminación de registros corruptos o inexactos de su conjunto de datos.
Al realizar un análisis de datos, también es importante asegurarse de que estamos usando los tipos de datos correctos.
Comprobación de tipos de datos
Podemos comprobar cómo se ven nuestros tipos de datos ejecutando esto votes
función en la parte inferior de nuestro programa:
Resultados de nuestro tipo de datos
Analicemos esto: nuestro tipo de datos de películas es un objeto, que es lo mismo que una cadena, lo cual sería correcto considerando que son títulos de películas. Nuestra puntuación de IMDb también es correcta porque tenemos números de punto flotante en esta columna (números decimales).
Pero nuestros grosses
, nv
, nv[1]
y nv
muestran que son objetos cuando deberían ser tipos de datos enteros, y nuestro gross
es un objeto en lugar de un gross
tipo de datos. ¿Cómo pasó esto?
Inicialmente, cuando le decíamos a nuestro raspador que tomara estos valores de cada contenedor HTML, le decíamos que tomara valores específicos de una cadena. Una cadena representa texto en lugar de números; se compone de un conjunto de caracteres que pueden además contienen números.
Por ejemplo, la palabra nv[1].text if len(nv) > 1 else ‘-’
y la frase nv
son ambas cadenas. Si tuviéramos que deshacernos de todo excepto de nv
de us_gross.append(grosses)
, sigue siendo una cadena, pero ahora es una que solo dice grosses
.
Limpieza de datos con pandas
Ahora que tenemos una idea clara de cómo se ven nuestros datos en este momento, es hora de comenzar a limpiarlos.
Esta puede ser una tarea tediosa, pero es muy importante.
Datos del año de limpieza
Para eliminar los paréntesis de nuestros datos anuales y convertir el objeto en un tipo de datos enteros, haremos lo siguiente:
|_+_|Desglose de los datos del año de limpieza:
us_grosses
le dice a los pandas que vayan a la columnaimport requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np url = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' headers = {'Accept-Language': 'en-US, en;q=0.5'} results = requests.get(url, headers=headers) soup = BeautifulSoup(results.text, 'html.parser') titles = [] years = [] time = [] imdb_ratings = [] metascores = [] votes = [] us_gross = [] movie_div = soup.find_all('div', class_='lister-item mode-advanced') for container in movie_div: #Name name = container.h3.a.text titles.append(name) #year year = container.h3.find('span', class_='lister-item-year').text years.append(year) #time runtime = container.p.find('span', class_='runtime').text if container.p.find('span', class_='runtime').text else '-' time.append(runtime) #IMDb rating imdb = float(container.strong.text) imdb_ratings.append(imdb) #metascore m_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score) #here are two NV containers, grab both of them as they hold both the votes and the grosses nv = container.find_all('span', attrs={'name': 'nv'}) #filter nv for votes vote = nv[0].text votes.append(vote) #filter nv for gross grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
en nuestroprint
print(titles) print(years) print(time) print(imdb_ratings) print(metascores) print(votes) print(us_gross)
este método:['Parasite', 'Jojo Rabbit', '1917', 'Knives Out', 'Uncut Gems', 'Once Upon a Time... in Hollywood', 'Joker', 'The Gentlemen', 'Ford v Ferrari', 'Little Women', 'The Irishman', 'The Lighthouse', 'Toy Story 4', 'Marriage Story', 'Avengers: Endgame', 'The Godfather', 'Blade Runner 2049', 'The Shawshank Redemption', 'The Dark Knight', 'Inglourious Basterds', 'Call Me by Your Name', 'The Two Popes', 'Pulp Fiction', 'Inception', 'Interstellar', 'Green Book', 'Blade Runner', 'The Wolf of Wall Street', 'Gone Girl', 'The Shining', 'The Matrix', 'Titanic', 'The Silence of the Lambs', 'Three Billboards Outside Ebbing, Missouri', 'Harry Potter and the Sorcerer's Stone', 'The Peanut Butter Falcon', 'The Handmaiden', 'Memories of Murder', 'The Lord of the Rings: The Fellowship of the Ring', 'Gladiator', 'The Martian', 'Bohemian Rhapsody', 'Watchmen', 'Forrest Gump', 'Thor: Ragnarok', 'Casino Royale', 'The Breakfast Club', 'The Godfather: Part II', 'Django Unchained', 'Baby Driver'] ['(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(I) (2019)', '(2019)', '(2019)', '(2019)', '(1972)', '(2017)', '(1994)', '(2008)', '(2009)', '(2017)', '(2019)', '(1994)', '(2010)', '(2014)', '(2018)', '(1982)', '(2013)', '(2014)', '(1980)', '(1999)', '(1997)', '(1991)', '(2017)', '(2001)', '(2019)', '(2016)', '(2003)', '(2001)', '(2000)', '(2015)', '(2018)', '(2009)', '(1994)', '(2017)', '(2006)', '(1985)', '(1974)', '(2012)', '(2017)'] ['132 min', '108 min', '119 min', '131 min', '135 min', '161 min', '122 min', '113 min', '152 min', '135 min', '209 min', '109 min', '100 min', '137 min', '181 min', '175 min', '164 min', '142 min', '152 min', '153 min', '132 min', '125 min', '154 min', '148 min', '169 min', '130 min', '117min', '180 min', '149 min', '146 min', '136 min', '194 min', '118 min', '115 min', '152 min', '97 min', '145 min', '132 min', '178 min', '155 min', '144 min', '134 min', '162 min', '142 min', '130 min', '144 min', '97 min', '202 min', '165 min', '113 min'] [8.6, 8.0, 8.5, 8.0, 7.6, 7.7, 8.6, 8.1, 8.2, 8.0, 8.0, 7.7, 7.8, 8.0, 8.5, 9.2, 8.0, 9.3, 9.0, 8.3, 7.9, 7.6, 8.9, 8.8, 8.6, 8.2, 8.1, 8.2, 8.1,8.4, 8.7, 7.8, 8.6, 8.2, 7.6, 7.7, 8.1, 8.1, 8.8, 8.5, 8.0, 8.0, 7.6, 8.8, 7.9, 8.0, 7.9, 9.0, 8.4, 7.6] ['96 ', '58 ', '78 ', '82 ', '90 ', '83 ', '59 ', '51 ', '81 ', '91 ', '94 ', '83 ', '84 ', '93 ', '78 ', '100 ', '81 ', '80 ', '84 ', '69 ', '93 ', '75 ', '94 ', '74 ', '74 ', '69 ', '84 ', '75 ', '79 ', '66 ', '73 ', '75 ', '85 ', '88 ', '64 ', '70 ', '84 ', '82 ', '92 ', '67 ', '80 ', '49 ', '56 ', '82 ', '74 ', '80 ', '62 ', '90 ', '81 ', '86 '] ['282,699', '142,517', '199,638', '195,728', '108,330', '396,071', '695,224', '42,015', '152,661', '65,234', '249,950', '77,453', '160,180', '179,887', '673,115', '1,511,929', '414,992', '2,194,397', '2,176,865', '1,184,882', '178,688', '76,291', '1,724,518', '1,925,684', '1,378,968', '293,695', '656,442', '1,092,063', '799,696', '835,496', '1,580,250', '994,453', '1,191,182', '383,958', '595,613', '34,091', '92,492', '115,125', '1,572,354', '1,267,310', '715,623', '410,199', '479,811', '1,693,344', '535,065', '555,756', '330,308', '1,059,089', '1,271,569', '398,553'] ['-', '
dice que extraiga todos los dígitos de la cadenaConstruyendo un Web Scraper con Python en 30 minutos
Mis habilidades en Python son básicas, así que si estás aquí sin muchas habilidades en codificación, espero que esta guía te ayude a adquirir más conocimientos y comprensión.
El proyecto perfecto para principiantes
Para obtener datos para proyectos de ML, IA o ciencia de datos, a menudo dependerá de bases de datos, API o conjuntos de datos CSV listos para usar. Pero, ¿qué pasa si no puede encontrar un conjunto de datos que desee utilizar y analizar? Ahí es donde entra en juego un raspador web.
Trabajar en proyectos es fundamental para consolidar los conocimientos adquiridos. Cuando comencé este proyecto, estaba un poco abrumado porque realmente no sabía nada.
Seguir con él, encontrar respuestas a mis preguntas en Stack Overflow y muchas pruebas y errores me ayudaron a comprender realmente cómo funciona la programación: cómo funcionan las páginas web, cómo usar bucles y cómo crear funciones y mantener los datos limpios. Hace que la construcción de un raspador web sea el proyecto perfecto para principiantes para cualquiera que se esté iniciando en Python.
Que cubriremos
Esta guía lo guiará a través de la comprensión de las páginas web HTML, la creación de un raspador web con Python y la creación de un
DataFrame
con pandas. Cubrirá la calidad de los datos, la limpieza de datos y la conversión de tipos de datos, completamente paso a paso y con instrucciones, código y explicaciones sobre cómo funciona cada pieza. ¡Espero que codifiques y disfrutes!Descargo de responsabilidad
Los sitios web pueden restringir o prohibir la extracción de datos de su sitio web. Los usuarios pueden estar sujetos a ramificaciones legales dependiendo de dónde y cómo intente recopilar información. Los sitios web suelen describir esto en sus términos de uso y en su
robots.txt
archivo encontrado en su sitio, que generalmente se parece a esto:[www.example.com/robots.txt](http://www.[site].com/robots.txt.)
. Así que raspe responsablemente y respete elrobots.txt
.¿Qué es Web Scraping?
Raspado web Consiste en recopilar datos disponibles en sitios web. Esto se puede hacer manualmente por un humano o usando un bot.
A Bot es un programa creado por usted que le ayuda a extraer los datos que necesita mucho más rápido que la mano y los ojos de un ser humano.
¿Qué vamos a raspar?
Es esencial identificar el objetivo de su raspado desde el principio. No queremos extraer ningún dato que en realidad no necesitemos.
Para este proyecto, extraeremos datos de las 1,000 películas principales de IMDb, específicamente las 50 películas principales de esta página. Aquí está la información que recopilaremos de cada lista de películas:
- El título
- El año en que fue lanzado
- Cuanto dura la pelicula
- Calificación de IMDb de la película
- El Metascore de la película
- ¿Cuántos votos obtuvo la película?
- Las ganancias brutas de la película en EE. UU.
¿Cómo funcionan los Web Scrapers?
Los web scrapers recopilan datos del sitio web de la misma manera que lo haría un humano: van a una página web del sitio web, obtienen los datos relevantes y pasan a la siguiente página web, solo que mucho más rápido.
Cada sitio web tiene una estructura diferente. Estas son algunas cosas importantes en las que pensar al crear un raspador web:
- ¿Cuál es la estructura de la página web que contiene los datos que busca?
- ¿Cómo llegamos a esas páginas web?
- ¿Necesitará recopilar más datos de la página siguiente?
La URL
Para empezar, veamos la URL de la página que queremos raspar.
Notamos algunas cosas sobre la URL:
?
actúa como un separador: indica el final de la ruta del recurso URL y el inicio de los parámetrosgroups=top_1000
especifica de qué se tratará la página&ref_adv_prv
nos lleva a la página anterior o siguiente. La referencia es la página en la que estamos actualmente.adv_nxt
yadv_prv
hay dos valores posibles, traducidos a avanzar a la página siguiente y avanzar a la página anterior.
Cuando navega hacia adelante y hacia atrás por las páginas, notará que solo cambian los parámetros. Tenga en cuenta esta estructura, ya que es útil saberlo mientras construimos el raspador.
El HTML
HTML significa Lenguaje de marcado de hipertexto, y la mayoría de las páginas web se escriben con él. Esencialmente, HTML es cómo dos computadoras se comunican entre sí a través de Internet, y los sitios web están qué ellos dicen.
Cuando accede a una URL, su computadora envía una solicitud al servidor que aloja el sitio. Se puede ejecutar cualquier tecnología en ese servidor (JavaScript, Ruby, Java, etc.) para procesar su solicitud. Finalmente, el servidor devuelve una respuesta a su navegador; a menudo, esa respuesta será en forma de una página HTML para que la muestre su navegador.
HTML describe la estructura de una página web de forma semántica y originalmente incluía señales para la apariencia del documento.
Inspeccionar HTML
Los usuarios de Chrome, Firefox y Safari pueden examinar la estructura HTML de cualquier página haciendo clic con el botón derecho del mouse y presionando la opción Inspeccionar.
Aparecerá un menú en la parte inferior o en el lado derecho de su página con una lista larga de todas las etiquetas HTML que contienen la información que se muestra en la ventana de su navegador. Si estás en Safari (foto de arriba), querrás presionar el botón a la izquierda de la barra de búsqueda, que parece un objetivo. Si está en Chrome o Firefox, hay un pequeño cuadro con un ícono de flecha en la parte superior izquierda que usará para inspeccionar.
Una vez que haga clic, si mueve el cursor sobre cualquier elemento de la página, notará que se resaltará junto con las etiquetas HTML en el menú con el que están asociados, como se ve arriba.
Saber cómo leer la estructura básica de la página HTML de una página es importante para que podamos recurrir a Python para que nos ayude a extraer el HTML de la página.
Instrumentos
Las herramientas que vamos a utilizar son:
- Responder (opcional) es un entorno de programación informática interactivo y sencillo que se utiliza a través de su navegador web. Recomiendo usar esto solo para propósitos de código si aún no tiene un IDE. Si usa Repl, asegúrese de estar usando el entorno Python.
- Peticiones nos permitirá enviar solicitudes HTTP para obtener archivos HTML
- Hermosa Sopa nos ayudará a analizar los archivos HTML
- pandas nos ayudará a reunir los datos en un
DataFrame
para limpiarlo y analizarlo - NumPy agregará soporte para funciones matemáticas y herramientas para trabajar con matrices
Ahora, codifiquemos
Puede seguir a continuación dentro de su entorno Repl o IDE, o puede ir directamente a el código completo aquí . ¡Divertirse!
Importar herramientas
Primero, importaremos las herramientas que necesitaremos para que podamos usarlas para ayudarnos a construir el raspador y obtener los datos que necesitamos.
|_+_|Películas en inglés
Es muy probable que cuando ejecutemos nuestro código para extraer algunas de estas películas, obtengamos los nombres de las películas traducidos al idioma principal del país en el que se originó.
Utilice este código para asegurarse de que obtengamos títulos traducidos al inglés de todas las películas que extraemos:
|_+_|Solicitar contenido de la URL
Obtenga el contenido de la página que estamos viendo solicitando la URL:
|_+_|Desglose de las solicitudes de URL:
import requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np
es la variable que creamos y asignamos la URL aheaders = {'Accept-Language': 'en-US, en;q=0.5'}
es la variable que creamos para almacenar nuestrourl = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' results = requests.get(url, headers=headers)
acciónurl
es el método que usamos para capturar el contenido de la URL. Elresults
parte le dice a nuestro raspador que nos traiga inglés, basado en nuestra línea de código anterior.
Usando BeautifulSoup
Haga que el contenido que tomamos sea fácil de leer usando
|_+_|request.get
:Rompiendo BeautifulSoup Down:
requests.get(url, headers=headers)
es la variable que creamos para asignar el métodoheaders
to, que especifica un formato de resultados deseado utilizando el analizador HTML; esto permite que Python lea los componentes de la página en lugar de tratarla como una cadena largaBeautifulSoup
imprimirá lo que hemos tomado en un formato de árbol más estructurado, lo que facilitará su lectura.
Los resultados de la impresión se verán más ordenados, así:
|_+_|Rotura |_+_| abajo:
soup = BeautifulSoup(results.text, 'html.parser') print(soup.prettify())
es la variable que usaremos para almacenar todos lossoup
contenedores con una clase deBeatifulSoup
- el
print(soup.prettify())
El método extrae todos losWe need to figure out what distinguishes each of these from other
div
contenedores que tienen undiv
atributo declass
de lo que hemos almacenado en nuestra sopa variable.
Prepárate para extraer cada artículo
¡Nos están perdiendo los ingresos brutos! Si miras la segunda película, la han incluido allí.
Algo a tener en cuenta siempre al crear un raspador web es la idea de que no toda la información que busca estará disponible para que la recopile.
En estos casos, debemos asegurarnos de que nuestro raspador web no deje de funcionar o se rompa cuando llegue a los datos faltantes y construyamos en torno a la idea de que simplemente no sabemos si eso sucederá o no.
Entrar en cada
lister-item
divCuando tomamos cada uno de los elementos que necesitamos en un solo
|_+_|mode-advanced
lister-item mode-advanced
contenedor, necesitamos que el raspador pase al siguientediv
lister-item mode-advanced
contenedor y coge esos elementos de la película también. Y luego debe pasar a la siguiente y así sucesivamente, 50 veces para cada página. Para que esto se ejecute, tendremos que envolver nuestro raspador en un bucle for.Rompiendo el |_+_| círculo:
- Un bucle
lister-item mode-advanced
se utiliza para iterar sobre una secuencia. Nuestra secuencia es cada contenedormovie_div = soup.find_all('div', class_='lister-item mode-advanced')
div que almacenamos en**find_all**
movie_div
es el nombre de la variable que ingresa cadadiv
. Puede nombrar esto como quiera (lister-item mode-advanced
,find_all()
,div
,class
), y no cambiará la función del bucle.
Se puede leer así:
|_+_|Extrae el título de la película.
Comenzando con el nombre de la película, busquemos su línea HTML correspondiente usando inspeccionar y haciendo clic en el título.
Vemos que el nombre está contenido dentro de una etiqueta de anclaje,
|_+_|lister-item mode-advanced
. Ellister-item mode-advanced
etiqueta. Este `` es el tercero de loslister-item mode-advanced
s anidados en el contenedor de la primera película.Desglosando títulos:
div
es la variable que usaremos para almacenar los datos del título que encontremoslister-item mode-advanced
es lo que se usa en nuestro bucle for; se usa para iterar cada vezdiv
y#initiate the for loop #this tells your scraper to iterate through #every div container we stored in move_div for container in movie_div:
es notación de atributo y le dice al raspador que acceda a cada una de esas etiquetas**for**
le dice al raspador que tome el texto anidado en la etiqueta ``for
le dice al raspador que tome lo que encontramos y almacenamos enlister-item mode-advanced
y agregarlo a nuestra lista vacía llamadamovie_div
, que creamos al principio
Extraer año de lanzamiento
Busquemos el año de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el año.
Vemos que estos datos se almacenan dentro de
container
etiqueta que contiene el título de la película. La notación de puntos, que usamos para encontrar los datos del título (div
), funcionó porque era la primerax
La etiqueta que queremos es la segunda etiqueta, tenemos que usar un método diferente.En cambio, podemos decirle a nuestro raspador que busque por la marca distintiva del segundo ''. Usaremos el
|_+_|loop
método , que es similar abanana
excepto que solo devuelve la primera coincidencia.Desglosando años:
cheese
es la variable que usaremos para almacenar los datos del año que encontremosfor in : OR in our case... for each lister-item mode-advanced div container: scrape these elements
es lo que usamos en nuestro. This tag is nested within a header tag,
bucle: se usa para iterar cada veztag is nested within a
es la notación de atributo, que le dice al raspador que acceda a esa etiquetadiv
es un método que usaremos para acceder a esta `` etiquetaname = container.h3.a.text titles.append(name)
es la etiqueta distintiva que queremosname
le dice al raspador que tome lo que encontramos y almacenamos encontainer
y agregarlo a nuestra lista vacía llamada.h3
(que creamos al principio)
Extraer la duración de la película
Busque la duración de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el total de minutos.
Los datos que necesitamos se pueden encontrar en una etiqueta con una clase de
|_+_|.a
. Como hicimos con el año, podemos hacer algo similar:Rompiendo el tiempo:
.text
es la variable que usaremos para almacenar los datos de tiempo que encontremostitles.append(name)
es lo que usamos en nuestroname
bucle: se usa para iterar cada veztitles
es un método que usaremos para acceder a esta `` etiquetatag below the
es la etiqueta distintiva que queremos.h3.a
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allítag after the `h3` tag. Since the
le dice al raspador que tome ese texto en la etiqueta ``find()
le dice al raspador que tome lo que encontramos y almacenamos enfind_all()
y agregarlo a nuestra lista vacía llamadayear = container.h3.find('span', class_='lister-item-year').text years.append(year)
(que creamos al principio)
Extraer calificaciones de IMDb
Busque la calificación de IMDb de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en la calificación de IMDb.
Ahora, nos centraremos en extraer la calificación de IMDb. Los datos que necesitamos se pueden encontrar en un
|_+_|year
etiqueta. Como no veo ningún otrocontainer
etiquetas, podemos usar la notación de atributos (notación de puntos) para capturar estos datos.Desglosando las calificaciones de IMDb:
for
es la variable que usaremos para almacenar los datos de calificaciones de IMDB que encuentre.h3
es lo que usamos en nuestro.find()
bucle: se usa para iterar cada vez(‘span’, class_ = ‘lister-item-year’)
es una notación de atributo que le dice al raspador que acceda a esa etiquetayears.append(year)
le dice al raspador que tome ese texto- El
year
El método convierte el texto que encontramos en un flotante, que es un decimal years
le dice al raspador que tome lo que encontramos y almacenamos enruntime
y agregarlo a nuestra lista vacía llamadaruntime = container.find('span', class_='runtime').text if container.p.find('span', class_='runtime') else '' time.append(runtime)
(que creamos al principio)
Extraer Metascore
Busque la calificación de Metascore de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el número de Metascore.
Los datos que necesitamos se pueden encontrar en una etiqueta `` que tiene una clase que dice
runtime
.Antes de decidirnos por eso, debe notar que, por supuesto, un 96 para Parasite muestra una calificación favorable, pero ¿son favorables los demás? Si resaltas el Metascore de la siguiente película, verás que JoJo Rabbit tiene una clase que dice
|_+_|container
. Dado que estas etiquetas son diferentes, sería seguro decirle al raspador que use solo la clasefor
al raspar:Desglosando Metascores:
.find()
es la variable que usaremos para almacenar los datos de calificación de Metascore que encuentre(‘span’, class_ = ‘runtime’)
es lo que usamos en nuestroif container.p.find(‘span’, class_=’runtime’) else ‘-’
bucle: se usa para iterar cada vez.text
es un método que usaremos para acceder a esta `` etiquetatime.append(runtime)
es la etiqueta distintiva que queremosruntime
le dice al raspador que tome ese textotime
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allí- El
**
El método convierte el texto que encontramos en un número entero. **
le dice al raspador que tome lo que encontramos y almacenamos enimdb = float(container.strong.text) imdb_ratings.append(imdb)
y agregarlo a nuestra lista vacía llamadaimdb
(que creamos al principio)
Extraer votos e ingresos brutos
Finalmente llegamos a los dos últimos elementos que necesitamos extraer, pero dejamos los más difíciles para el final.
Aquí es donde las cosas se ponen un poco complicadas. Como se mencionó anteriormente, debería haber notado que cuando miramos la primera película de esta lista, no vemos un número de ingresos brutos. Cuando miramos la segunda película de la lista, podemos ver ambas.
Echemos un vistazo al código HTML de la segunda película y partamos de ahí.
Tanto los votos como los brutos están resaltados a la derecha. Después de mirar los votos y los contenedores brutos para la película # 2, ¿qué notas?
Como puede ver, ambos están en una etiqueta `` que tiene un
container
atributo que es igual afor
y un.strong
atributo que contiene los valores del número distintivo que necesitamos para cada uno.¿Cómo podemos obtener los datos del segundo si los parámetros de búsqueda del primero son los mismos? ¿Cómo le decimos a nuestro raspador que se salte el primero y raspe el segundo?
Este requirió mucho esfuerzo mental, toneladas de café y un par de noches para descubrirlo. Así es como lo hice:
|_+_|Rompiendo votos y brutos abajo:
.text
es una variable completamente nueva que usaremos para almacenar tanto los votos como las etiquetas `` brutas ''float()
es lo que usamos en nuestroimdb_ratings.append(imdb)
bucle para iterar cada vezimdb
es el método que usaremos para obtener las dos `` etiquetasimdb_ratings
es cómo podemos tomar los atributos de esa etiqueta específica.metascore favorable
es la variable que usaremos para almacenar los votos que encontremos en elmetascore mixed
etiquetametascore
le dice al raspador que entre enm_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score)
etiqueta y toma los primeros datos de la lista, que son los votos porquem_score
viene primero en nuestro código HTML (las computadoras cuentan en binario; comienzan a contar en 0, no en 1)container
le dice al raspador que tome ese textofor
le dice al raspador que tome lo que encontramos y almacenamos en.find()
y agregarlo a nuestra lista vacía llamada(‘span’, class_ = ‘metascore’)
(que creamos al principio).text
es la variable que usaremos para almacenar el bruto que encontremos enif container.find(‘span’, class_=’metascore’) else ‘-’
etiquetaint()
le dice al raspador que entre enmetascores.append(m_score)
etiqueta y tome el segundo dato de la lista, que esm_score
porquemetascores
ocupa el segundo lugar en nuestro código HTMLname
dice si la longitud denv
es mayor que uno, luego busque el segundo dato almacenado. Pero si los datos almacenados endata-value
no es mayor que uno, es decir, si falta el valor bruto, coloque un guión allínv = container.find_all('span', attrs={'name': 'nv'}) vote = nv[0].text votes.append(vote) grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
le dice al raspador que tome lo que encontramos y almacenamos ennv
y agregarlo a nuestra lista vacía llamadacontainer
(que creamos al principio)
Su código ahora debería verse así:
|_+_|Veamos lo que tenemos hasta ahora
Ahora que le hemos dicho a nuestro raspador qué elementos raspar, usemos el
|_+_|for
función para imprimir cada lista a la que hemos enviado nuestros datos extraídos:Nuestras listas se ven así
|_+_|Hasta ahora todo va bien, pero todavía no hemos llegado a ese punto. Necesitamos limpiar un poco nuestros datos. Parece que tenemos algunos elementos no deseados en nuestros datos: signos de dólar,
find_all()
s,(‘span’, attrs = ‘name’ : ’nv’)
s, comas, paréntesis y espacio en blanco adicional en los Metascores.Construyendo un DataFrame con pandas
El siguiente orden del día es construir un
vote
con pandas para almacenar los datos que tenemos muy bien en una tabla para comprender realmente lo que está sucediendo.Así es como lo hacemos:
|_+_|Desglosando nuestro marco de datos:
nv
es lo que llamaremos nuestronv[0]
nv
así es como inicializamos la creación de unvotes
con pandas- Las claves de la izquierda son los nombres de las columnas.
- Los valores de la derecha son nuestras listas de datos que hemos extraído
Ver nuestro DataFrame
Podemos ver cómo se ve todo simplemente usando el
|_+_|.text
función en nuestrovotes.append(vote)
—Que llamamosvote
—Al final de nuestro programa:Nuestro DataFrame de pandas se ve así
Calidad de los datos
Antes de embarcarse en proyectos como este, debe saber cuáles son sus criterios de calidad de datos, es decir, qué reglas o restricciones deben seguir sus datos. Aquí hay unos ejemplos:
- Restricciones de tipo de datos: Los valores en sus columnas deben ser de un tipo de datos particular: numérico, booleano, fecha, etc.
- Restricciones obligatorias : Algunas columnas no pueden estar vacías
- Patrones de expresión regular: T campos ext que tienen que estar en un patrón determinado, como números de teléfono
¿Qué es la limpieza de datos?
Limpieza de datos es el proceso de detección y corrección o eliminación de registros corruptos o inexactos de su conjunto de datos.
Al realizar un análisis de datos, también es importante asegurarse de que estamos usando los tipos de datos correctos.
Comprobación de tipos de datos
Podemos comprobar cómo se ven nuestros tipos de datos ejecutando esto
|_+_|votes
función en la parte inferior de nuestro programa:Resultados de nuestro tipo de datos
Analicemos esto: nuestro tipo de datos de películas es un objeto, que es lo mismo que una cadena, lo cual sería correcto considerando que son títulos de películas. Nuestra puntuación de IMDb también es correcta porque tenemos números de punto flotante en esta columna (números decimales).
Pero nuestros
grosses
,nv
,nv[1]
ynv
muestran que son objetos cuando deberían ser tipos de datos enteros, y nuestrogross
es un objeto en lugar de ungross
tipo de datos. ¿Cómo pasó esto?Inicialmente, cuando le decíamos a nuestro raspador que tomara estos valores de cada contenedor HTML, le decíamos que tomara valores específicos de una cadena. Una cadena representa texto en lugar de números; se compone de un conjunto de caracteres que pueden además contienen números.
Por ejemplo, la palabra
nv[1].text if len(nv) > 1 else ‘-’
y la frasenv
son ambas cadenas. Si tuviéramos que deshacernos de todo excepto denv
deus_gross.append(grosses)
, sigue siendo una cadena, pero ahora es una que solo dicegrosses
.Limpieza de datos con pandas
Ahora que tenemos una idea clara de cómo se ven nuestros datos en este momento, es hora de comenzar a limpiarlos.
Esta puede ser una tarea tediosa, pero es muy importante.
Datos del año de limpieza
Para eliminar los paréntesis de nuestros datos anuales y convertir el objeto en un tipo de datos enteros, haremos lo siguiente:
|_+_|Desglose de los datos del año de limpieza:
us_grosses
le dice a los pandas que vayan a la columnaimport requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np url = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' headers = {'Accept-Language': 'en-US, en;q=0.5'} results = requests.get(url, headers=headers) soup = BeautifulSoup(results.text, 'html.parser') titles = [] years = [] time = [] imdb_ratings = [] metascores = [] votes = [] us_gross = [] movie_div = soup.find_all('div', class_='lister-item mode-advanced') for container in movie_div: #Name name = container.h3.a.text titles.append(name) #year year = container.h3.find('span', class_='lister-item-year').text years.append(year) #time runtime = container.p.find('span', class_='runtime').text if container.p.find('span', class_='runtime').text else '-' time.append(runtime) #IMDb rating imdb = float(container.strong.text) imdb_ratings.append(imdb) #metascore m_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score) #here are two NV containers, grab both of them as they hold both the votes and the grosses nv = container.find_all('span', attrs={'name': 'nv'}) #filter nv for votes vote = nv[0].text votes.append(vote) #filter nv for gross grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
en nuestroprint
print(titles) print(years) print(time) print(imdb_ratings) print(metascores) print(votes) print(us_gross)
este método:['Parasite', 'Jojo Rabbit', '1917', 'Knives Out', 'Uncut Gems', 'Once Upon a Time... in Hollywood', 'Joker', 'The Gentlemen', 'Ford v Ferrari', 'Little Women', 'The Irishman', 'The Lighthouse', 'Toy Story 4', 'Marriage Story', 'Avengers: Endgame', 'The Godfather', 'Blade Runner 2049', 'The Shawshank Redemption', 'The Dark Knight', 'Inglourious Basterds', 'Call Me by Your Name', 'The Two Popes', 'Pulp Fiction', 'Inception', 'Interstellar', 'Green Book', 'Blade Runner', 'The Wolf of Wall Street', 'Gone Girl', 'The Shining', 'The Matrix', 'Titanic', 'The Silence of the Lambs', 'Three Billboards Outside Ebbing, Missouri', 'Harry Potter and the Sorcerer's Stone', 'The Peanut Butter Falcon', 'The Handmaiden', 'Memories of Murder', 'The Lord of the Rings: The Fellowship of the Ring', 'Gladiator', 'The Martian', 'Bohemian Rhapsody', 'Watchmen', 'Forrest Gump', 'Thor: Ragnarok', 'Casino Royale', 'The Breakfast Club', 'The Godfather: Part II', 'Django Unchained', 'Baby Driver'] ['(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(I) (2019)', '(2019)', '(2019)', '(2019)', '(1972)', '(2017)', '(1994)', '(2008)', '(2009)', '(2017)', '(2019)', '(1994)', '(2010)', '(2014)', '(2018)', '(1982)', '(2013)', '(2014)', '(1980)', '(1999)', '(1997)', '(1991)', '(2017)', '(2001)', '(2019)', '(2016)', '(2003)', '(2001)', '(2000)', '(2015)', '(2018)', '(2009)', '(1994)', '(2017)', '(2006)', '(1985)', '(1974)', '(2012)', '(2017)'] ['132 min', '108 min', '119 min', '131 min', '135 min', '161 min', '122 min', '113 min', '152 min', '135 min', '209 min', '109 min', '100 min', '137 min', '181 min', '175 min', '164 min', '142 min', '152 min', '153 min', '132 min', '125 min', '154 min', '148 min', '169 min', '130 min', '117min', '180 min', '149 min', '146 min', '136 min', '194 min', '118 min', '115 min', '152 min', '97 min', '145 min', '132 min', '178 min', '155 min', '144 min', '134 min', '162 min', '142 min', '130 min', '144 min', '97 min', '202 min', '165 min', '113 min'] [8.6, 8.0, 8.5, 8.0, 7.6, 7.7, 8.6, 8.1, 8.2, 8.0, 8.0, 7.7, 7.8, 8.0, 8.5, 9.2, 8.0, 9.3, 9.0, 8.3, 7.9, 7.6, 8.9, 8.8, 8.6, 8.2, 8.1, 8.2, 8.1,8.4, 8.7, 7.8, 8.6, 8.2, 7.6, 7.7, 8.1, 8.1, 8.8, 8.5, 8.0, 8.0, 7.6, 8.8, 7.9, 8.0, 7.9, 9.0, 8.4, 7.6] ['96 ', '58 ', '78 ', '82 ', '90 ', '83 ', '59 ', '51 ', '81 ', '91 ', '94 ', '83 ', '84 ', '93 ', '78 ', '100 ', '81 ', '80 ', '84 ', '69 ', '93 ', '75 ', '94 ', '74 ', '74 ', '69 ', '84 ', '75 ', '79 ', '66 ', '73 ', '75 ', '85 ', '88 ', '64 ', '70 ', '84 ', '82 ', '92 ', '67 ', '80 ', '49 ', '56 ', '82 ', '74 ', '80 ', '62 ', '90 ', '81 ', '86 '] ['282,699', '142,517', '199,638', '195,728', '108,330', '396,071', '695,224', '42,015', '152,661', '65,234', '249,950', '77,453', '160,180', '179,887', '673,115', '1,511,929', '414,992', '2,194,397', '2,176,865', '1,184,882', '178,688', '76,291', '1,724,518', '1,925,684', '1,378,968', '293,695', '656,442', '1,092,063', '799,696', '835,496', '1,580,250', '994,453', '1,191,182', '383,958', '595,613', '34,091', '92,492', '115,125', '1,572,354', '1,267,310', '715,623', '410,199', '479,811', '1,693,344', '535,065', '555,756', '330,308', '1,059,089', '1,271,569', '398,553'] ['-', '$0.35M', '-', '-', '-', '$135.37M', '$192.73M', '-', '-', '-', '-', '$0.43M', '$433.03M', '-', '$858.37M', '$134.97M', '$92.05M', '$28.34M', '$534.86M', '$120.54M', '$18.10M', '-', '$107.93M', '$292.58M', '$188.02M', '$85.08M', '$32.87M', '$116.90M', '$167.77M', '$44.02M', '$171.48M', '$659.33M', '$130.74M', '$54.51M', '$317.58M', '$13.12M', '$2.01M', '$0.01M', '$315.54M', '$187.71M', '$228.43M', '$216.43M', '$107.51M', '$330.25M', '$315.06M', '$167.45M', '$45.88M', '$57.30M', '$162.81M', '$107.83M']
dice que extraiga todos los dígitos de la cadena- El
M
El método convierte el resultado en un número entero.
Ahora, si ejecutamos
min
en la parte inferior de nuestro programa para ver cómo se ven nuestros datos anuales, este es el resultado:Debería ver su lista de años sin paréntesis. Y el tipo de datos que se muestra ahora es un número entero. Nuestros datos anuales se limpian oficialmente.
Datos de tiempo de limpieza
Haremos exactamente lo que hicimos al limpiar nuestros datos de años anteriores a nuestros datos de tiempo al tomar solo los dígitos y convertir nuestro tipo de datos en un número entero.
|_+_|Limpieza de datos de Metascore
La única limpieza que debemos hacer aquí es convertir nuestro tipo de datos de objeto en un número entero:
|_+_|Limpieza de votos
Con los votos, necesitamos eliminar las comas y convertirlo en un tipo de datos entero:
|_+_|Rompiendo los votos de limpieza en contra:
DataFrame
son nuestros datos de votos en nuestras películasmovies = pd.DataFrame({ 'movie': titles, 'year': years, 'timeMin': time, 'imdb': imdb_ratings, 'metascore': metascores, 'votes': votes, 'us_grossMillions': us_gross, })
. Estamos asignando nuestros nuevos datos limpiados a nuestros votosmovies
.- .
DataFrame
agarra la cadena y usa elpd.DataFrame
método para reemplazar las comas con una cita vacía (nada) - El
DataFrame
El método convierte el resultado en un número entero.
Limpieza de datos brutos
Los datos brutos implican algunos obstáculos que superar. Lo que tenemos que hacer es eliminar el signo de dólar y el
|_+_|print
s de los datos y convertirlo en un número de punto flotante. A continuación, le indicamos cómo hacerlo:Desglose de la limpieza en bruto:
Código de limpieza superior:
DataFrame
son nuestros datos brutos en nuestras películasmovies
. Asignaremos nuestros nuevos datos limpiados a nuestroprint(movies)
columna.print
le dice a los pandas que vayan a la columnaprint(movies.dtypes)
en nuestroyear
- El
timeMin
la función llama a la función especificada para cada elemento de un iterable metascore
es una función anónima en Python (una sin nombre). Las funciones normales se definen mediantevotes
palabra clave.us_grossMillions
son nuestros argumentos de función. Esto le dice a nuestra función que elimine elfloat
del lado izquierdo y pele elcheese
desde el lado derecho.
Código de conversión inferior:
I ate 10 blocks of cheese
está despojado de los elementos que no necesitamos, y ahora le asignaremos los datos del código de conversión para terminarlo10
es un método que podemos usar para cambiar esta columna a un flotante. La razón por la que usamos esto es porque tenemos muchos guiones en esta columna, y no podemos convertirla en un flotante usandoI ate 10 blocks of cheese string
- esto detectaría un error.10
transformará los valores no numéricos, nuestros guiones, en valores NaN (no un número) porque tenemos guiones en lugar de los datos que faltan
Revise el código limpiado y convertido
Veamos cómo lo hicimos. Ejecute el
|_+_|movies['year'] = movies['year'].str.extract('(d+)').astype(int)
función para ver nuestros datos y los tipos de datos que tenemos:El resultado de nuestros datos limpios
El resultado de nuestros tipos de datos
¡Se ve bien!
Código final terminado
Aquí está el código final de su raspador web de una sola página:
|_+_|Guardar sus datos en un CSV
¿De qué sirven nuestros datos extraídos si no podemos guardarlos para proyectos o análisis futuros? A continuación se muestra el código que puede agregar al final de su programa para guardar sus datos en un archivo CSV:
|_+_|Desglosando el archivo CSV:
- |_+_|
Para que este código se ejecute correctamente, deberá crear un archivo vacío y nombrarlo como desee, asegurándose de que tenga la extensión
movies[‘year’]
. Llamé al míoyear
, como puede ver arriba, pero siéntase libre de nombrarlo como quiera. Solo asegúrese de cambiar el código anterior para que coincida.Si está en Repl, puede crear un archivo CSV vacío colocando el cursor cerca de Archivos y haciendo clic en la opción Agregar archivo. Nómbrelo y guárdelo con un
DataFrame
extensión. Luego, agregue el código al final de su programa:|_+_|
Todos sus datos deberían aparecer en su CSV. Una vez que lo descargue en su computadora / lo abra, su archivo se verá así:
Conclusión
Hemos recorrido un largo camino desde solicitar el contenido HTML de nuestra página web hasta limpiar toda nuestra
|_+_|.str.extract(‘(d+’)
. Ahora debería saber cómo extraer páginas web con la misma estructura de URL y HTML que le mostré anteriormente. A continuación, se muestra un resumen de lo que hemos logrado:Próximos pasos
¡Espero que te hayas divertido haciendo esto!
Si desea aprovechar lo que ha aprendido, aquí tiene algunas ideas para probar:
- Obtenga los datos de la película para las 1,000 películas en esa lista
- Extraiga otros datos sobre cada película, por ejemplo, género, director, protagonista o el resumen de la película.
- Encuentra un sitio web diferente para raspar que te interese
En mi próximo artículo, explicaré cómo recorrer todas las páginas de esta lista de IMDb para capturar todas las 1,000 películas, lo que implicará algunas alteraciones en el código final que tenemos aquí.
¡Feliz codificación!
# python # ciencia de datos # aprendizaje automático # programación
.35M', '-', '-', '-', '5.37M', '2.73M', '-', '-', '-', '-', 'Construyendo un Web Scraper con Python en 30 minutos
Mis habilidades en Python son básicas, así que si estás aquí sin muchas habilidades en codificación, espero que esta guía te ayude a adquirir más conocimientos y comprensión.
El proyecto perfecto para principiantes
Para obtener datos para proyectos de ML, IA o ciencia de datos, a menudo dependerá de bases de datos, API o conjuntos de datos CSV listos para usar. Pero, ¿qué pasa si no puede encontrar un conjunto de datos que desee utilizar y analizar? Ahí es donde entra en juego un raspador web.
Trabajar en proyectos es fundamental para consolidar los conocimientos adquiridos. Cuando comencé este proyecto, estaba un poco abrumado porque realmente no sabía nada.
Seguir con él, encontrar respuestas a mis preguntas en Stack Overflow y muchas pruebas y errores me ayudaron a comprender realmente cómo funciona la programación: cómo funcionan las páginas web, cómo usar bucles y cómo crear funciones y mantener los datos limpios. Hace que la construcción de un raspador web sea el proyecto perfecto para principiantes para cualquiera que se esté iniciando en Python.
Que cubriremos
Esta guía lo guiará a través de la comprensión de las páginas web HTML, la creación de un raspador web con Python y la creación de un
DataFrame
con pandas. Cubrirá la calidad de los datos, la limpieza de datos y la conversión de tipos de datos, completamente paso a paso y con instrucciones, código y explicaciones sobre cómo funciona cada pieza. ¡Espero que codifiques y disfrutes!Descargo de responsabilidad
Los sitios web pueden restringir o prohibir la extracción de datos de su sitio web. Los usuarios pueden estar sujetos a ramificaciones legales dependiendo de dónde y cómo intente recopilar información. Los sitios web suelen describir esto en sus términos de uso y en su
robots.txt
archivo encontrado en su sitio, que generalmente se parece a esto:[www.example.com/robots.txt](http://www.[site].com/robots.txt.)
. Así que raspe responsablemente y respete elrobots.txt
.¿Qué es Web Scraping?
Raspado web Consiste en recopilar datos disponibles en sitios web. Esto se puede hacer manualmente por un humano o usando un bot.
A Bot es un programa creado por usted que le ayuda a extraer los datos que necesita mucho más rápido que la mano y los ojos de un ser humano.
¿Qué vamos a raspar?
Es esencial identificar el objetivo de su raspado desde el principio. No queremos extraer ningún dato que en realidad no necesitemos.
Para este proyecto, extraeremos datos de las 1,000 películas principales de IMDb, específicamente las 50 películas principales de esta página. Aquí está la información que recopilaremos de cada lista de películas:
- El título
- El año en que fue lanzado
- Cuanto dura la pelicula
- Calificación de IMDb de la película
- El Metascore de la película
- ¿Cuántos votos obtuvo la película?
- Las ganancias brutas de la película en EE. UU.
¿Cómo funcionan los Web Scrapers?
Los web scrapers recopilan datos del sitio web de la misma manera que lo haría un humano: van a una página web del sitio web, obtienen los datos relevantes y pasan a la siguiente página web, solo que mucho más rápido.
Cada sitio web tiene una estructura diferente. Estas son algunas cosas importantes en las que pensar al crear un raspador web:
- ¿Cuál es la estructura de la página web que contiene los datos que busca?
- ¿Cómo llegamos a esas páginas web?
- ¿Necesitará recopilar más datos de la página siguiente?
La URL
Para empezar, veamos la URL de la página que queremos raspar.
Notamos algunas cosas sobre la URL:
?
actúa como un separador: indica el final de la ruta del recurso URL y el inicio de los parámetrosgroups=top_1000
especifica de qué se tratará la página&ref_adv_prv
nos lleva a la página anterior o siguiente. La referencia es la página en la que estamos actualmente.adv_nxt
yadv_prv
hay dos valores posibles, traducidos a avanzar a la página siguiente y avanzar a la página anterior.
Cuando navega hacia adelante y hacia atrás por las páginas, notará que solo cambian los parámetros. Tenga en cuenta esta estructura, ya que es útil saberlo mientras construimos el raspador.
El HTML
HTML significa Lenguaje de marcado de hipertexto, y la mayoría de las páginas web se escriben con él. Esencialmente, HTML es cómo dos computadoras se comunican entre sí a través de Internet, y los sitios web están qué ellos dicen.
Cuando accede a una URL, su computadora envía una solicitud al servidor que aloja el sitio. Se puede ejecutar cualquier tecnología en ese servidor (JavaScript, Ruby, Java, etc.) para procesar su solicitud. Finalmente, el servidor devuelve una respuesta a su navegador; a menudo, esa respuesta será en forma de una página HTML para que la muestre su navegador.
HTML describe la estructura de una página web de forma semántica y originalmente incluía señales para la apariencia del documento.
Inspeccionar HTML
Los usuarios de Chrome, Firefox y Safari pueden examinar la estructura HTML de cualquier página haciendo clic con el botón derecho del mouse y presionando la opción Inspeccionar.
Aparecerá un menú en la parte inferior o en el lado derecho de su página con una lista larga de todas las etiquetas HTML que contienen la información que se muestra en la ventana de su navegador. Si estás en Safari (foto de arriba), querrás presionar el botón a la izquierda de la barra de búsqueda, que parece un objetivo. Si está en Chrome o Firefox, hay un pequeño cuadro con un ícono de flecha en la parte superior izquierda que usará para inspeccionar.
Una vez que haga clic, si mueve el cursor sobre cualquier elemento de la página, notará que se resaltará junto con las etiquetas HTML en el menú con el que están asociados, como se ve arriba.
Saber cómo leer la estructura básica de la página HTML de una página es importante para que podamos recurrir a Python para que nos ayude a extraer el HTML de la página.
Instrumentos
Las herramientas que vamos a utilizar son:
- Responder (opcional) es un entorno de programación informática interactivo y sencillo que se utiliza a través de su navegador web. Recomiendo usar esto solo para propósitos de código si aún no tiene un IDE. Si usa Repl, asegúrese de estar usando el entorno Python.
- Peticiones nos permitirá enviar solicitudes HTTP para obtener archivos HTML
- Hermosa Sopa nos ayudará a analizar los archivos HTML
- pandas nos ayudará a reunir los datos en un
DataFrame
para limpiarlo y analizarlo - NumPy agregará soporte para funciones matemáticas y herramientas para trabajar con matrices
Ahora, codifiquemos
Puede seguir a continuación dentro de su entorno Repl o IDE, o puede ir directamente a el código completo aquí . ¡Divertirse!
Importar herramientas
Primero, importaremos las herramientas que necesitaremos para que podamos usarlas para ayudarnos a construir el raspador y obtener los datos que necesitamos.
|_+_|Películas en inglés
Es muy probable que cuando ejecutemos nuestro código para extraer algunas de estas películas, obtengamos los nombres de las películas traducidos al idioma principal del país en el que se originó.
Utilice este código para asegurarse de que obtengamos títulos traducidos al inglés de todas las películas que extraemos:
|_+_|Solicitar contenido de la URL
Obtenga el contenido de la página que estamos viendo solicitando la URL:
|_+_|Desglose de las solicitudes de URL:
import requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np
es la variable que creamos y asignamos la URL aheaders = {'Accept-Language': 'en-US, en;q=0.5'}
es la variable que creamos para almacenar nuestrourl = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' results = requests.get(url, headers=headers)
acciónurl
es el método que usamos para capturar el contenido de la URL. Elresults
parte le dice a nuestro raspador que nos traiga inglés, basado en nuestra línea de código anterior.
Usando BeautifulSoup
Haga que el contenido que tomamos sea fácil de leer usando
|_+_|request.get
:Rompiendo BeautifulSoup Down:
requests.get(url, headers=headers)
es la variable que creamos para asignar el métodoheaders
to, que especifica un formato de resultados deseado utilizando el analizador HTML; esto permite que Python lea los componentes de la página en lugar de tratarla como una cadena largaBeautifulSoup
imprimirá lo que hemos tomado en un formato de árbol más estructurado, lo que facilitará su lectura.
Los resultados de la impresión se verán más ordenados, así:
|_+_|Rotura |_+_| abajo:
soup = BeautifulSoup(results.text, 'html.parser') print(soup.prettify())
es la variable que usaremos para almacenar todos lossoup
contenedores con una clase deBeatifulSoup
- el
print(soup.prettify())
El método extrae todos losWe need to figure out what distinguishes each of these from other
div
contenedores que tienen undiv
atributo declass
de lo que hemos almacenado en nuestra sopa variable.
Prepárate para extraer cada artículo
¡Nos están perdiendo los ingresos brutos! Si miras la segunda película, la han incluido allí.
Algo a tener en cuenta siempre al crear un raspador web es la idea de que no toda la información que busca estará disponible para que la recopile.
En estos casos, debemos asegurarnos de que nuestro raspador web no deje de funcionar o se rompa cuando llegue a los datos faltantes y construyamos en torno a la idea de que simplemente no sabemos si eso sucederá o no.
Entrar en cada
lister-item
divCuando tomamos cada uno de los elementos que necesitamos en un solo
|_+_|mode-advanced
lister-item mode-advanced
contenedor, necesitamos que el raspador pase al siguientediv
lister-item mode-advanced
contenedor y coge esos elementos de la película también. Y luego debe pasar a la siguiente y así sucesivamente, 50 veces para cada página. Para que esto se ejecute, tendremos que envolver nuestro raspador en un bucle for.Rompiendo el |_+_| círculo:
- Un bucle
lister-item mode-advanced
se utiliza para iterar sobre una secuencia. Nuestra secuencia es cada contenedormovie_div = soup.find_all('div', class_='lister-item mode-advanced')
div que almacenamos en**find_all**
movie_div
es el nombre de la variable que ingresa cadadiv
. Puede nombrar esto como quiera (lister-item mode-advanced
,find_all()
,div
,class
), y no cambiará la función del bucle.
Se puede leer así:
|_+_|Extrae el título de la película.
Comenzando con el nombre de la película, busquemos su línea HTML correspondiente usando inspeccionar y haciendo clic en el título.
Vemos que el nombre está contenido dentro de una etiqueta de anclaje,
|_+_|lister-item mode-advanced
. Ellister-item mode-advanced
etiqueta. Este `` es el tercero de loslister-item mode-advanced
s anidados en el contenedor de la primera película.Desglosando títulos:
div
es la variable que usaremos para almacenar los datos del título que encontremoslister-item mode-advanced
es lo que se usa en nuestro bucle for; se usa para iterar cada vezdiv
y#initiate the for loop #this tells your scraper to iterate through #every div container we stored in move_div for container in movie_div:
es notación de atributo y le dice al raspador que acceda a cada una de esas etiquetas**for**
le dice al raspador que tome el texto anidado en la etiqueta ``for
le dice al raspador que tome lo que encontramos y almacenamos enlister-item mode-advanced
y agregarlo a nuestra lista vacía llamadamovie_div
, que creamos al principio
Extraer año de lanzamiento
Busquemos el año de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el año.
Vemos que estos datos se almacenan dentro de
container
etiqueta que contiene el título de la película. La notación de puntos, que usamos para encontrar los datos del título (div
), funcionó porque era la primerax
La etiqueta que queremos es la segunda etiqueta, tenemos que usar un método diferente.En cambio, podemos decirle a nuestro raspador que busque por la marca distintiva del segundo ''. Usaremos el
|_+_|loop
método , que es similar abanana
excepto que solo devuelve la primera coincidencia.Desglosando años:
cheese
es la variable que usaremos para almacenar los datos del año que encontremosfor in : OR in our case... for each lister-item mode-advanced div container: scrape these elements
es lo que usamos en nuestro. This tag is nested within a header tag,
bucle: se usa para iterar cada veztag is nested within a
es la notación de atributo, que le dice al raspador que acceda a esa etiquetadiv
es un método que usaremos para acceder a esta `` etiquetaname = container.h3.a.text titles.append(name)
es la etiqueta distintiva que queremosname
le dice al raspador que tome lo que encontramos y almacenamos encontainer
y agregarlo a nuestra lista vacía llamada.h3
(que creamos al principio)
Extraer la duración de la película
Busque la duración de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el total de minutos.
Los datos que necesitamos se pueden encontrar en una etiqueta con una clase de
|_+_|.a
. Como hicimos con el año, podemos hacer algo similar:Rompiendo el tiempo:
.text
es la variable que usaremos para almacenar los datos de tiempo que encontremostitles.append(name)
es lo que usamos en nuestroname
bucle: se usa para iterar cada veztitles
es un método que usaremos para acceder a esta `` etiquetatag below the
es la etiqueta distintiva que queremos.h3.a
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allítag after the `h3` tag. Since the
le dice al raspador que tome ese texto en la etiqueta ``find()
le dice al raspador que tome lo que encontramos y almacenamos enfind_all()
y agregarlo a nuestra lista vacía llamadayear = container.h3.find('span', class_='lister-item-year').text years.append(year)
(que creamos al principio)
Extraer calificaciones de IMDb
Busque la calificación de IMDb de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en la calificación de IMDb.
Ahora, nos centraremos en extraer la calificación de IMDb. Los datos que necesitamos se pueden encontrar en un
|_+_|year
etiqueta. Como no veo ningún otrocontainer
etiquetas, podemos usar la notación de atributos (notación de puntos) para capturar estos datos.Desglosando las calificaciones de IMDb:
for
es la variable que usaremos para almacenar los datos de calificaciones de IMDB que encuentre.h3
es lo que usamos en nuestro.find()
bucle: se usa para iterar cada vez(‘span’, class_ = ‘lister-item-year’)
es una notación de atributo que le dice al raspador que acceda a esa etiquetayears.append(year)
le dice al raspador que tome ese texto- El
year
El método convierte el texto que encontramos en un flotante, que es un decimal years
le dice al raspador que tome lo que encontramos y almacenamos enruntime
y agregarlo a nuestra lista vacía llamadaruntime = container.find('span', class_='runtime').text if container.p.find('span', class_='runtime') else '' time.append(runtime)
(que creamos al principio)
Extraer Metascore
Busque la calificación de Metascore de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el número de Metascore.
Los datos que necesitamos se pueden encontrar en una etiqueta `` que tiene una clase que dice
runtime
.Antes de decidirnos por eso, debe notar que, por supuesto, un 96 para Parasite muestra una calificación favorable, pero ¿son favorables los demás? Si resaltas el Metascore de la siguiente película, verás que JoJo Rabbit tiene una clase que dice
|_+_|container
. Dado que estas etiquetas son diferentes, sería seguro decirle al raspador que use solo la clasefor
al raspar:Desglosando Metascores:
.find()
es la variable que usaremos para almacenar los datos de calificación de Metascore que encuentre(‘span’, class_ = ‘runtime’)
es lo que usamos en nuestroif container.p.find(‘span’, class_=’runtime’) else ‘-’
bucle: se usa para iterar cada vez.text
es un método que usaremos para acceder a esta `` etiquetatime.append(runtime)
es la etiqueta distintiva que queremosruntime
le dice al raspador que tome ese textotime
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allí- El
**
El método convierte el texto que encontramos en un número entero. **
le dice al raspador que tome lo que encontramos y almacenamos enimdb = float(container.strong.text) imdb_ratings.append(imdb)
y agregarlo a nuestra lista vacía llamadaimdb
(que creamos al principio)
Extraer votos e ingresos brutos
Finalmente llegamos a los dos últimos elementos que necesitamos extraer, pero dejamos los más difíciles para el final.
Aquí es donde las cosas se ponen un poco complicadas. Como se mencionó anteriormente, debería haber notado que cuando miramos la primera película de esta lista, no vemos un número de ingresos brutos. Cuando miramos la segunda película de la lista, podemos ver ambas.
Echemos un vistazo al código HTML de la segunda película y partamos de ahí.
Tanto los votos como los brutos están resaltados a la derecha. Después de mirar los votos y los contenedores brutos para la película # 2, ¿qué notas?
Como puede ver, ambos están en una etiqueta `` que tiene un
container
atributo que es igual afor
y un.strong
atributo que contiene los valores del número distintivo que necesitamos para cada uno.¿Cómo podemos obtener los datos del segundo si los parámetros de búsqueda del primero son los mismos? ¿Cómo le decimos a nuestro raspador que se salte el primero y raspe el segundo?
Este requirió mucho esfuerzo mental, toneladas de café y un par de noches para descubrirlo. Así es como lo hice:
|_+_|Rompiendo votos y brutos abajo:
.text
es una variable completamente nueva que usaremos para almacenar tanto los votos como las etiquetas `` brutas ''float()
es lo que usamos en nuestroimdb_ratings.append(imdb)
bucle para iterar cada vezimdb
es el método que usaremos para obtener las dos `` etiquetasimdb_ratings
es cómo podemos tomar los atributos de esa etiqueta específica.metascore favorable
es la variable que usaremos para almacenar los votos que encontremos en elmetascore mixed
etiquetametascore
le dice al raspador que entre enm_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score)
etiqueta y toma los primeros datos de la lista, que son los votos porquem_score
viene primero en nuestro código HTML (las computadoras cuentan en binario; comienzan a contar en 0, no en 1)container
le dice al raspador que tome ese textofor
le dice al raspador que tome lo que encontramos y almacenamos en.find()
y agregarlo a nuestra lista vacía llamada(‘span’, class_ = ‘metascore’)
(que creamos al principio).text
es la variable que usaremos para almacenar el bruto que encontremos enif container.find(‘span’, class_=’metascore’) else ‘-’
etiquetaint()
le dice al raspador que entre enmetascores.append(m_score)
etiqueta y tome el segundo dato de la lista, que esm_score
porquemetascores
ocupa el segundo lugar en nuestro código HTMLname
dice si la longitud denv
es mayor que uno, luego busque el segundo dato almacenado. Pero si los datos almacenados endata-value
no es mayor que uno, es decir, si falta el valor bruto, coloque un guión allínv = container.find_all('span', attrs={'name': 'nv'}) vote = nv[0].text votes.append(vote) grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
le dice al raspador que tome lo que encontramos y almacenamos ennv
y agregarlo a nuestra lista vacía llamadacontainer
(que creamos al principio)
Su código ahora debería verse así:
|_+_|Veamos lo que tenemos hasta ahora
Ahora que le hemos dicho a nuestro raspador qué elementos raspar, usemos el
|_+_|for
función para imprimir cada lista a la que hemos enviado nuestros datos extraídos:Nuestras listas se ven así
|_+_|Hasta ahora todo va bien, pero todavía no hemos llegado a ese punto. Necesitamos limpiar un poco nuestros datos. Parece que tenemos algunos elementos no deseados en nuestros datos: signos de dólar,
find_all()
s,(‘span’, attrs = ‘name’ : ’nv’)
s, comas, paréntesis y espacio en blanco adicional en los Metascores.Construyendo un DataFrame con pandas
El siguiente orden del día es construir un
vote
con pandas para almacenar los datos que tenemos muy bien en una tabla para comprender realmente lo que está sucediendo.Así es como lo hacemos:
|_+_|Desglosando nuestro marco de datos:
nv
es lo que llamaremos nuestronv[0]
nv
así es como inicializamos la creación de unvotes
con pandas- Las claves de la izquierda son los nombres de las columnas.
- Los valores de la derecha son nuestras listas de datos que hemos extraído
Ver nuestro DataFrame
Podemos ver cómo se ve todo simplemente usando el
|_+_|.text
función en nuestrovotes.append(vote)
—Que llamamosvote
—Al final de nuestro programa:Nuestro DataFrame de pandas se ve así
Calidad de los datos
Antes de embarcarse en proyectos como este, debe saber cuáles son sus criterios de calidad de datos, es decir, qué reglas o restricciones deben seguir sus datos. Aquí hay unos ejemplos:
- Restricciones de tipo de datos: Los valores en sus columnas deben ser de un tipo de datos particular: numérico, booleano, fecha, etc.
- Restricciones obligatorias : Algunas columnas no pueden estar vacías
- Patrones de expresión regular: T campos ext que tienen que estar en un patrón determinado, como números de teléfono
¿Qué es la limpieza de datos?
Limpieza de datos es el proceso de detección y corrección o eliminación de registros corruptos o inexactos de su conjunto de datos.
Al realizar un análisis de datos, también es importante asegurarse de que estamos usando los tipos de datos correctos.
Comprobación de tipos de datos
Podemos comprobar cómo se ven nuestros tipos de datos ejecutando esto
|_+_|votes
función en la parte inferior de nuestro programa:Resultados de nuestro tipo de datos
Analicemos esto: nuestro tipo de datos de películas es un objeto, que es lo mismo que una cadena, lo cual sería correcto considerando que son títulos de películas. Nuestra puntuación de IMDb también es correcta porque tenemos números de punto flotante en esta columna (números decimales).
Pero nuestros
grosses
,nv
,nv[1]
ynv
muestran que son objetos cuando deberían ser tipos de datos enteros, y nuestrogross
es un objeto en lugar de ungross
tipo de datos. ¿Cómo pasó esto?Inicialmente, cuando le decíamos a nuestro raspador que tomara estos valores de cada contenedor HTML, le decíamos que tomara valores específicos de una cadena. Una cadena representa texto en lugar de números; se compone de un conjunto de caracteres que pueden además contienen números.
Por ejemplo, la palabra
nv[1].text if len(nv) > 1 else ‘-’
y la frasenv
son ambas cadenas. Si tuviéramos que deshacernos de todo excepto denv
deus_gross.append(grosses)
, sigue siendo una cadena, pero ahora es una que solo dicegrosses
.Limpieza de datos con pandas
Ahora que tenemos una idea clara de cómo se ven nuestros datos en este momento, es hora de comenzar a limpiarlos.
Esta puede ser una tarea tediosa, pero es muy importante.
Datos del año de limpieza
Para eliminar los paréntesis de nuestros datos anuales y convertir el objeto en un tipo de datos enteros, haremos lo siguiente:
|_+_|Desglose de los datos del año de limpieza:
us_grosses
le dice a los pandas que vayan a la columnaimport requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np url = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' headers = {'Accept-Language': 'en-US, en;q=0.5'} results = requests.get(url, headers=headers) soup = BeautifulSoup(results.text, 'html.parser') titles = [] years = [] time = [] imdb_ratings = [] metascores = [] votes = [] us_gross = [] movie_div = soup.find_all('div', class_='lister-item mode-advanced') for container in movie_div: #Name name = container.h3.a.text titles.append(name) #year year = container.h3.find('span', class_='lister-item-year').text years.append(year) #time runtime = container.p.find('span', class_='runtime').text if container.p.find('span', class_='runtime').text else '-' time.append(runtime) #IMDb rating imdb = float(container.strong.text) imdb_ratings.append(imdb) #metascore m_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score) #here are two NV containers, grab both of them as they hold both the votes and the grosses nv = container.find_all('span', attrs={'name': 'nv'}) #filter nv for votes vote = nv[0].text votes.append(vote) #filter nv for gross grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
en nuestroprint
print(titles) print(years) print(time) print(imdb_ratings) print(metascores) print(votes) print(us_gross)
este método:['Parasite', 'Jojo Rabbit', '1917', 'Knives Out', 'Uncut Gems', 'Once Upon a Time... in Hollywood', 'Joker', 'The Gentlemen', 'Ford v Ferrari', 'Little Women', 'The Irishman', 'The Lighthouse', 'Toy Story 4', 'Marriage Story', 'Avengers: Endgame', 'The Godfather', 'Blade Runner 2049', 'The Shawshank Redemption', 'The Dark Knight', 'Inglourious Basterds', 'Call Me by Your Name', 'The Two Popes', 'Pulp Fiction', 'Inception', 'Interstellar', 'Green Book', 'Blade Runner', 'The Wolf of Wall Street', 'Gone Girl', 'The Shining', 'The Matrix', 'Titanic', 'The Silence of the Lambs', 'Three Billboards Outside Ebbing, Missouri', 'Harry Potter and the Sorcerer's Stone', 'The Peanut Butter Falcon', 'The Handmaiden', 'Memories of Murder', 'The Lord of the Rings: The Fellowship of the Ring', 'Gladiator', 'The Martian', 'Bohemian Rhapsody', 'Watchmen', 'Forrest Gump', 'Thor: Ragnarok', 'Casino Royale', 'The Breakfast Club', 'The Godfather: Part II', 'Django Unchained', 'Baby Driver'] ['(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(I) (2019)', '(2019)', '(2019)', '(2019)', '(1972)', '(2017)', '(1994)', '(2008)', '(2009)', '(2017)', '(2019)', '(1994)', '(2010)', '(2014)', '(2018)', '(1982)', '(2013)', '(2014)', '(1980)', '(1999)', '(1997)', '(1991)', '(2017)', '(2001)', '(2019)', '(2016)', '(2003)', '(2001)', '(2000)', '(2015)', '(2018)', '(2009)', '(1994)', '(2017)', '(2006)', '(1985)', '(1974)', '(2012)', '(2017)'] ['132 min', '108 min', '119 min', '131 min', '135 min', '161 min', '122 min', '113 min', '152 min', '135 min', '209 min', '109 min', '100 min', '137 min', '181 min', '175 min', '164 min', '142 min', '152 min', '153 min', '132 min', '125 min', '154 min', '148 min', '169 min', '130 min', '117min', '180 min', '149 min', '146 min', '136 min', '194 min', '118 min', '115 min', '152 min', '97 min', '145 min', '132 min', '178 min', '155 min', '144 min', '134 min', '162 min', '142 min', '130 min', '144 min', '97 min', '202 min', '165 min', '113 min'] [8.6, 8.0, 8.5, 8.0, 7.6, 7.7, 8.6, 8.1, 8.2, 8.0, 8.0, 7.7, 7.8, 8.0, 8.5, 9.2, 8.0, 9.3, 9.0, 8.3, 7.9, 7.6, 8.9, 8.8, 8.6, 8.2, 8.1, 8.2, 8.1,8.4, 8.7, 7.8, 8.6, 8.2, 7.6, 7.7, 8.1, 8.1, 8.8, 8.5, 8.0, 8.0, 7.6, 8.8, 7.9, 8.0, 7.9, 9.0, 8.4, 7.6] ['96 ', '58 ', '78 ', '82 ', '90 ', '83 ', '59 ', '51 ', '81 ', '91 ', '94 ', '83 ', '84 ', '93 ', '78 ', '100 ', '81 ', '80 ', '84 ', '69 ', '93 ', '75 ', '94 ', '74 ', '74 ', '69 ', '84 ', '75 ', '79 ', '66 ', '73 ', '75 ', '85 ', '88 ', '64 ', '70 ', '84 ', '82 ', '92 ', '67 ', '80 ', '49 ', '56 ', '82 ', '74 ', '80 ', '62 ', '90 ', '81 ', '86 '] ['282,699', '142,517', '199,638', '195,728', '108,330', '396,071', '695,224', '42,015', '152,661', '65,234', '249,950', '77,453', '160,180', '179,887', '673,115', '1,511,929', '414,992', '2,194,397', '2,176,865', '1,184,882', '178,688', '76,291', '1,724,518', '1,925,684', '1,378,968', '293,695', '656,442', '1,092,063', '799,696', '835,496', '1,580,250', '994,453', '1,191,182', '383,958', '595,613', '34,091', '92,492', '115,125', '1,572,354', '1,267,310', '715,623', '410,199', '479,811', '1,693,344', '535,065', '555,756', '330,308', '1,059,089', '1,271,569', '398,553'] ['-', '$0.35M', '-', '-', '-', '$135.37M', '$192.73M', '-', '-', '-', '-', '$0.43M', '$433.03M', '-', '$858.37M', '$134.97M', '$92.05M', '$28.34M', '$534.86M', '$120.54M', '$18.10M', '-', '$107.93M', '$292.58M', '$188.02M', '$85.08M', '$32.87M', '$116.90M', '$167.77M', '$44.02M', '$171.48M', '$659.33M', '$130.74M', '$54.51M', '$317.58M', '$13.12M', '$2.01M', '$0.01M', '$315.54M', '$187.71M', '$228.43M', '$216.43M', '$107.51M', '$330.25M', '$315.06M', '$167.45M', '$45.88M', '$57.30M', '$162.81M', '$107.83M']
dice que extraiga todos los dígitos de la cadena- El
M
El método convierte el resultado en un número entero.
Ahora, si ejecutamos
min
en la parte inferior de nuestro programa para ver cómo se ven nuestros datos anuales, este es el resultado:Debería ver su lista de años sin paréntesis. Y el tipo de datos que se muestra ahora es un número entero. Nuestros datos anuales se limpian oficialmente.
Datos de tiempo de limpieza
Haremos exactamente lo que hicimos al limpiar nuestros datos de años anteriores a nuestros datos de tiempo al tomar solo los dígitos y convertir nuestro tipo de datos en un número entero.
|_+_|Limpieza de datos de Metascore
La única limpieza que debemos hacer aquí es convertir nuestro tipo de datos de objeto en un número entero:
|_+_|Limpieza de votos
Con los votos, necesitamos eliminar las comas y convertirlo en un tipo de datos entero:
|_+_|Rompiendo los votos de limpieza en contra:
DataFrame
son nuestros datos de votos en nuestras películasmovies = pd.DataFrame({ 'movie': titles, 'year': years, 'timeMin': time, 'imdb': imdb_ratings, 'metascore': metascores, 'votes': votes, 'us_grossMillions': us_gross, })
. Estamos asignando nuestros nuevos datos limpiados a nuestros votosmovies
.- .
DataFrame
agarra la cadena y usa elpd.DataFrame
método para reemplazar las comas con una cita vacía (nada) - El
DataFrame
El método convierte el resultado en un número entero.
Limpieza de datos brutos
Los datos brutos implican algunos obstáculos que superar. Lo que tenemos que hacer es eliminar el signo de dólar y el
|_+_|print
s de los datos y convertirlo en un número de punto flotante. A continuación, le indicamos cómo hacerlo:Desglose de la limpieza en bruto:
Código de limpieza superior:
DataFrame
son nuestros datos brutos en nuestras películasmovies
. Asignaremos nuestros nuevos datos limpiados a nuestroprint(movies)
columna.print
le dice a los pandas que vayan a la columnaprint(movies.dtypes)
en nuestroyear
- El
timeMin
la función llama a la función especificada para cada elemento de un iterable metascore
es una función anónima en Python (una sin nombre). Las funciones normales se definen mediantevotes
palabra clave.us_grossMillions
son nuestros argumentos de función. Esto le dice a nuestra función que elimine elfloat
del lado izquierdo y pele elcheese
desde el lado derecho.
Código de conversión inferior:
I ate 10 blocks of cheese
está despojado de los elementos que no necesitamos, y ahora le asignaremos los datos del código de conversión para terminarlo10
es un método que podemos usar para cambiar esta columna a un flotante. La razón por la que usamos esto es porque tenemos muchos guiones en esta columna, y no podemos convertirla en un flotante usandoI ate 10 blocks of cheese string
- esto detectaría un error.10
transformará los valores no numéricos, nuestros guiones, en valores NaN (no un número) porque tenemos guiones en lugar de los datos que faltan
Revise el código limpiado y convertido
Veamos cómo lo hicimos. Ejecute el
|_+_|movies['year'] = movies['year'].str.extract('(d+)').astype(int)
función para ver nuestros datos y los tipos de datos que tenemos:El resultado de nuestros datos limpios
El resultado de nuestros tipos de datos
¡Se ve bien!
Código final terminado
Aquí está el código final de su raspador web de una sola página:
|_+_|Guardar sus datos en un CSV
¿De qué sirven nuestros datos extraídos si no podemos guardarlos para proyectos o análisis futuros? A continuación se muestra el código que puede agregar al final de su programa para guardar sus datos en un archivo CSV:
|_+_|Desglosando el archivo CSV:
- |_+_|
Para que este código se ejecute correctamente, deberá crear un archivo vacío y nombrarlo como desee, asegurándose de que tenga la extensión
movies[‘year’]
. Llamé al míoyear
, como puede ver arriba, pero siéntase libre de nombrarlo como quiera. Solo asegúrese de cambiar el código anterior para que coincida.Si está en Repl, puede crear un archivo CSV vacío colocando el cursor cerca de Archivos y haciendo clic en la opción Agregar archivo. Nómbrelo y guárdelo con un
DataFrame
extensión. Luego, agregue el código al final de su programa:|_+_|
Todos sus datos deberían aparecer en su CSV. Una vez que lo descargue en su computadora / lo abra, su archivo se verá así:
Conclusión
Hemos recorrido un largo camino desde solicitar el contenido HTML de nuestra página web hasta limpiar toda nuestra
|_+_|.str.extract(‘(d+’)
. Ahora debería saber cómo extraer páginas web con la misma estructura de URL y HTML que le mostré anteriormente. A continuación, se muestra un resumen de lo que hemos logrado:Próximos pasos
¡Espero que te hayas divertido haciendo esto!
Si desea aprovechar lo que ha aprendido, aquí tiene algunas ideas para probar:
- Obtenga los datos de la película para las 1,000 películas en esa lista
- Extraiga otros datos sobre cada película, por ejemplo, género, director, protagonista o el resumen de la película.
- Encuentra un sitio web diferente para raspar que te interese
En mi próximo artículo, explicaré cómo recorrer todas las páginas de esta lista de IMDb para capturar todas las 1,000 películas, lo que implicará algunas alteraciones en el código final que tenemos aquí.
¡Feliz codificación!
# python # ciencia de datos # aprendizaje automático # programación
.43M', '3.03M', '-', '8.37M', '4.97M', '.05M', '.34M', '4.86M', '0.54M', '.10M', '-', '7.93M', '2.58M', '8.02M', '.08M', '.87M', '6.90M', '7.77M', '.02M', '1.48M', '9.33M', '0.74M', '.51M', '7.58M', '.12M', '.01M', 'Construyendo un Web Scraper con Python en 30 minutos
Mis habilidades en Python son básicas, así que si estás aquí sin muchas habilidades en codificación, espero que esta guía te ayude a adquirir más conocimientos y comprensión.
El proyecto perfecto para principiantes
Para obtener datos para proyectos de ML, IA o ciencia de datos, a menudo dependerá de bases de datos, API o conjuntos de datos CSV listos para usar. Pero, ¿qué pasa si no puede encontrar un conjunto de datos que desee utilizar y analizar? Ahí es donde entra en juego un raspador web.
Trabajar en proyectos es fundamental para consolidar los conocimientos adquiridos. Cuando comencé este proyecto, estaba un poco abrumado porque realmente no sabía nada.
Seguir con él, encontrar respuestas a mis preguntas en Stack Overflow y muchas pruebas y errores me ayudaron a comprender realmente cómo funciona la programación: cómo funcionan las páginas web, cómo usar bucles y cómo crear funciones y mantener los datos limpios. Hace que la construcción de un raspador web sea el proyecto perfecto para principiantes para cualquiera que se esté iniciando en Python.
Que cubriremos
Esta guía lo guiará a través de la comprensión de las páginas web HTML, la creación de un raspador web con Python y la creación de un
DataFrame
con pandas. Cubrirá la calidad de los datos, la limpieza de datos y la conversión de tipos de datos, completamente paso a paso y con instrucciones, código y explicaciones sobre cómo funciona cada pieza. ¡Espero que codifiques y disfrutes!Descargo de responsabilidad
Los sitios web pueden restringir o prohibir la extracción de datos de su sitio web. Los usuarios pueden estar sujetos a ramificaciones legales dependiendo de dónde y cómo intente recopilar información. Los sitios web suelen describir esto en sus términos de uso y en su
robots.txt
archivo encontrado en su sitio, que generalmente se parece a esto:[www.example.com/robots.txt](http://www.[site].com/robots.txt.)
. Así que raspe responsablemente y respete elrobots.txt
.¿Qué es Web Scraping?
Raspado web Consiste en recopilar datos disponibles en sitios web. Esto se puede hacer manualmente por un humano o usando un bot.
A Bot es un programa creado por usted que le ayuda a extraer los datos que necesita mucho más rápido que la mano y los ojos de un ser humano.
¿Qué vamos a raspar?
Es esencial identificar el objetivo de su raspado desde el principio. No queremos extraer ningún dato que en realidad no necesitemos.
Para este proyecto, extraeremos datos de las 1,000 películas principales de IMDb, específicamente las 50 películas principales de esta página. Aquí está la información que recopilaremos de cada lista de películas:
- El título
- El año en que fue lanzado
- Cuanto dura la pelicula
- Calificación de IMDb de la película
- El Metascore de la película
- ¿Cuántos votos obtuvo la película?
- Las ganancias brutas de la película en EE. UU.
¿Cómo funcionan los Web Scrapers?
Los web scrapers recopilan datos del sitio web de la misma manera que lo haría un humano: van a una página web del sitio web, obtienen los datos relevantes y pasan a la siguiente página web, solo que mucho más rápido.
Cada sitio web tiene una estructura diferente. Estas son algunas cosas importantes en las que pensar al crear un raspador web:
- ¿Cuál es la estructura de la página web que contiene los datos que busca?
- ¿Cómo llegamos a esas páginas web?
- ¿Necesitará recopilar más datos de la página siguiente?
La URL
Para empezar, veamos la URL de la página que queremos raspar.
Notamos algunas cosas sobre la URL:
?
actúa como un separador: indica el final de la ruta del recurso URL y el inicio de los parámetrosgroups=top_1000
especifica de qué se tratará la página&ref_adv_prv
nos lleva a la página anterior o siguiente. La referencia es la página en la que estamos actualmente.adv_nxt
yadv_prv
hay dos valores posibles, traducidos a avanzar a la página siguiente y avanzar a la página anterior.
Cuando navega hacia adelante y hacia atrás por las páginas, notará que solo cambian los parámetros. Tenga en cuenta esta estructura, ya que es útil saberlo mientras construimos el raspador.
El HTML
HTML significa Lenguaje de marcado de hipertexto, y la mayoría de las páginas web se escriben con él. Esencialmente, HTML es cómo dos computadoras se comunican entre sí a través de Internet, y los sitios web están qué ellos dicen.
Cuando accede a una URL, su computadora envía una solicitud al servidor que aloja el sitio. Se puede ejecutar cualquier tecnología en ese servidor (JavaScript, Ruby, Java, etc.) para procesar su solicitud. Finalmente, el servidor devuelve una respuesta a su navegador; a menudo, esa respuesta será en forma de una página HTML para que la muestre su navegador.
HTML describe la estructura de una página web de forma semántica y originalmente incluía señales para la apariencia del documento.
Inspeccionar HTML
Los usuarios de Chrome, Firefox y Safari pueden examinar la estructura HTML de cualquier página haciendo clic con el botón derecho del mouse y presionando la opción Inspeccionar.
Aparecerá un menú en la parte inferior o en el lado derecho de su página con una lista larga de todas las etiquetas HTML que contienen la información que se muestra en la ventana de su navegador. Si estás en Safari (foto de arriba), querrás presionar el botón a la izquierda de la barra de búsqueda, que parece un objetivo. Si está en Chrome o Firefox, hay un pequeño cuadro con un ícono de flecha en la parte superior izquierda que usará para inspeccionar.
Una vez que haga clic, si mueve el cursor sobre cualquier elemento de la página, notará que se resaltará junto con las etiquetas HTML en el menú con el que están asociados, como se ve arriba.
Saber cómo leer la estructura básica de la página HTML de una página es importante para que podamos recurrir a Python para que nos ayude a extraer el HTML de la página.
Instrumentos
Las herramientas que vamos a utilizar son:
- Responder (opcional) es un entorno de programación informática interactivo y sencillo que se utiliza a través de su navegador web. Recomiendo usar esto solo para propósitos de código si aún no tiene un IDE. Si usa Repl, asegúrese de estar usando el entorno Python.
- Peticiones nos permitirá enviar solicitudes HTTP para obtener archivos HTML
- Hermosa Sopa nos ayudará a analizar los archivos HTML
- pandas nos ayudará a reunir los datos en un
DataFrame
para limpiarlo y analizarlo - NumPy agregará soporte para funciones matemáticas y herramientas para trabajar con matrices
Ahora, codifiquemos
Puede seguir a continuación dentro de su entorno Repl o IDE, o puede ir directamente a el código completo aquí . ¡Divertirse!
Importar herramientas
Primero, importaremos las herramientas que necesitaremos para que podamos usarlas para ayudarnos a construir el raspador y obtener los datos que necesitamos.
|_+_|Películas en inglés
Es muy probable que cuando ejecutemos nuestro código para extraer algunas de estas películas, obtengamos los nombres de las películas traducidos al idioma principal del país en el que se originó.
Utilice este código para asegurarse de que obtengamos títulos traducidos al inglés de todas las películas que extraemos:
|_+_|Solicitar contenido de la URL
Obtenga el contenido de la página que estamos viendo solicitando la URL:
|_+_|Desglose de las solicitudes de URL:
import requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np
es la variable que creamos y asignamos la URL aheaders = {'Accept-Language': 'en-US, en;q=0.5'}
es la variable que creamos para almacenar nuestrourl = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' results = requests.get(url, headers=headers)
acciónurl
es el método que usamos para capturar el contenido de la URL. Elresults
parte le dice a nuestro raspador que nos traiga inglés, basado en nuestra línea de código anterior.
Usando BeautifulSoup
Haga que el contenido que tomamos sea fácil de leer usando
|_+_|request.get
:Rompiendo BeautifulSoup Down:
requests.get(url, headers=headers)
es la variable que creamos para asignar el métodoheaders
to, que especifica un formato de resultados deseado utilizando el analizador HTML; esto permite que Python lea los componentes de la página en lugar de tratarla como una cadena largaBeautifulSoup
imprimirá lo que hemos tomado en un formato de árbol más estructurado, lo que facilitará su lectura.
Los resultados de la impresión se verán más ordenados, así:
|_+_|Rotura |_+_| abajo:
soup = BeautifulSoup(results.text, 'html.parser') print(soup.prettify())
es la variable que usaremos para almacenar todos lossoup
contenedores con una clase deBeatifulSoup
- el
print(soup.prettify())
El método extrae todos losWe need to figure out what distinguishes each of these from other
div
contenedores que tienen undiv
atributo declass
de lo que hemos almacenado en nuestra sopa variable.
Prepárate para extraer cada artículo
¡Nos están perdiendo los ingresos brutos! Si miras la segunda película, la han incluido allí.
Algo a tener en cuenta siempre al crear un raspador web es la idea de que no toda la información que busca estará disponible para que la recopile.
En estos casos, debemos asegurarnos de que nuestro raspador web no deje de funcionar o se rompa cuando llegue a los datos faltantes y construyamos en torno a la idea de que simplemente no sabemos si eso sucederá o no.
Entrar en cada
lister-item
divCuando tomamos cada uno de los elementos que necesitamos en un solo
|_+_|mode-advanced
lister-item mode-advanced
contenedor, necesitamos que el raspador pase al siguientediv
lister-item mode-advanced
contenedor y coge esos elementos de la película también. Y luego debe pasar a la siguiente y así sucesivamente, 50 veces para cada página. Para que esto se ejecute, tendremos que envolver nuestro raspador en un bucle for.Rompiendo el |_+_| círculo:
- Un bucle
lister-item mode-advanced
se utiliza para iterar sobre una secuencia. Nuestra secuencia es cada contenedormovie_div = soup.find_all('div', class_='lister-item mode-advanced')
div que almacenamos en**find_all**
movie_div
es el nombre de la variable que ingresa cadadiv
. Puede nombrar esto como quiera (lister-item mode-advanced
,find_all()
,div
,class
), y no cambiará la función del bucle.
Se puede leer así:
|_+_|Extrae el título de la película.
Comenzando con el nombre de la película, busquemos su línea HTML correspondiente usando inspeccionar y haciendo clic en el título.
Vemos que el nombre está contenido dentro de una etiqueta de anclaje,
|_+_|lister-item mode-advanced
. Ellister-item mode-advanced
etiqueta. Este `` es el tercero de loslister-item mode-advanced
s anidados en el contenedor de la primera película.Desglosando títulos:
div
es la variable que usaremos para almacenar los datos del título que encontremoslister-item mode-advanced
es lo que se usa en nuestro bucle for; se usa para iterar cada vezdiv
y#initiate the for loop #this tells your scraper to iterate through #every div container we stored in move_div for container in movie_div:
es notación de atributo y le dice al raspador que acceda a cada una de esas etiquetas**for**
le dice al raspador que tome el texto anidado en la etiqueta ``for
le dice al raspador que tome lo que encontramos y almacenamos enlister-item mode-advanced
y agregarlo a nuestra lista vacía llamadamovie_div
, que creamos al principio
Extraer año de lanzamiento
Busquemos el año de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el año.
Vemos que estos datos se almacenan dentro de
container
etiqueta que contiene el título de la película. La notación de puntos, que usamos para encontrar los datos del título (div
), funcionó porque era la primerax
La etiqueta que queremos es la segunda etiqueta, tenemos que usar un método diferente.En cambio, podemos decirle a nuestro raspador que busque por la marca distintiva del segundo ''. Usaremos el
|_+_|loop
método , que es similar abanana
excepto que solo devuelve la primera coincidencia.Desglosando años:
cheese
es la variable que usaremos para almacenar los datos del año que encontremosfor in : OR in our case... for each lister-item mode-advanced div container: scrape these elements
es lo que usamos en nuestro. This tag is nested within a header tag,
bucle: se usa para iterar cada veztag is nested within a
es la notación de atributo, que le dice al raspador que acceda a esa etiquetadiv
es un método que usaremos para acceder a esta `` etiquetaname = container.h3.a.text titles.append(name)
es la etiqueta distintiva que queremosname
le dice al raspador que tome lo que encontramos y almacenamos encontainer
y agregarlo a nuestra lista vacía llamada.h3
(que creamos al principio)
Extraer la duración de la película
Busque la duración de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el total de minutos.
Los datos que necesitamos se pueden encontrar en una etiqueta con una clase de
|_+_|.a
. Como hicimos con el año, podemos hacer algo similar:Rompiendo el tiempo:
.text
es la variable que usaremos para almacenar los datos de tiempo que encontremostitles.append(name)
es lo que usamos en nuestroname
bucle: se usa para iterar cada veztitles
es un método que usaremos para acceder a esta `` etiquetatag below the
es la etiqueta distintiva que queremos.h3.a
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allítag after the `h3` tag. Since the
le dice al raspador que tome ese texto en la etiqueta ``find()
le dice al raspador que tome lo que encontramos y almacenamos enfind_all()
y agregarlo a nuestra lista vacía llamadayear = container.h3.find('span', class_='lister-item-year').text years.append(year)
(que creamos al principio)
Extraer calificaciones de IMDb
Busque la calificación de IMDb de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en la calificación de IMDb.
Ahora, nos centraremos en extraer la calificación de IMDb. Los datos que necesitamos se pueden encontrar en un
|_+_|year
etiqueta. Como no veo ningún otrocontainer
etiquetas, podemos usar la notación de atributos (notación de puntos) para capturar estos datos.Desglosando las calificaciones de IMDb:
for
es la variable que usaremos para almacenar los datos de calificaciones de IMDB que encuentre.h3
es lo que usamos en nuestro.find()
bucle: se usa para iterar cada vez(‘span’, class_ = ‘lister-item-year’)
es una notación de atributo que le dice al raspador que acceda a esa etiquetayears.append(year)
le dice al raspador que tome ese texto- El
year
El método convierte el texto que encontramos en un flotante, que es un decimal years
le dice al raspador que tome lo que encontramos y almacenamos enruntime
y agregarlo a nuestra lista vacía llamadaruntime = container.find('span', class_='runtime').text if container.p.find('span', class_='runtime') else '' time.append(runtime)
(que creamos al principio)
Extraer Metascore
Busque la calificación de Metascore de la película y su línea HTML correspondiente utilizando inspeccionar y haciendo clic en el número de Metascore.
Los datos que necesitamos se pueden encontrar en una etiqueta `` que tiene una clase que dice
runtime
.Antes de decidirnos por eso, debe notar que, por supuesto, un 96 para Parasite muestra una calificación favorable, pero ¿son favorables los demás? Si resaltas el Metascore de la siguiente película, verás que JoJo Rabbit tiene una clase que dice
|_+_|container
. Dado que estas etiquetas son diferentes, sería seguro decirle al raspador que use solo la clasefor
al raspar:Desglosando Metascores:
.find()
es la variable que usaremos para almacenar los datos de calificación de Metascore que encuentre(‘span’, class_ = ‘runtime’)
es lo que usamos en nuestroif container.p.find(‘span’, class_=’runtime’) else ‘-’
bucle: se usa para iterar cada vez.text
es un método que usaremos para acceder a esta `` etiquetatime.append(runtime)
es la etiqueta distintiva que queremosruntime
le dice al raspador que tome ese textotime
dice que si hay datos allí, consígalos, pero si faltan datos, coloque un guión allí- El
**
El método convierte el texto que encontramos en un número entero. **
le dice al raspador que tome lo que encontramos y almacenamos enimdb = float(container.strong.text) imdb_ratings.append(imdb)
y agregarlo a nuestra lista vacía llamadaimdb
(que creamos al principio)
Extraer votos e ingresos brutos
Finalmente llegamos a los dos últimos elementos que necesitamos extraer, pero dejamos los más difíciles para el final.
Aquí es donde las cosas se ponen un poco complicadas. Como se mencionó anteriormente, debería haber notado que cuando miramos la primera película de esta lista, no vemos un número de ingresos brutos. Cuando miramos la segunda película de la lista, podemos ver ambas.
Echemos un vistazo al código HTML de la segunda película y partamos de ahí.
Tanto los votos como los brutos están resaltados a la derecha. Después de mirar los votos y los contenedores brutos para la película # 2, ¿qué notas?
Como puede ver, ambos están en una etiqueta `` que tiene un
container
atributo que es igual afor
y un.strong
atributo que contiene los valores del número distintivo que necesitamos para cada uno.¿Cómo podemos obtener los datos del segundo si los parámetros de búsqueda del primero son los mismos? ¿Cómo le decimos a nuestro raspador que se salte el primero y raspe el segundo?
Este requirió mucho esfuerzo mental, toneladas de café y un par de noches para descubrirlo. Así es como lo hice:
|_+_|Rompiendo votos y brutos abajo:
.text
es una variable completamente nueva que usaremos para almacenar tanto los votos como las etiquetas `` brutas ''float()
es lo que usamos en nuestroimdb_ratings.append(imdb)
bucle para iterar cada vezimdb
es el método que usaremos para obtener las dos `` etiquetasimdb_ratings
es cómo podemos tomar los atributos de esa etiqueta específica.metascore favorable
es la variable que usaremos para almacenar los votos que encontremos en elmetascore mixed
etiquetametascore
le dice al raspador que entre enm_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score)
etiqueta y toma los primeros datos de la lista, que son los votos porquem_score
viene primero en nuestro código HTML (las computadoras cuentan en binario; comienzan a contar en 0, no en 1)container
le dice al raspador que tome ese textofor
le dice al raspador que tome lo que encontramos y almacenamos en.find()
y agregarlo a nuestra lista vacía llamada(‘span’, class_ = ‘metascore’)
(que creamos al principio).text
es la variable que usaremos para almacenar el bruto que encontremos enif container.find(‘span’, class_=’metascore’) else ‘-’
etiquetaint()
le dice al raspador que entre enmetascores.append(m_score)
etiqueta y tome el segundo dato de la lista, que esm_score
porquemetascores
ocupa el segundo lugar en nuestro código HTMLname
dice si la longitud denv
es mayor que uno, luego busque el segundo dato almacenado. Pero si los datos almacenados endata-value
no es mayor que uno, es decir, si falta el valor bruto, coloque un guión allínv = container.find_all('span', attrs={'name': 'nv'}) vote = nv[0].text votes.append(vote) grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
le dice al raspador que tome lo que encontramos y almacenamos ennv
y agregarlo a nuestra lista vacía llamadacontainer
(que creamos al principio)
Su código ahora debería verse así:
|_+_|Veamos lo que tenemos hasta ahora
Ahora que le hemos dicho a nuestro raspador qué elementos raspar, usemos el
|_+_|for
función para imprimir cada lista a la que hemos enviado nuestros datos extraídos:Nuestras listas se ven así
|_+_|Hasta ahora todo va bien, pero todavía no hemos llegado a ese punto. Necesitamos limpiar un poco nuestros datos. Parece que tenemos algunos elementos no deseados en nuestros datos: signos de dólar,
find_all()
s,(‘span’, attrs = ‘name’ : ’nv’)
s, comas, paréntesis y espacio en blanco adicional en los Metascores.Construyendo un DataFrame con pandas
El siguiente orden del día es construir un
vote
con pandas para almacenar los datos que tenemos muy bien en una tabla para comprender realmente lo que está sucediendo.Así es como lo hacemos:
|_+_|Desglosando nuestro marco de datos:
nv
es lo que llamaremos nuestronv[0]
nv
así es como inicializamos la creación de unvotes
con pandas- Las claves de la izquierda son los nombres de las columnas.
- Los valores de la derecha son nuestras listas de datos que hemos extraído
Ver nuestro DataFrame
Podemos ver cómo se ve todo simplemente usando el
|_+_|.text
función en nuestrovotes.append(vote)
—Que llamamosvote
—Al final de nuestro programa:Nuestro DataFrame de pandas se ve así
Calidad de los datos
Antes de embarcarse en proyectos como este, debe saber cuáles son sus criterios de calidad de datos, es decir, qué reglas o restricciones deben seguir sus datos. Aquí hay unos ejemplos:
- Restricciones de tipo de datos: Los valores en sus columnas deben ser de un tipo de datos particular: numérico, booleano, fecha, etc.
- Restricciones obligatorias : Algunas columnas no pueden estar vacías
- Patrones de expresión regular: T campos ext que tienen que estar en un patrón determinado, como números de teléfono
¿Qué es la limpieza de datos?
Limpieza de datos es el proceso de detección y corrección o eliminación de registros corruptos o inexactos de su conjunto de datos.
Al realizar un análisis de datos, también es importante asegurarse de que estamos usando los tipos de datos correctos.
Comprobación de tipos de datos
Podemos comprobar cómo se ven nuestros tipos de datos ejecutando esto
|_+_|votes
función en la parte inferior de nuestro programa:Resultados de nuestro tipo de datos
Analicemos esto: nuestro tipo de datos de películas es un objeto, que es lo mismo que una cadena, lo cual sería correcto considerando que son títulos de películas. Nuestra puntuación de IMDb también es correcta porque tenemos números de punto flotante en esta columna (números decimales).
Pero nuestros
grosses
,nv
,nv[1]
ynv
muestran que son objetos cuando deberían ser tipos de datos enteros, y nuestrogross
es un objeto en lugar de ungross
tipo de datos. ¿Cómo pasó esto?Inicialmente, cuando le decíamos a nuestro raspador que tomara estos valores de cada contenedor HTML, le decíamos que tomara valores específicos de una cadena. Una cadena representa texto en lugar de números; se compone de un conjunto de caracteres que pueden además contienen números.
Por ejemplo, la palabra
nv[1].text if len(nv) > 1 else ‘-’
y la frasenv
son ambas cadenas. Si tuviéramos que deshacernos de todo excepto denv
deus_gross.append(grosses)
, sigue siendo una cadena, pero ahora es una que solo dicegrosses
.Limpieza de datos con pandas
Ahora que tenemos una idea clara de cómo se ven nuestros datos en este momento, es hora de comenzar a limpiarlos.
Esta puede ser una tarea tediosa, pero es muy importante.
Datos del año de limpieza
Para eliminar los paréntesis de nuestros datos anuales y convertir el objeto en un tipo de datos enteros, haremos lo siguiente:
|_+_|Desglose de los datos del año de limpieza:
us_grosses
le dice a los pandas que vayan a la columnaimport requests from requests import get from bs4 import BeautifulSoup import pandas as pd import numpy as np url = 'https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv' headers = {'Accept-Language': 'en-US, en;q=0.5'} results = requests.get(url, headers=headers) soup = BeautifulSoup(results.text, 'html.parser') titles = [] years = [] time = [] imdb_ratings = [] metascores = [] votes = [] us_gross = [] movie_div = soup.find_all('div', class_='lister-item mode-advanced') for container in movie_div: #Name name = container.h3.a.text titles.append(name) #year year = container.h3.find('span', class_='lister-item-year').text years.append(year) #time runtime = container.p.find('span', class_='runtime').text if container.p.find('span', class_='runtime').text else '-' time.append(runtime) #IMDb rating imdb = float(container.strong.text) imdb_ratings.append(imdb) #metascore m_score = container.find('span', class_='metascore').text if container.find('span', class_='metascore') else '-' metascores.append(m_score) #here are two NV containers, grab both of them as they hold both the votes and the grosses nv = container.find_all('span', attrs={'name': 'nv'}) #filter nv for votes vote = nv[0].text votes.append(vote) #filter nv for gross grosses = nv[1].text if len(nv) > 1 else '-' us_gross.append(grosses)
en nuestroprint
print(titles) print(years) print(time) print(imdb_ratings) print(metascores) print(votes) print(us_gross)
este método:['Parasite', 'Jojo Rabbit', '1917', 'Knives Out', 'Uncut Gems', 'Once Upon a Time... in Hollywood', 'Joker', 'The Gentlemen', 'Ford v Ferrari', 'Little Women', 'The Irishman', 'The Lighthouse', 'Toy Story 4', 'Marriage Story', 'Avengers: Endgame', 'The Godfather', 'Blade Runner 2049', 'The Shawshank Redemption', 'The Dark Knight', 'Inglourious Basterds', 'Call Me by Your Name', 'The Two Popes', 'Pulp Fiction', 'Inception', 'Interstellar', 'Green Book', 'Blade Runner', 'The Wolf of Wall Street', 'Gone Girl', 'The Shining', 'The Matrix', 'Titanic', 'The Silence of the Lambs', 'Three Billboards Outside Ebbing, Missouri', 'Harry Potter and the Sorcerer's Stone', 'The Peanut Butter Falcon', 'The Handmaiden', 'Memories of Murder', 'The Lord of the Rings: The Fellowship of the Ring', 'Gladiator', 'The Martian', 'Bohemian Rhapsody', 'Watchmen', 'Forrest Gump', 'Thor: Ragnarok', 'Casino Royale', 'The Breakfast Club', 'The Godfather: Part II', 'Django Unchained', 'Baby Driver'] ['(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(2019)', '(I) (2019)', '(2019)', '(2019)', '(2019)', '(1972)', '(2017)', '(1994)', '(2008)', '(2009)', '(2017)', '(2019)', '(1994)', '(2010)', '(2014)', '(2018)', '(1982)', '(2013)', '(2014)', '(1980)', '(1999)', '(1997)', '(1991)', '(2017)', '(2001)', '(2019)', '(2016)', '(2003)', '(2001)', '(2000)', '(2015)', '(2018)', '(2009)', '(1994)', '(2017)', '(2006)', '(1985)', '(1974)', '(2012)', '(2017)'] ['132 min', '108 min', '119 min', '131 min', '135 min', '161 min', '122 min', '113 min', '152 min', '135 min', '209 min', '109 min', '100 min', '137 min', '181 min', '175 min', '164 min', '142 min', '152 min', '153 min', '132 min', '125 min', '154 min', '148 min', '169 min', '130 min', '117min', '180 min', '149 min', '146 min', '136 min', '194 min', '118 min', '115 min', '152 min', '97 min', '145 min', '132 min', '178 min', '155 min', '144 min', '134 min', '162 min', '142 min', '130 min', '144 min', '97 min', '202 min', '165 min', '113 min'] [8.6, 8.0, 8.5, 8.0, 7.6, 7.7, 8.6, 8.1, 8.2, 8.0, 8.0, 7.7, 7.8, 8.0, 8.5, 9.2, 8.0, 9.3, 9.0, 8.3, 7.9, 7.6, 8.9, 8.8, 8.6, 8.2, 8.1, 8.2, 8.1,8.4, 8.7, 7.8, 8.6, 8.2, 7.6, 7.7, 8.1, 8.1, 8.8, 8.5, 8.0, 8.0, 7.6, 8.8, 7.9, 8.0, 7.9, 9.0, 8.4, 7.6] ['96 ', '58 ', '78 ', '82 ', '90 ', '83 ', '59 ', '51 ', '81 ', '91 ', '94 ', '83 ', '84 ', '93 ', '78 ', '100 ', '81 ', '80 ', '84 ', '69 ', '93 ', '75 ', '94 ', '74 ', '74 ', '69 ', '84 ', '75 ', '79 ', '66 ', '73 ', '75 ', '85 ', '88 ', '64 ', '70 ', '84 ', '82 ', '92 ', '67 ', '80 ', '49 ', '56 ', '82 ', '74 ', '80 ', '62 ', '90 ', '81 ', '86 '] ['282,699', '142,517', '199,638', '195,728', '108,330', '396,071', '695,224', '42,015', '152,661', '65,234', '249,950', '77,453', '160,180', '179,887', '673,115', '1,511,929', '414,992', '2,194,397', '2,176,865', '1,184,882', '178,688', '76,291', '1,724,518', '1,925,684', '1,378,968', '293,695', '656,442', '1,092,063', '799,696', '835,496', '1,580,250', '994,453', '1,191,182', '383,958', '595,613', '34,091', '92,492', '115,125', '1,572,354', '1,267,310', '715,623', '410,199', '479,811', '1,693,344', '535,065', '555,756', '330,308', '1,059,089', '1,271,569', '398,553'] ['-', '$0.35M', '-', '-', '-', '$135.37M', '$192.73M', '-', '-', '-', '-', '$0.43M', '$433.03M', '-', '$858.37M', '$134.97M', '$92.05M', '$28.34M', '$534.86M', '$120.54M', '$18.10M', '-', '$107.93M', '$292.58M', '$188.02M', '$85.08M', '$32.87M', '$116.90M', '$167.77M', '$44.02M', '$171.48M', '$659.33M', '$130.74M', '$54.51M', '$317.58M', '$13.12M', '$2.01M', '$0.01M', '$315.54M', '$187.71M', '$228.43M', '$216.43M', '$107.51M', '$330.25M', '$315.06M', '$167.45M', '$45.88M', '$57.30M', '$162.81M', '$107.83M']
dice que extraiga todos los dígitos de la cadena- El
M
El método convierte el resultado en un número entero.
Ahora, si ejecutamos
min
en la parte inferior de nuestro programa para ver cómo se ven nuestros datos anuales, este es el resultado:Debería ver su lista de años sin paréntesis. Y el tipo de datos que se muestra ahora es un número entero. Nuestros datos anuales se limpian oficialmente.
Datos de tiempo de limpieza
Haremos exactamente lo que hicimos al limpiar nuestros datos de años anteriores a nuestros datos de tiempo al tomar solo los dígitos y convertir nuestro tipo de datos en un número entero.
|_+_|Limpieza de datos de Metascore
La única limpieza que debemos hacer aquí es convertir nuestro tipo de datos de objeto en un número entero:
|_+_|Limpieza de votos
Con los votos, necesitamos eliminar las comas y convertirlo en un tipo de datos entero:
|_+_|Rompiendo los votos de limpieza en contra:
DataFrame
son nuestros datos de votos en nuestras películasmovies = pd.DataFrame({ 'movie': titles, 'year': years, 'timeMin': time, 'imdb': imdb_ratings, 'metascore': metascores, 'votes': votes, 'us_grossMillions': us_gross, })
. Estamos asignando nuestros nuevos datos limpiados a nuestros votosmovies
.- .
DataFrame
agarra la cadena y usa elpd.DataFrame
método para reemplazar las comas con una cita vacía (nada) - El
DataFrame
El método convierte el resultado en un número entero.
Limpieza de datos brutos
Los datos brutos implican algunos obstáculos que superar. Lo que tenemos que hacer es eliminar el signo de dólar y el
|_+_|print
s de los datos y convertirlo en un número de punto flotante. A continuación, le indicamos cómo hacerlo:Desglose de la limpieza en bruto:
Código de limpieza superior:
DataFrame
son nuestros datos brutos en nuestras películasmovies
. Asignaremos nuestros nuevos datos limpiados a nuestroprint(movies)
columna.print
le dice a los pandas que vayan a la columnaprint(movies.dtypes)
en nuestroyear
- El
timeMin
la función llama a la función especificada para cada elemento de un iterable metascore
es una función anónima en Python (una sin nombre). Las funciones normales se definen mediantevotes
palabra clave.us_grossMillions
son nuestros argumentos de función. Esto le dice a nuestra función que elimine elfloat
del lado izquierdo y pele elcheese
desde el lado derecho.
Código de conversión inferior:
I ate 10 blocks of cheese
está despojado de los elementos que no necesitamos, y ahora le asignaremos los datos del código de conversión para terminarlo10
es un método que podemos usar para cambiar esta columna a un flotante. La razón por la que usamos esto es porque tenemos muchos guiones en esta columna, y no podemos convertirla en un flotante usandoI ate 10 blocks of cheese string
- esto detectaría un error.10
transformará los valores no numéricos, nuestros guiones, en valores NaN (no un número) porque tenemos guiones en lugar de los datos que faltan
Revise el código limpiado y convertido
Veamos cómo lo hicimos. Ejecute el
|_+_|movies['year'] = movies['year'].str.extract('(d+)').astype(int)
función para ver nuestros datos y los tipos de datos que tenemos:El resultado de nuestros datos limpios
El resultado de nuestros tipos de datos
¡Se ve bien!
Código final terminado
Aquí está el código final de su raspador web de una sola página:
|_+_|Guardar sus datos en un CSV
¿De qué sirven nuestros datos extraídos si no podemos guardarlos para proyectos o análisis futuros? A continuación se muestra el código que puede agregar al final de su programa para guardar sus datos en un archivo CSV:
|_+_|Desglosando el archivo CSV:
- |_+_|
Para que este código se ejecute correctamente, deberá crear un archivo vacío y nombrarlo como desee, asegurándose de que tenga la extensión
movies[‘year’]
. Llamé al míoyear
, como puede ver arriba, pero siéntase libre de nombrarlo como quiera. Solo asegúrese de cambiar el código anterior para que coincida.Si está en Repl, puede crear un archivo CSV vacío colocando el cursor cerca de Archivos y haciendo clic en la opción Agregar archivo. Nómbrelo y guárdelo con un
DataFrame
extensión. Luego, agregue el código al final de su programa:|_+_|
Todos sus datos deberían aparecer en su CSV. Una vez que lo descargue en su computadora / lo abra, su archivo se verá así:
Conclusión
Hemos recorrido un largo camino desde solicitar el contenido HTML de nuestra página web hasta limpiar toda nuestra
|_+_|.str.extract(‘(d+’)
. Ahora debería saber cómo extraer páginas web con la misma estructura de URL y HTML que le mostré anteriormente. A continuación, se muestra un resumen de lo que hemos logrado:Próximos pasos
¡Espero que te hayas divertido haciendo esto!
Si desea aprovechar lo que ha aprendido, aquí tiene algunas ideas para probar:
- Obtenga los datos de la película para las 1,000 películas en esa lista
- Extraiga otros datos sobre cada película, por ejemplo, género, director, protagonista o el resumen de la película.
- Encuentra un sitio web diferente para raspar que te interese
En mi próximo artículo, explicaré cómo recorrer todas las páginas de esta lista de IMDb para capturar todas las 1,000 películas, lo que implicará algunas alteraciones en el código final que tenemos aquí.
¡Feliz codificación!
# python # ciencia de datos # aprendizaje automático # programación
.01M', '5.54M', '7.71M', '8.43M', '6.43M', '7.51M', '0.25M', '5.06M', '7.45M', '.88M', '.30M', '2.81M', '7.83M']- El
M
El método convierte el resultado en un número entero.
Ahora, si ejecutamos min
en la parte inferior de nuestro programa para ver cómo se ven nuestros datos anuales, este es el resultado:
Debería ver su lista de años sin paréntesis. Y el tipo de datos que se muestra ahora es un número entero. Nuestros datos anuales se limpian oficialmente.
Datos de tiempo de limpieza
Haremos exactamente lo que hicimos al limpiar nuestros datos de años anteriores a nuestros datos de tiempo al tomar solo los dígitos y convertir nuestro tipo de datos en un número entero.
|_+_|Limpieza de datos de Metascore
La única limpieza que debemos hacer aquí es convertir nuestro tipo de datos de objeto en un número entero:
|_+_|Limpieza de votos
Con los votos, necesitamos eliminar las comas y convertirlo en un tipo de datos entero:
|_+_|Rompiendo los votos de limpieza en contra:
DataFrame
son nuestros datos de votos en nuestras películasmovies = pd.DataFrame({ 'movie': titles, 'year': years, 'timeMin': time, 'imdb': imdb_ratings, 'metascore': metascores, 'votes': votes, 'us_grossMillions': us_gross, })
. Estamos asignando nuestros nuevos datos limpiados a nuestros votosmovies
.- .
DataFrame
agarra la cadena y usa elpd.DataFrame
método para reemplazar las comas con una cita vacía (nada) - El
DataFrame
El método convierte el resultado en un número entero.
Limpieza de datos brutos
Los datos brutos implican algunos obstáculos que superar. Lo que tenemos que hacer es eliminar el signo de dólar y el print
s de los datos y convertirlo en un número de punto flotante. A continuación, le indicamos cómo hacerlo:
Desglose de la limpieza en bruto:
Código de limpieza superior:
DataFrame
son nuestros datos brutos en nuestras películasmovies
. Asignaremos nuestros nuevos datos limpiados a nuestroprint(movies)
columna.print
le dice a los pandas que vayan a la columnaprint(movies.dtypes)
en nuestroyear
- El
timeMin
la función llama a la función especificada para cada elemento de un iterable metascore
es una función anónima en Python (una sin nombre). Las funciones normales se definen mediantevotes
palabra clave.us_grossMillions
son nuestros argumentos de función. Esto le dice a nuestra función que elimine elfloat
del lado izquierdo y pele elcheese
desde el lado derecho.
Código de conversión inferior:
I ate 10 blocks of cheese
está despojado de los elementos que no necesitamos, y ahora le asignaremos los datos del código de conversión para terminarlo10
es un método que podemos usar para cambiar esta columna a un flotante. La razón por la que usamos esto es porque tenemos muchos guiones en esta columna, y no podemos convertirla en un flotante usandoI ate 10 blocks of cheese string
- esto detectaría un error.10
transformará los valores no numéricos, nuestros guiones, en valores NaN (no un número) porque tenemos guiones en lugar de los datos que faltan
Revise el código limpiado y convertido
Veamos cómo lo hicimos. Ejecute el movies['year'] = movies['year'].str.extract('(d+)').astype(int)
función para ver nuestros datos y los tipos de datos que tenemos:
El resultado de nuestros datos limpios
El resultado de nuestros tipos de datos
¡Se ve bien!
Código final terminado
Aquí está el código final de su raspador web de una sola página:
|_+_|Guardar sus datos en un CSV
¿De qué sirven nuestros datos extraídos si no podemos guardarlos para proyectos o análisis futuros? A continuación se muestra el código que puede agregar al final de su programa para guardar sus datos en un archivo CSV:
|_+_|Desglosando el archivo CSV:
- |_+_|
Para que este código se ejecute correctamente, deberá crear un archivo vacío y nombrarlo como desee, asegurándose de que tenga la extensión movies[‘year’]
. Llamé al mío year
, como puede ver arriba, pero siéntase libre de nombrarlo como quiera. Solo asegúrese de cambiar el código anterior para que coincida.
crear un sitio web de cartera con reaccionar
Si está en Repl, puede crear un archivo CSV vacío colocando el cursor cerca de Archivos y haciendo clic en la opción Agregar archivo. Nómbrelo y guárdelo con un DataFrame
extensión. Luego, agregue el código al final de su programa:
|_+_|
Todos sus datos deberían aparecer en su CSV. Una vez que lo descargue en su computadora / lo abra, su archivo se verá así:
Conclusión
Hemos recorrido un largo camino desde solicitar el contenido HTML de nuestra página web hasta limpiar toda nuestra .str.extract(‘(d+’)
. Ahora debería saber cómo extraer páginas web con la misma estructura de URL y HTML que le mostré anteriormente. A continuación, se muestra un resumen de lo que hemos logrado:
Próximos pasos
¡Espero que te hayas divertido haciendo esto!
Si desea aprovechar lo que ha aprendido, aquí tiene algunas ideas para probar:
- Obtenga los datos de la película para las 1,000 películas en esa lista
- Extraiga otros datos sobre cada película, por ejemplo, género, director, protagonista o el resumen de la película.
- Encuentra un sitio web diferente para raspar que te interese
En mi próximo artículo, explicaré cómo recorrer todas las páginas de esta lista de IMDb para capturar todas las 1,000 películas, lo que implicará algunas alteraciones en el código final que tenemos aquí.
¡Feliz codificación!
# python # ciencia de datos # aprendizaje automático # programación