En mi viaje de programación autodidacta , mis intereses se encuentran en el aprendizaje automático (ML) y la inteligencia artificial (IA), y el lenguaje que elegí dominar es Python.
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 obtener más conocimiento y comprensión.
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é sucede si no puede encontrar un conjunto de datos que desea usar y analizar? Ahí es donde entra en juego un web scraper.
Trabajar en proyectos es crucial para solidificar el conocimiento que obtienes. Cuando comencé este proyecto, estaba un poco abrumado porque realmente no sabía nada.
Cumplir con eso, encontrar respuestas a mis preguntas sobre 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 comience en Python.
Esta guía lo guiará a través de la comprensión de las páginas web HTML, la creación de un web scraper 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 parte. ¡Espero que codifiques y disfrutes!
Los sitios web pueden restringir o prohibir el raspado de datos de su sitio web. Los usuarios pueden estar sujetos a ramificaciones legales dependiendo de dónde y cómo intente obtener información. Los sitios web generalmente describen esto en sus términos de uso y en su archivo robots.txt que se encuentra en su sitio, que generalmente se parece a esto: www.example.com/robots.txt . Así que raspe de manera responsable y respete el archivo robots.txt.
El web scraping consiste en recopilar datos disponibles en sitios web. Esto se puede hacer manualmente por un ser humano o mediante el uso de un bot.
Un bot es un programa que creas que te ayuda a extraer los datos que necesitas mucho más rápido de lo que pueden hacerlo la mano y los ojos de un ser humano.
Es esencial identificar el objetivo de su raspado desde el principio. No queremos raspar ningún dato que en realidad no necesitemos.
Para este proyecto, recopilaremos datos de las "1000 mejores" películas de IMDb , específicamente las 50 mejores películas de esta página. Aquí está la información que recopilaremos de cada lista de películas:
Los rastreadores web recopilan datos del sitio web de la misma manera que lo haría un ser 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 a tener en cuenta al construir un raspador web:
Para comenzar, veamos la URL de la página que queremos raspar .
Esto es lo que vemos en la URL:
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ámetros groups=top_1000
especifica de qué tratará la página &ref_adv_prv
nos lleva a la página siguiente o anterior. La referencia es la página en la que estamos actualmente. adv_nxt
y adv_prv
hay dos valores posibles: traducidos para avanzar a la página siguiente y avanzar a la página anterior.Cuando navegue de un lado a otro por las páginas, notará que solo cambian los parámetros. Tenga en cuenta esta estructura, ya que es útil conocerla mientras construimos el raspador.
HTML significa lenguaje de marcado de hipertexto, y la mayoría de las páginas web se escriben con él. Esencialmente, HTML es la forma en que dos computadoras se comunican entre sí a través de Internet, y los sitios web son lo que dicen.
Cuando accede a una URL, su computadora envía una solicitud al servidor que aloja el sitio. Cualquier tecnología puede estar ejecutándose en ese servidor (JavaScript, Ruby, Java, etc.) para procesar su solicitud. Eventualmente, el servidor devuelve una respuesta a su navegador; a menudo, esa respuesta tendrá la forma de una página HTML para que la muestre su navegador.
HTML describe la estructura de una página web semánticamente y originalmente incluía pistas para la apariencia del documento.
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 larga lista 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 haya hecho clic, si mueve el cursor sobre cualquier elemento de la página, notará que se resaltará junto con las etiquetas HTML en el menú al que están asociadas, 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 ayudarnos a extraer el HTML de la página.
Las herramientas que vamos a utilizar son:
Puede seguir a continuación dentro de su entorno Repl o IDE, o puede ir directamente al código completo aquí . ¡Que te diviertas!
Primero, importaremos las herramientas que necesitaremos para que podamos usarlas para ayudarnos a construir el raspador y obtener los datos que necesitamos.
Es muy probable que cuando ejecutemos nuestro código para raspar algunas de estas películas, obtengamos los nombres de las películas traducidos al idioma principal del país en el que se originó la película.
Usa este código para asegurarte de obtener títulos traducidos al inglés de todas las películas que raspamos:
Obtener el contenido de la página que estamos viendo solicitando la URL:
Desglose de las solicitudes de URL:
url
es la variable que creamos y le asignamos la URL results
es la variable que creamos para almacenar nuestra acción request.get requests.get(url, headers=headers)
es el método que usamos para captar el contenido de la URL. headers
parte le dice a nuestro raspador que nos traiga inglés, basado en nuestra línea de código anterior.Haga que el contenido que capturamos sea fácil de leer usando BeautifulSoup:
Rompiendo BeautifulSoup abajo:
soup
es la variable que creamos para asignar el método BeatifulSoup, que especifica un formato deseado de resultados utilizando el analizador HTML; esto permite que Python lea los componentes de la página en lugar de tratarlos como una cadena larga print(soup.prettify())
imprimirá lo que hemos agarrado en un formato de árbol más estructurado, lo que facilita la lecturaLos resultados de la impresión se verán más ordenados, así:
Cuando escribimos código para extraer nuestros datos, necesitamos un lugar para almacenar esos datos. Cree variables para cada tipo de datos que extraerá y asígnele una lista vacía, indicada por corchetes
[]
. Recuerde la lista de información que queríamos obtener de cada película anterior: Tu código ahora debería verse así. Tenga en cuenta que podemos eliminar nuestra
print
función hasta que necesitemos usarla de nuevo. Es hora de revisar el código HTML en nuestra página web.
Vaya a la página web que estamos raspando, inspecciónela y desplace el cursor sobre una sola película en su totalidad, como se muestra a continuación:
Necesitamos descubrir qué distingue a cada uno de estos de otros contenedores div que vemos.
Notarás la lista de
div
elementos a la derecha con un class
atributo que tiene dos valores: lister-item
y mode-advanced
.Si hace clic en cada uno de ellos, notará que resaltará cada contenedor de película a la izquierda de la página, como arriba.
Si hacemos una búsqueda rápida dentro de inspeccionar (presionamos Ctrl+F y escribimos
lister-item mode-advanced
), veremos 50 coincidencias que representan las 50 películas que se muestran en una sola página. Ahora sabemos que toda la información que buscamos se encuentra dentro de este div
etiqueta. lister-item mode-advanced
divisionesNuestro próximo paso es decirle a nuestro raspador que encuentre todos estos divs avanzados en modo lister-item:
Rotura
find_all
abajo: movie_div
es la variable que usaremos para almacenar todos los contenedores div con una clase de lister-item mode-advanced
find_all()
El método extrae todos los contenedores div que tienen un atributo de clase de modo de elemento de listado avanzado de lo que hemos almacenado en nuestra sopa de variables.Si nos fijamos en la primera película de nuestra lista:
¡Nos faltan las ganancias brutas! Si miras la segunda película, la han incluido allí.
Algo que siempre debe considerar al construir un web scraper 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 construir alrededor de la idea de que simplemente no sabemos si eso sucederá o no.
lister-item mode-advanced
divisiónCuando tomamos cada uno de los elementos que necesitamos en un solo
lister-item mode-advanced
div
contenedor, necesitamos el raspador para pasar al siguiente lister-item mode-advanced
div
contenedor y toma esos elementos de la película también. Y luego debe pasar al siguiente y así sucesivamente, 50 veces para cada página. Para que esto se ejecute, necesitaremos envolver nuestro raspador en un bucle for . Desglosando el bucle for:
for
círculo se utiliza para iterar sobre una secuencia. Nuestra secuencia es cada lister-item mode-advanced
div
contenedor en el que almacenamos movie_div
container
es el nombre de la variable que entra en cada div. Puedes nombrar esto como quieras ( x
, loop
, banana
, cheese
), y no cambiará la función del ciclo.Se puede leer así:
Comenzando con el nombre de la película, ubiquemos 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,
<a>
. Esta etiqueta está anidada dentro de una etiqueta de encabezado, <h3>
. los <h3>
la etiqueta está anidada dentro de un <div>
etiqueta. Este <div>
es el tercero de los div
Está anidado en el contenedor de la primera película. Desglosando los títulos:
name
es la variable que usaremos para almacenar los datos del título que encontremos container
es lo que usamos en nuestro for
bucle: se usa para iterar cada vez. h3
y .a
es una notación de atributo y le dice al raspador que acceda a cada una de esas etiquetas. text
le dice al raspador que tome el texto anidado en la etiqueta <a> titles.append(name)
le dice al raspador que tome lo que encontramos y almacenó en el nombre y lo agregue a nuestra lista vacía llamada títulos, que creamos al principioLocalicemos 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 del
<span>
etiqueta debajo de la <a>
etiqueta que contiene el título de la película. La notación de puntos, que usamos para encontrar los datos del título ( .h3.a
), funcionó porque fue el primero <a>
etiqueta después de la h3
etiqueta. Desde el <span>
la etiqueta que queremos es la segunda <span>
etiqueta, tenemos que usar un método diferente.En cambio, podemos decirle a nuestro raspador que busque por la marca distintiva del segundo
<span>
. Usaremos el find()
método , que es similar a find_all()
excepto que solo devuelve el primer partido. Desglosando los años:
year
es la variable que usaremos para almacenar los datos del año que encontremos container
es lo que usamos en nuestro bucle for: se usa para iterar cada vez. h3
es una notación de atributo, que le dice al raspador que acceda a esa etiqueta. find()
es un método que usaremos para acceder a este particular <span>
etiqueta 'span', class_ = 'lister-item-year'
) es el distintivo <span>
etiqueta que queremos years.append(year)
le dice al raspador que tome lo que encontramos y almacenamos en el año y lo agregue a nuestra lista vacía llamada años (que creamos al principio)Ubique la duración de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el total de minutos.
Los datos que necesitamos se pueden encontrar en un
<span>
etiqueta con una clase de tiempo de ejecución. Como hicimos con el año, podemos hacer algo similar: Desglosando el tiempo:
runtime
es la variable que usaremos para almacenar los datos de tiempo que encontremos container
es lo que usamos en nuestro bucle for: se usa para iterar cada vez. find()
es un método que usaremos para acceder a este particular <span>
etiqueta <span>
etiqueta que queremos if container.p.find('span', class_='runtime') else '-'
dice que si hay datos allí, agárralos, pero si faltan datos, entonces coloca un guión allí. text
le dice al raspador que tome ese texto en el <span>
etiqueta time.append(runtime)
le dice al raspador que tome lo que encontramos y almacenó en tiempo de ejecución y lo agregue a nuestra lista vacía llamada tiempo (que creamos al principio)Encuentre la calificación de IMDb de la película y su línea HTML correspondiente usando 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
<strong>
etiqueta. Como no veo ninguna otra <strong>
etiquetas, podemos usar la notación de atributos (notación de puntos) para capturar estos datos. Desglosando las calificaciones de IMDb:
imdb
es la variable que usaremos para almacenar los datos de calificaciones de IMDB que encuentre container
es lo que usamos en nuestro for
bucle: se usa para iterar cada vez. strong
es una notación de atributo que le dice al raspador que acceda a esa etiqueta. text
le dice al raspador que tome ese texto float()
convierte el texto que encontramos en un flotante, que es un decimal imdb_ratings.append(imdb)
le dice al raspador que tome lo que encontramos y almacenamos en imdb
y para agregarlo a nuestra lista vacía llamada imdb_ratings
(que creamos al principio).Encuentre la calificación Metascore de la película y su línea HTML correspondiente usando inspeccionar y haciendo clic en el número Metascore.
Los datos que necesitamos se pueden encontrar en un
<span>
etiqueta que tiene una clase que dice metascore favorable
.Antes de decidirnos por eso, debes notar que, por supuesto, un 96 para “Parasite” muestra una calificación favorable, pero ¿los demás son favorables? Si resalta el Metascore de la próxima película, verá que "JoJo Rabbit" tiene una clase que dice
metascore mixed
. Dado que estas etiquetas son diferentes, sería seguro decirle al raspador que use solo la clase metascore
al raspar: Desglosando Metascores:
m_score
es la variable que usaremos para almacenar los datos de clasificación Metascore que encuentra container
es lo que usamos en nuestro for
bucle: se usa para iterar cada vez. find()
es un método que usaremos para acceder a este particular <span>
etiqueta 'span', class_ = 'metascore'
) es el distintivo <span>
etiqueta que queremos. text
le dice al raspador que tome ese texto if container.find('span', class_='metascore') else '-'
dice que si hay datos allí, agárralos, pero si faltan, entonces coloca un guión allí int()
método convierte el texto que encontramos en un número entero metascores.append(m_score)
le dice al raspador que tome lo que encontramos y almacenamos en m_score
y para agregarlo a nuestra lista vacía llamada metascores
(que creamos al principio)Finalmente llegamos a los dos elementos finales que necesitamos extraer, pero dejamos lo más difícil 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 en esta lista, no vemos un número de ganancias brutas. 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 el bruto se destacan a la derecha. Después de ver los votos y los contenedores brutos de la película n.° 2, ¿qué notas?
Como puede ver, ambos están en una
<span>
etiqueta que tiene un name
atributo que equivale nv
y un data-value
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?
Esto requirió mucha flexión del cerebro, toneladas de café y un par de noches para descubrirlo. Así es como lo hice:
Desglose de votos y bruto:
nv
es una variable completamente nueva que usaremos para mantener tanto los votos como el bruto <span>
etiquetas container
es lo que usamos en nuestro for
bucle para iterar cada vez find_all()
es el método que usaremos para capturar ambos <span>
etiquetas 'span', attrs = 'name' : 'nv'
) es cómo podemos obtener atributos de esa etiqueta específica. vote
es la variable que usaremos para almacenar los votos que encontremos en el nv
etiqueta nv[0]
le dice al raspador que entre en el nv
etiquete y tome los primeros datos en la lista, que son los votos porque los votos son lo primero en nuestro código HTML (las computadoras cuentan en binario, comienzan a contar en 0, no en 1). text
le dice al raspador que tome ese texto votes.append(vote)
le dice al raspador que tome lo que encontramos y almacenamos en vote
y para agregarlo a nuestra lista vacía llamada votes
(que creamos al principio) grosses
es la variable que usaremos para almacenar el bruto que encontramos en el nv
etiqueta nv[1]
le dice al raspador que entre en el nv
etiquete y obtenga los segundos datos de la lista, lo cual es bruto porque el bruto ocupa el segundo lugar en nuestro código HTML nv[1].text if len(nv) > 1 else '-'
dice si la longitud de nv
es mayor que uno, luego encuentre el segundo dato que está almacenado. Pero si los datos que están almacenados en nv
no es mayor que uno, es decir, si falta el bruto, entonces coloque un guión allí. us_gross.append(grosses)
le dice al raspador que tome lo que encontramos y almacenamos en grosses
y para agregarlo a nuestra lista vacía llamada us_grosses
(que creamos al principio)Su código ahora debería verse así:
Ahora que le hemos dicho a nuestro raspador qué elementos raspar, usemos la función de impresión para imprimir cada lista a la que enviamos nuestros datos raspados:
Nuestras listas se ven así
['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', ' 117 min', ' 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.35 M', '-', '-', '-', '$ 135.37 M', '$ 192.73 M', '-', '-', '-', '-', '$ 0.43 M', '$ 433.03 M', '-', '$ 858.37 M', '$ 134.97 M', '$ 92.05 M', '$ 28.34 M', '$ 534.86 M', '$ 120.54 M', '$ 18.10 M', '-', '$ 107.93 M', '$ 292.58 M', '$ 188.02 M', '$ 85.08 M', '$ 32.87 M', '$ 116.90 M', '$ 167.77 M', '$ 44.02 M', '$ 171.48 M', '$ 659.33 M', '$ 130.74 M', '$ 54.51 M', '$ 317.58 M', '$ 13.12 M', '$ 2.01 M', '$ 0.01 M', '$ 315.54 M', '$ 187.71 M', '$ 228.43 M', '$ 216.43 M', '$ 107.51 M', '$ 330.25 M', '$ 315.06 M', '$ 167.45 M', '$ 45.88 M', '$ 57.30 M', '$ 162.81 M', '$ 107.83 M']
Hasta aquí todo bien, pero aún 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, Ms, mins, comas, paréntesis y espacios en blanco adicionales en Metascores.
El siguiente orden del día es construir un DataFrame con pandas para almacenar los datos que tenemos muy bien en una tabla para entender realmente lo que está pasando.
Así es como lo hacemos:
movies
es lo que llamaremos nuestro DataFrame pd.DataFrame
es como inicializamos la creación de un DataFrame con pandasPodemos ver cómo se ve todo simplemente usando la función de impresión en nuestro DataFrame, al que llamamos películas, en la parte inferior de nuestro programa:
Nuestro marco de datos de pandas se ve así
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:
La limpieza de datos es el proceso de detectar y corregir o eliminar registros corruptos o inexactos de su conjunto de datos.
Al realizar análisis de datos, también es importante asegurarse de que estamos utilizando los tipos de datos correctos.
Podemos verificar cómo se ven nuestros tipos de datos ejecutando esta función de impresión en la parte inferior de nuestro programa:
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. Nuestro puntaje de IMDb también es correcto porque tenemos números de coma flotante en esta columna (números decimales).
Pero nuestro
year
, timeMin
, metascore
, y votes
muestran que son objetos cuando deberían ser tipos de datos enteros, y nuestro us_grossMillions
es un objeto en lugar de un float
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 también pueden contener números.
Por ejemplo, la palabra
cheese
y el phrase I ate 10 blocks of cheese
son ambas cadenas. Si tuviéramos que deshacernos de todo excepto del 10
desde el I ate 10 blocks of cheese
cadena, sigue siendo una cadena, pero ahora es una que solo dice 10
.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.
Para eliminar los paréntesis de los datos de nuestro año y convertir el objeto en un tipo de datos entero, haremos lo siguiente:
Desglose de los datos del año de limpieza:
movies['year']
le dice a los pandas que vayan a la columna año en nuestro DataFrame
.str.extract('(\d+')
este método: ('(\d+')
dice que extraiga todos los dígitos en la cadena .astype(int)
método convierte el resultado a un número enteroAhora, si corremos
print(movies['year'])
en la parte inferior de nuestro programa para ver cómo se ven los datos de nuestro año, 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. Los datos de nuestro año se limpian oficialmente.
Haremos exactamente lo que hicimos limpiando los datos de nuestro año anterior a nuestros datos de tiempo tomando solo los dígitos y convirtiendo nuestro tipo de datos a un número entero.
La única limpieza que necesitamos hacer aquí es convertir nuestro tipo de datos de objeto en un número entero:
Con los votos, debemos eliminar las comas y convertirlo en un tipo de datos entero:
Desglosando los votos de limpieza:
movies['votes']
son nuestros datos de votos en nuestras películas DataFrame
. Estamos asignando nuestros nuevos datos limpios a nuestros votos. DataFrame
. .str.replace(' , ' , '')
agarra la cuerda y usa el replace
método para reemplazar las comas con una comilla vacía (nada) .astype(int)
método convierte el resultado en un número enteroLos datos brutos implican algunos obstáculos para saltar. Lo que debemos hacer es eliminar el signo de dólar y la M de los datos y convertirlos en un número de coma flotante. Aquí está cómo hacerlo:
Desglosando la limpieza bruta:
Código de limpieza superior:
movies['us_grossMillions']
son nuestros datos brutos en nuestras películas DataFrame
. Asignaremos nuestros nuevos datos limpios a nuestro us_grossMillions
columna. movies['us_grossMillions']
le dice a los pandas que vayan al column us_grossMillions
en nuestro DataFrame
.map()
función llama a la función especificada para cada elemento de un iterable lambda x: x
es una función anónima en Python (una sin nombre). Las funciones normales se definen usando el def
palabra clave. lstrip('$').rstrip('M')
son los argumentos de nuestra función. Esto le dice a nuestra función que elimine el $
desde el lado izquierdo y pelar el M
desde el lado derecho.Código de conversión inferior:
movies['us_grossMillions']
está despojado de los elementos que no necesitamos, y ahora le asignaremos los datos del código de conversión para terminarlo pd.to_numeric
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 simplemente convertirlo en un flotante usando .astype(float) — esto detectaría un error. errors='coerce'
transformará los valores no numéricos, nuestros guiones, en NaN (no-un-número) valores porque tenemos guiones en lugar de los datos que faltanVeamos cómo lo hicimos. Ejecute la función de impresión para ver nuestros datos y los tipos de datos que tenemos:
¡Se ve bien!
Aquí está el código final de su raspador web de una sola página:
¿Cuál es el uso de nuestros datos raspados si no podemos guardarlos para ningún proyecto o análisis futuro? 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:
movies.to_csv('movies.csv')
Para que este código se ejecute correctamente, deberá crear un archivo vacío y nombrarlo como desee, asegurándose de que tenga el
.csv
extensión. nombré a la mía movies.csv
, 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 pasando el mouse cerca de Archivos y haciendo clic en la opción "Agregar archivo". Nómbrelo y guárdelo con un
.csv
extensión. Luego, agregue el código al final de su programa: movies.to_csv('the_name_of_your_csv_here.csv')
Todos sus datos deben completarse en su CSV. Una vez que lo descargue en su computadora/lo abra, su archivo se verá así:
Hemos recorrido un largo camino desde solicitar el contenido HTML de nuestra página web hasta limpiar todo nuestro DataFrame. Ahora debería saber cómo raspar páginas web con la misma estructura HTML y URL que le mostré anteriormente. Este es un resumen de lo que hemos logrado:
¡Espero que te hayas divertido haciendo esto!
Si desea aprovechar lo que ha aprendido, aquí hay algunas ideas para probar:
En mi próximo artículo, explicaré cómo recorrer todas las páginas de esta lista de IMDb para obtener todas las 1000 películas, lo que implicará algunas modificaciones en el código final que tenemos aquí.
Publicado anteriormente en https://medium.com/better-programming/the-only-step-by-step-guide-youll-need-to-build-a-web-scraper-with-python-e79066bd895a