Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby
Code

Construyendo su primer Web Scraper, parte 3

by
Length:LongLanguages:

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Bienvenido de nuevo a esta serie sobre la construcción de un web scraper. En este tutorial, pasaré por un ejemplo de scraping de datos de mi propio sitio de podcast. Voy a cubrir en detalle cómo extraí los datos, cómo el ayudante y los métodos de utilidad realizar su trabajo, y cómo todas las piezas del rompecabezas se unen.

Temas

  • Scraping mi podcast
  • Pry
  • Scraper
  • Métodos auxiliares
  • Escribir mensajes

Scraping mi podcast

Pongamos en práctica lo que hemos aprendido hasta ahora. Por varias razones, un rediseño para mi podcast Entre | Las pantallas estaban atrasadas. Había problemas que me hizo gritar cuando me desperté en la mañana. Así que decidí crear un nuevo sitio estático, construido con Middleman y alojado con GitHub Pages.

He invertido una buena cantidad de tiempo en el nuevo diseño después de ajustar un blog Middleman a mis necesidades. Todo lo que quedaba por hacer era importar el contenido de mi aplicación Sinatra respaldada por la base de datos, así que necesitaba raspar el contenido existente y transferirlo a mi nuevo sitio estático.

Hacer esto a mano en la moda schmuck no estaba en la mesa, ni siquiera una pregunta, ya que podía confiar en mis amigos Nokogiri y Mechanize para hacer el trabajo para mí. Lo que está por delante de usted es un trabajo scrape  razonablemente pequeño que no es demasiado complicado, pero ofrece algunos giros interesantes que deben ser educativos para la web scraping novatos por ahí.

A continuación se presentan dos capturas de pantalla de mi podcast.

Captura de pantalla del viejo Podcast

A screenshot of an old podcast

Captura de pantalla Nuevo Podcast

A screenshot of a new podcast

Vamos a desglosar lo que queremos lograr. Queremos extraer los siguientes datos de 139 episodios que se distribuyen en 21 sitios indexados paginados:

  • el título
  • El entrevistado
  • La subcarpeta con la lista de temas
  • El número de pista de SoundCloud para cada episodio
  • La fecha
  • El número del episodio
  • El texto de las notas del espectáculo
  • Los enlaces de las notas del espectáculo

Hacemos iteración a través de la paginación y dejamos que Mechanize haga clic en cada enlace de un episodio. En la siguiente página de detalles, encontraremos toda la información de arriba que necesitamos. Utilizando los datos raspados, queremos rellenar el asunto principal y el "cuerpo" de los archivos de reducción para cada episodio.

A continuación puede ver una vista previa de cómo vamos a componer los nuevos archivos de reducción con el contenido que hemos extraído. Creo que esto le dará una buena idea sobre el alcance delante de nosotros. Esto representa el paso final en nuestro pequeño script. No se preocupe, lo repasaremos con más detalle.

def compose_markdown

También quería añadir algunos trucos que el antiguo sitio no podía reproducir. Tener un sistema de etiquetado personalizado y completo en el lugar era crucial para mí. Quería que los oyentes tuvieran una profunda herramienta de descubrimiento. Por lo tanto, necesitaba etiquetas para cada entrevistado y dividir el subtítulo en etiquetas también. Como he producido 139 episodios en la primera temporada solo, tuve que preparar el sitio por un tiempo cuando la cantidad de contenido se vuelve más difícil. Un sistema profundo de etiquetado con recomendaciones inteligentemente colocadas era el camino a seguir. Esto me permitió mantener el sitio ligero y rápido.

Echemos un vistazo al código completo para raspar el contenido de mi sitio. Mire a su alrededor y trate de averiguar el panorama general de lo que está pasando. Puesto que espero que usted esté en el lado del principiante de cosas, permanecí lejos de abstraer demasiado y erré en el lado de la claridad. Hice un par de refactorings que fueron dirigidos a ayudar a la claridad del código, pero también dejó un poco de carne en el hueso para que usted juegue con cuando haya terminado con este artículo. Después de todo, el aprendizaje de la calidad sucede cuando usted va más allá de la lectura y juega con un cierto código por su cuenta.

En el camino, le animo a comenzar a pensar en cómo puede mejorar el código frente a usted. Esta será su tarea final al final de este artículo. Una pequeña sugerencia de mí: romper grandes métodos en pequeños es siempre un buen punto de partida. Una vez que usted entienda cómo funciona el código, usted debe tener un tiempo divertido perfeccionar en que refactorización.

Ya comencé por extraer un montón de métodos en ayudantes pequeños y enfocados. Usted debe fácilmente ser capaz de aplicar lo que ha aprendido de mis artículos anteriores sobre los olores de código y sus refactorizacion. Si esto todavía le pasa la cabeza ahora mismo, no se preocupe, todos hemos estado allí. Simplemente manténganlo, y en algún momento las cosas comenzarán a hacer clic más rápido.

Código Completo

¿Por qué no requerimos require "Nokogiri"? Mechanize nos proporciona todas nuestras necesidades de scraping. Como discutimos en el artículo anterior, Mechanize se basa en la parte superior de Nokogiri y nos permite extraer el contenido también. Sin embargo, fue importante cubrir esa joya en el primer artículo, ya que necesitábamos construir encima de él.

Pry

Lo primero es lo primero. Antes de saltar a nuestro código aquí, pensé que era necesario mostrarle cómo puede verificar eficientemente si su código funciona como se espera en cada paso del camino. Como ciertamente ha notado, he añadido otra herramienta a la mezcla. Entre otras cosas, Pry es realmente útil para depurar.

Si coloca Pry.start(binding) en cualquier parte de su código, puede inspeccionar su aplicación exactamente en ese punto. Puede introducir objetos en puntos específicos de la aplicación. Esto es realmente útil para tomar su aplicación paso a paso sin tropezar con sus propios pies. Por ejemplo, vamos a colocarlo justo después de nuestra función write_page y comprobar si el link es lo que esperamos.

Pry

Si ejecuta el script, obtendremos algo como esto.

Salida

Cuando preguntamos por el objeto link, podemos comprobar si estamos en el camino correcto antes de pasar a otros detalles de implementación.

Terminal

Parece que lo que necesitamos. Genial, podemos seguir adelante. Hacer esto paso a paso a través de toda la aplicación es una práctica importante para asegurarse de que no te pierdas y realmente entiendes cómo funciona. No voy a cubrir Pry aquí en más detalle ya que me llevaría al menos otro artículo completo para hacerlo. Sólo puedo recomendar su uso como una alternativa a la shell IRB estándar. Volver a nuestra tarea principal.

Scraper

Ahora que has tenido la oportunidad de familiarizarse con las piezas del rompecabezas en su lugar, te recomiendo que las examinemos una por una y aclaremos algunos puntos interesantes aquí y allá. Comencemos con las piezas centrales.

podcast_scraper.rb

¿Qué sucede en el método scrape? En primer lugar, me paseo por todas las páginas de índice en el podcast viejo. Estoy utilizando la URL antigua de la aplicación Heroku ya que el nuevo sitio ya está en línea en betweenscreens.fm. Tenía 20 páginas de episodios que necesitaba iterar.

Delimite el bucle a través de la variable link_range, que actualizo con cada bucle. Pasar por la paginación fue tan simple como usar esta variable en la URL de cada página. Simple y eficaz.

def scrape

Entonces, cada vez que tengo una nueva página con otros ocho episodios para raspar, uso page.links para identificar los enlaces que queremos hacer clic y seguir a la página de detalle de cada episodio. Decidí ir con una gama de acoplamientos (links[2..8]) puesto que era constante en cada página. También fue la forma más fácil de orientar los vínculos que necesito de cada página de índice. No hay necesidad de moverse con los selectores CSS aquí.

A continuación, alimentaremos ese vínculo para la página de detalles al método write_page. Aquí es donde se hace la mayor parte del trabajo. Tomamos ese enlace, lo hacemos clic y lo seguimos hasta la página de detalles donde podemos empezar a extraer sus datos. En esa página encontramos toda la información que necesito para componer mis nuevos episodios de rebajas para el nuevo sitio.

def write_page

def extract_data

Como puede ver arriba, tomamos esa página de detalle detail_page y aplicamos un montón de métodos de extracción. Extraemos el entrevistado interviewee, título title, sc_id, texto text, Titulo de episodio episode_title y Numero de Episodio episode_number. Refactoré un grupo de métodos de ayuda enfocados que están a cargo de estas responsabilidades de extracción. Echemos un rápido vistazo a ellos:

Métodos auxiliares

Métodos de Extracción

He extraído estos ayudantes porque me permitió tener métodos más pequeños en general. Encapsular su comportamiento también fue importante. El código también lee mejor. La mayoría toman el detail_page como un argumento y extraen algunos datos específicos que necesitamos para nuestros mensajes de Middleman.

Buscamos la página de un selector específico y obtenemos el texto sin espacios en blanco innecesarios.

Tomamos el título y eliminar ? y # ya que estos no juegan muy bien con la materia delantera en los mensajes de nuestros episodios. Más sobre la materia delantera abajo.

Aquí necesitamos trabajar un poco más para extraer el ID de SoundCloud para nuestras pistas alojadas. Primero necesitamos los iframes de Mechanize con el href de soundcloud.com y lo convierten en una cadena para escanear ...

Entonces haga coincidir una expresión regular con sus dígitos de la pista id- nuestro soundcloud_id "221003494".

Extraer las notas de la demostración es otra vez absolutamente directo. Sólo necesitamos buscar los párrafos de la serie en la página de detalles. No hay sorpresas aquí.

Lo mismo ocurre con el subtítulo, excepto que es sólo una preparación para extraer el número de episodios de forma limpia.

Aquí necesitamos otra ronda de expresión regular. Vamos a echar un vistazo antes y después de aplicar el regex.

episode_subtitle

número

Un paso más hasta que tengamos un número limpio.

Tomamos ese número con un hash # y lo quitamos. Voilà, tenemos nuestro primer episodio número 139 extraído también. Sugiero que examinemos los otros métodos de utilidad antes de reunirlos.

Métodos de utilidad

Después de todo ese negocio de extracción, tenemos algo de limpieza que hacer. Ya podemos empezar a preparar los datos para componer la reducción. Por ejemplo, cortar el episode_subtitle un poco más para obtener una fecha limpia y construir las etiquetas tags con el método build_tags.

def clean_date

Ejecutamos otra regex que busca fechas como esta: " 26 de agosto de 2015". Como puede ver, esto no es muy útil todavía. A partir del string_date que obtenemos del subtítulo, necesitamos crear un objeto Date real. De lo contrario, sería inútil crear puestos de Middleman.

string_date

Por lo tanto tomamos esa cadena y hacemos un Date.parse. El resultado parece mucho más prometedor.

Date

def build_tags

Esto toma el título title y el entrevistado interviewee que hemos construido dentro del método extract_data y elimina todos los caracteres de pipa y basura.  Reemplazamos caracteres de tubo con una coma, @,?, # Y & con una cadena vacía, y finalmente cuidar de las abreviaturas para with.

def strip_pipes

Al final, incluimos también el nombre del entrevistado en la lista de etiquetas y separamos cada etiqueta con una coma.

Antes

Despues

Cada una de estas etiquetas terminará siendo un enlace a una colección de publicaciones para ese tema. Todo esto sucedió dentro del método extract_data. Veamos otra vez dónde estamos:

def extract_data

Todo lo que queda por hacer aquí es devolver un hash de opciones con los datos que hemos extraído. Podemos alimentar este hash en el método compose_markdown, que obtiene nuestros datos listos para ser escritos como un archivo que necesito para mi nuevo sitio.

Escribir mensajes

def compose_markdown

Para publicar episodios de podcast en mi sitio de Middleman, opté por reutilizar el sistema de blogs. En lugar de crear publicaciones de blog "puras", creo notas de muestra para mis episodios que muestran los episodios alojados de SoundCloud a través de iframes. En los sitios de índice, solo muestro ese iframe más el título y otras cosas.

El formato que necesito para que esto funcione está compuesto por algo llamado materia delantera front matter. Esto es básicamente una tienda de valores clave para mis sitios estáticos. Está reemplazando mis necesidades de base de datos de mi antiguo sitio de Sinatra.

Los datos como el nombre del entrevistado, la fecha, la identificación de pista de SoundCloud, el número de episodio y así sucesivamente van entre tres guiones (---) encima de nuestros archivos de episodios. A continuación se presenta el contenido de cada episodio: cosas, preguntas, enlaces, material de patrocinador, etc.

Front Matter

En el método compose_markdown, uso un HEREDOC para componer ese archivo con su materia inicial para cada episodio que realizamos. Desde el hash de opciones que alimentamos este método, extraemos todos los datos que recogimos en el método ayudante extract_data .

def compose_markdown

Este es el plan para un nuevo episodio de podcast allí mismo. Esto es lo que buscabamos. Quizás se esté preguntando sobre esta sintaxis particular: #{options[:interviewee]}. Interpolo como de costumbre con cuerdas, pero como ya estoy dentro de un <<-HEREDOC, puedo dejar las comillas dobles.

Sólo para orientarnos, todavía estamos en el bucle, dentro de la función write_page para cada enlace pulsado a una página de detalle con las notas de un episodio singular. Lo que sucede a continuación es prepararse para escribir este plano en el sistema de archivos. En otras palabras, creamos el episodio real proporcionando un nombre de archivo y el compuesto markdown_text.

Para ese último paso, sólo necesitamos preparar los siguientes ingredientes: la fecha, el nombre del entrevistado y el número del episodio. Además, el markdown_text, por supuesto, que acabamos de obtener de compose_markdown.

def write_page

Entonces sólo necesitamos tomar el file_name y el markdown_text y escribir el archivo.

def write_page

Vamos a romper esto también. Para cada puesto, necesito un formato específico: algo así como 2016-10-25-Avdi-Grimm-120. Quiero escribir los archivos que comiencen con la fecha e incluir el nombre del entrevistado y el número del episodio.

Para que coincida con el formato que Middleman espera para los nuevos mensajes, necesité tomar el nombre del entrevistado y ponerlo a través de mi método auxiliar dasherize para darme el nombre, Avdi Grimm a Avdi-Grimm. Nada mágico, pero vale la pena mirar:

def dasherize

Elimina espacios en blanco del texto que rascamos para el nombre del entrevistado y reemplaza el espacio en blanco entre Avdi y Grimm con un guión. El resto del nombre de archivo se descompone en la cadena misma: "date-interviewee-name-episodenumber".

def write_page

Dado que el contenido extraído viene directamente de un sitio HTML, no puedo usar simplemente .md o .markdown como la extensión del nombre de archivo. Decidí ir con .html.erb.md. Para futuros episodios que compongo sin scraping, puedo dejar fuera la parte .html.erb y solo necesito .md.

Después de este paso, el bucle en la función scrape termina, y debemos tener un solo episodio que se parezca a esto:

2014-12-01-Avdi-Grimm-1.html.erb.md

Este scraper comenzaría en el último episodio, por supuesto, hasta el primero. Para fines de demostración, el episodio 01 es tan bueno como cualquiera. Usted puede ver en la parte superior front matter con los datos que hemos extraído.

Todo eso estaba bloqueado previamente en la base de datos de mi número de episodio de la aplicación Sinatra, la fecha, el nombre del entrevistado y así sucesivamente. Ahora lo tenemos preparado para ser parte de mi nuevo sitio estático de Middleman. Todo debajo de los dos guiones triples (---) es el texto de las notas del espectáculo: preguntas y enlaces en su mayoría.

Pensamientos finales

Y hemos terminado. Mi nuevo podcast ya está funcionando. Estoy muy contento de haber tomado el tiempo para rediseñar desde cero. Es mucho mejor publicar nuevos episodios ahora. Descubrir nuevos contenidos debe ser más suave para los usuarios también.

Como mencioné anteriormente, este es el momento en que debes ir a tu editor de código para divertirte. Tome este código y luchar con él un poco. Trate de encontrar la manera de hacerlo más sencillo. Hay algunas oportunidades para refactorizar el código.

En general, espero que este pequeño ejemplo le dio una buena idea de lo que puede hacer con su nueva web scraping. Por supuesto, usted puede lograr desafíos mucho más sofisticados-estoy seguro de que hay incluso un montón de pequeñas oportunidades de negocios que se hacen con estas habilidades.

Pero como siempre, tome un paso a la vez, y no se frustre demasiado si las cosas no hacen clic de inmediato. Esto no sólo es normal para la mayoría de las personas, sino que se espera. Es parte del viaje. ¡Feliz scraping!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.