Construyendo tu Primer Scraper Web, Primera Parte
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
Rubyland tiene dos piedras preciosas que han ocupado el foco del scraping de la tela para los últimos años: Nokogiri y Mechanize. Pasamos un artículo sobre cada uno de estos antes de ponerlos en acción con un ejemplo práctico.
Temas
- ¿Qué es la Web Scraping?
- Permiso
- El problema
- Nokogiri
- ¿Extracción?
- Páginas
- API
- Navegación del Nodo
¿Qué es Web Scraping?
Hay términos más sofisticados que la web o el scraping de pantalla. La recolección de la tela y la extracción de datos de la tela casi te dicen enseguida qué está sucediendo. Podemos automatizar la extracción de datos de páginas web — y no es tan complicado.
En cierto modo, estas herramientas te permiten imitar y automatizar la navegación web humana. Escribes un programa que sólo extrae el tipo de datos que te interesan. La orientación de datos específicos es casi tan fácil como usar selectores CSS.
Hace unos años me suscribí a algún curso de vídeo en línea que tenía como un millón de videos cortos, pero no hay opción para descargarlos en masa. Tuve que ir a través de cada enlace por mi cuenta y hacer el temido "guardar como" yo mismo. Era una especie de scraping web humano, algo que a menudo necesitamos hacer cuando no tenemos el conocimiento necesario para automatizar ese tipo de cosas. El curso en sí estaba bien, pero no usé más sus servicios después de eso. Era demasiado tedioso.
Hoy, no me interesaría demasiado ese UX tan revelador. Un scraper que me hiciera la descarga me llevaría solo un par de minutos armarlo. ¡No hay problema!
Déjame desglosarlo muy rápido antes de comenzar. Todo se puede condensar en un par de pasos. Primero buscamos una página web que tenga los datos deseados que necesitamos. Luego buscamos en esa página e identificamos la información que queremos extraer.
El último paso es enfocar estos bits, cortarlos si es necesario y decidir cómo y dónde desea almacenarlos. El HTML bien escrito es a
menudo la clave para que este proceso sea fácil y agradable. Para extracciones más complicadas, puede ser doloroso si tiene que lidiar con marcados mal estructuradas.
¿Qué hay de las API? Muy buena pregunta. Si tiene acceso a un servicio con una API, a menudo hay poca necesidad de escribir su propio scraper. Este enfoque es principalmente para sitios web que no ofrecen ese tipo de conveniencia. Sin una API, esta es a menudo la única forma de automatizar la extracción de información de los sitios web.
Podrías preguntar, ¿cómo funciona realmente? Sin saltar al fondo, la respuesta corta es atravesando el árbol de estructuras de datos. Nokogiri construye estas estructuras de datos a partir de los documentos que lo alimenta y le permite orientar las partes de interés para la extracción. Por ejemplo, CSS es un lenguaje escrito para cruzar árboles, para buscar estructuras de datos de árbol, y podemos utilizarlo para la extracción de datos.
Hay muchos enfoques y soluciones para usar. Rubyland tiene dos gemas que han ocupado el centro de atención durante varios años. Muchas personas todavía confían en Nokogiri y Mechanize para las necesidades de scrapping HTML. Ambos han sido probados y probados para ser fáciles
de usar y altamente capaces. Los miraremos a ambos. Pero antes de eso, me gustaría tomar un momento para abordar el problema que vamos a resolver al final de esta breve serie introductoria.
Permisos
Antes de comenzar scrapping, asegúrese de tener permiso de los sitios a los que intenta acceder para la extracción de datos. Si el sitio tiene una API o una fuente RSS, por ejemplo, puede que no solo sea más fácil obtener ese contenido deseado, sino que también podría ser la opción legal de elección.
No todos apreciarán si lo haces masivamente en sus sitios, comprensiblemente. Obtenga información sobre ese sitio en particular que le interesa y no se meta en problemas. Hay pocas probabilidades de que inflijas un daño grave, pero correr el riesgo, sin saberlo, no es el camino a seguir.
El problema
Necesitaba construir un nuevo podcast El diseño no estaba donde yo quería, y odiaba la forma de publicar publicaciones nuevas. ¡Maldito WYSIWYG! Un poco de contexto. Hace aproximadamente dos años, construí la primera versión de mi podcast. La idea era jugar con Sinatra y construir algo súper liviano. Me encontré con un par de problemas inesperados ya que hice casi todo a medida.
Viniendo de Rails, definitivamente fue un viaje educativo que aprecié, pero rápidamente lamenté no haber usado un sitio estático que podría haber implementado en GitHub a través de GitHub Pages. Implementar nuevos episodios y mantenerlos carecía de la simplicidad que estaba buscando. Por un tiempo, decidí que tenía peces más grandes para
freír y me concentré en producir nuevo material de podcast.
El verano pasado comencé a ponerme serio y trabajé en un sitio Middleman alojado en las páginas de GitHub. Para la segunda temporada del espectáculo, quería algo genial. Un diseño nuevo y simplificado, Markdown para publicar nuevos episodios y ninguna pelea con Heroku. ¡Cielos! El caso es que tenía 139 episodios por ahí que necesitaban ser importados y convertidos primero para poder trabajar con Middleman.
Para publicaciones, Middleman usa archivos .markdown que han llamado frontmatter para datos, que básicamente reemplaza mi base de datos. Hacer esta transferencia a mano no es una opción para 139 episodios. Para eso es la computación. Necesitaba encontrar una forma de analizar el HTML de mi sitio web anterior, eliminar el contenido relevante y transferirlo a publicaciones de blog que utilizo para publicar nuevos episodios de podcast en Middleman.
Por lo tanto, en los próximos tres artículos, les presentaré las
herramientas comúnmente usadas en Rubyland para tales tareas. Al final, revisaremos mi solución para mostrarte algo práctico
también.
Nokogiri
Incluso si eres completamente nuevo en Ruby/Rails, es muy probable que ya hayas oído hablar de esta pequeña gema. El nombre se quita a menudo y se pega contigo fácilmente. No estoy seguro de que muchos sepan que nokogiri es japonés para "sierra".
Es un nombre apropiado una vez que entiendes lo que hace la herramienta. El creador de esta gema es el encantador Tenderlove, Aaron Patterson. Nokogiri convierte documentos XML y HTML en una estructura de datos, una estructura de datos en árbol, para ser más precisos. La herramienta es rápida y ofrece una interfaz agradable también. En general, es una biblioteca muy potente que se encarga de una multitud de necesidades de scrapping HTML.
Puedes usar Nokogiri no solo para analizar HTML; XML también. Le brinda las opciones tanto del lenguaje de ruta XML como de las interfaces CSS para recorrer los documentos que carga. XML Path Language, o XPath para abreviar, es un lenguaje de consulta.
Nos permite seleccionar nodos desde documentos XML. Los selectores de CSS son probablemente más familiares para los principiantes. Al igual que con los estilos que escribe, los selectores de CSS hacen que sea increíblemente fácil orientar las secciones específicas de las páginas que son de interés para la extracción. Solo debe informar a Nokogiri qué es lo que persigue cuando se dirige a un destino en particular.
Páginas
Lo que siempre necesitamos para empezar es buscar la página real que nos interesa. Especificamos qué tipo de documento de Nokogiri queremos analizar: XML o HTML, por ejemplo:
1 |
Nokogiri::XML |
2 |
|
3 |
Nokogiri::HTML |
some_scraper.rb
1 |
require "nokogiri" |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
page = Nokogiri::XML(File.open("some.xml")) |
6 |
|
7 |
page = Nokogiri::HTML(File.open("some.html")) |
Nokogiri:XML y Nokogiri:HTML puede tomar objetos IO u objetos String. Lo que sucede arriba es sencillo. Esto abre y recupera la página designada usando open-uri y luego carga su estructura, su XML o HTML en un nuevo documento Nokogiri. XML no es algo con lo que los principiantes tengan que lidiar muy a menudo.
Por lo tanto, recomendaría que nos centremos en el análisis de HTML por ahora. ¿Por qué open-uri? Este módulo de la Biblioteca estándar de Ruby nos
permite tomar el sitio sin mucho alboroto. Debido a que objetos IO son faciles, podemos hacer uso de open-uri.
API
Pongamos esto en práctica con un mini ejemplo:
at_css
some_podcast_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
header = page.at_css("h2.post-title") |
10 |
|
11 |
title = header.text |
12 |
|
13 |
puts "This is the raw header of the latest episode: #{header}" |
14 |
|
15 |
puts "This is the title of the latest episode: #{title}" |
Lo que hicimos aquí representa todos los pasos que generalmente están relacionados con el scrapping web, solo a un nivel micro. Decidimos qué URL necesitamos y qué sitio necesitamos buscar, y las cargamos en un nuevo documento de Nokogiri. Luego abrimos esa página y nos dirigimos a una sección específica.
Aquí solo quería saber el título del último episodio. Usar el método at_css y un selector CSS para h2.post-title era todo lo que necesitaba para enfocar el punto de
extracción. Sin embargo, con este método, solo haremos scrapping este elemento singular. Esto nos da el selector completo, que la mayoría de las veces
no es exactamente lo que necesitamos. Por lo tanto, extraemos solo la porción de texto interno de este nodo a través del método text. Para comparar, puede verificar el resultado tanto para el encabezado como para el texto a continuación.
Salida
1 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2> |
2 |
|
3 |
This is the title of the latest episode: David Heinemeier Hansson |
Aunque este ejemplo tiene aplicaciones muy limitadas, posee todos los ingredientes, todos los pasos que debe comprender. Creo que es genial lo simple que es esto. Debido a que podría no ser obvio a partir de este ejemplo, me gustaría señalar qué tan poderosa puede ser esta herramienta. Veamos qué más podemos hacer con un script de Nokogiri.
¡Atención!
Si eres principiante y no estás seguro de cómo orientar el HTML necesario para esto, le recomiendo que busques en línea cómo inspeccionar los contenidos de los sitios web en su navegador. Básicamente, todos los navegadores principales hacen que este proceso sea realmente fácil en estos días.
En Chrome, solo tiene que hacer clic derecho en un elemento del sitio web y elegir la opción de inspección. Esto abrirá una pequeña ventana en la parte inferior de su navegador que muestra algo así como una radiografía del DOM del sitio. Tiene muchas más opciones, y recomiendo pasar tiempo en Google para educarse. ¡Es un tiempo dedicado sabiamente!
css
El método css nos brindará no solo un elemento de elección sino cualquier elemento que coincida con los criterios de búsqueda en la página. ¡Muy
limpio y directo!
some_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
headers = page.css("h2.post-title") |
10 |
|
11 |
headers.each do |header| |
12 |
puts "This is the raw title of the latest episode: #{header}" |
13 |
end
|
14 |
|
15 |
headers.each do |header| |
16 |
puts "This is the title of the latest episode: #{header.text}" |
17 |
end
|
Salida
1 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2> |
2 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2> |
3 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2> |
4 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2> |
5 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2> |
6 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2> |
7 |
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/136/">James Edward Gray II</a></h2> |
8 |
|
9 |
This is the title of the latest episode: David Heinemeier Hansson |
10 |
This is the title of the latest episode: Zach Holman |
11 |
This is the title of the latest episode: Joel Glovier |
12 |
This is the title of the latest episode: João Ferreira |
13 |
This is the title of the latest episode: Corwin Harrell |
14 |
This is the title of the latest episode: Roberto Machado |
15 |
This is the title of the latest episode: James Edward Gray II |
La única pequeña diferencia en este ejemplo es que iteraré en los encabezados sin formato primero. También extraje su texto interno con el método text. Nokogiri se detiene automáticamente al final de la página y no intenta seguir la paginación en ningún lugar automáticamente.
Digamos que queremos tener un poco más de información, por ejemplo, la fecha y el subtítulo de cada episodio. Simplemente podemos expandir el ejemplo anterior. De todos modos, es una buena idea seguir este paso por paso. Haz que funcione una pieza pequeña y agrega más complejidad en el camino.
some_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
articles = page.css("article.index-article") |
10 |
|
11 |
articles.each do |article| |
12 |
header = article.at_css("h2.post-title") |
13 |
date = article.at_css(".post-date") |
14 |
subtitle = article.at_css(".topic-list") |
15 |
|
16 |
puts "This is the raw header: #{header}" |
17 |
puts "This is the raw date: #Jul 19, 2018" |
18 |
puts "This is the raw subtitle: #{subtitle}\n\n" |
19 |
|
20 |
puts "This is the text header: #{header.text}" |
21 |
puts "This is the text date: #{date.text}" |
22 |
puts "This is the text subtitle: #{subtitle.text}\n\n" |
23 |
end
|
Salida
1 |
This is the raw header: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2> |
2 |
This is the raw date: <span class="post-date">Oct 18 | 2016</span> |
3 |
This is the raw subtitle: <h3 class="topic-list">Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk</h3> |
4 |
|
5 |
This is the text header: David Heinemeier Hansson |
6 |
This is the text date: Oct 18 | 2016
|
7 |
This is the text subtitle: Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk
|
8 |
|
9 |
This is the raw header: <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2> |
10 |
This is the raw date: <span class="post-date">Oct 12 | 2016</span> |
11 |
This is the raw subtitle: <h3 class="topic-list">Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication</h3> |
12 |
|
13 |
This is the text header: Zach Holman |
14 |
This is the text date: Oct 12 | 2016
|
15 |
This is the text subtitle: Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication |
16 |
|
17 |
This is the raw header: <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2> |
18 |
This is the raw date: <span class="post-date">Oct 10 | 2016</span> |
19 |
This is the raw subtitle: <h3 class="topic-list">Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source</h3> |
20 |
|
21 |
This is the text header: Joel Glovier |
22 |
This is the text date: Oct 10 | 2016
|
23 |
This is the text subtitle: Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source |
24 |
|
25 |
This is the raw header: <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2> |
26 |
This is the raw date: <span class="post-date">Aug 26 | 2015</span> |
27 |
This is the raw subtitle: <h3 class="topic-list">Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3> |
28 |
|
29 |
This is the text header: João Ferreira |
30 |
This is the text date: Aug 26 | 2015
|
31 |
This is the text subtitle: Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values |
32 |
|
33 |
This is the raw header: <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2> |
34 |
This is the raw date: <span class="post-date">Aug 06 | 2015</span> |
35 |
This is the raw subtitle: <h3 class="topic-list">Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3> |
36 |
|
37 |
This is the text header: Corwin Harrell |
38 |
This is the text date: Aug 06 | 2015
|
39 |
This is the text subtitle: Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays | |
40 |
|
41 |
This is the raw header: <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2> |
42 |
This is the raw date: <span class="post-date">Aug 03 | 2015</span> |
43 |
This is the raw subtitle: <h3 class="topic-list">CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs</h3> |
44 |
|
45 |
This is the text header: Roberto Machado |
46 |
This is the text date: Aug 03 | 2015
|
47 |
This is the text subtitle: CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs |
48 |
|
49 |
This is the raw header: <h2 class="post-title"><a href="episodes/136/">James Edward Gray II</a></h2> |
50 |
This is the raw date: <span class="post-date">Jul 30 | 2015</span> |
51 |
This is the raw subtitle: <h3 class="topic-list">Screencasting | Less Code | Reading code | Getting unstuck | Rails’s codebase | CodeNewbie | Small examples | Future plans | PeepCode | Frequency & pricing</h3> |
52 |
|
53 |
This is the text header: James Edward Gray II |
54 |
This is the text date: Jul 30 | 2015
|
55 |
This is the text subtitle: Screencasting | Less Code | Reading code | Getting unstuck | Rails’s codebase | CodeNewbie | Small examples | Future plans | PeepCode | Frequency & pricing |
En este punto, ya tenemos algunos datos para jugar. Podemos estructurarlo o destruirlo de la manera que queramos. Lo anterior simplemente debe mostrar lo que tenemos de manera legible. Por supuesto, podemos profundizar en cada uno de ellos mediante el uso de expresiones regulares con el método text.
Analizaremos esto con más detalle cuando llegue el momento de resolver el problema real del podcast. No será una clase en la expresión regular, pero verás algo más en acción, pero no te preocupes, no tanto como para hacer explotar tu cerebro.
Atributos
Lo que podría ser útil en esta etapa es extraer el href
para los episodios individuales también. No podría ser más simple.
some_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
articles = page.css("article.index-article") |
10 |
|
11 |
articles.each do |article| |
12 |
header = article.at_css("h2.post-title") |
13 |
date = article.at_css(".post-date") |
14 |
subtitle = article.at_css(".topic-list") |
15 |
link = article.at_css("h2.post-title a") |
16 |
podcast_url = "http://betweenscreens.fm/" |
17 |
|
18 |
puts "This is the raw header: #{header}" |
19 |
puts "This is the raw date: #Jul 19, 2018" |
20 |
puts "This is the raw subtitle: #{subtitle}" |
21 |
puts "This is the raw link: #{link}\n\n" |
22 |
|
23 |
puts "This is the text header: #{header.text}" |
24 |
puts "This is the text date: #{date.text}" |
25 |
puts "This is the text subtitle: #{subtitle.text}" |
26 |
puts "This is the raw link: #{podcast_url}#{link[:href]}\n\n" |
27 |
end
|
Los aspectos más importantes a los que hay que prestar atención aquí son [:href] y podcast_url. Si etiqueta en [:] simplemente puede extraer un atributo del selector de
destino. He abstraido un poco más, pero se puede ver más claramente cómo funciona a continuación.
1 |
...
|
2 |
|
3 |
href = article.at_css("h2.post-title a")[:href] |
4 |
|
5 |
...
|
Para obtener una URL completa y útil, guardé el dominio raíz en una variable y construí la URL completa para cada episodio.
1 |
...
|
2 |
|
3 |
podcast_url = "http://betweenscreens.fm/" |
4 |
|
5 |
puts "This is the raw link: #{podcast_url}#{link[:href]}\n\n" |
6 |
|
7 |
...
|
Echemos un vistazo rápido a la salida:
Salida
1 |
This is the raw header: <h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
2 |
This is the raw date: <span class="post-date">Oct 25 | 2016</span> |
3 |
This is the raw subtitle: <h3 class="topic-list">Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> |
4 |
This is the raw link: <a href="episodes/143/">Jason Long</a> |
5 |
|
6 |
This is the text header: Jason Long |
7 |
This is the text date: Oct 25 | 2016
|
8 |
This is the text subtitle: Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas
|
9 |
This is the href: http://betweenscreens.fm/episodes/143/ |
10 |
|
11 |
This is the raw header: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2> |
12 |
This is the raw date: <span class="post-date">Oct 18 | 2016</span> |
13 |
This is the raw subtitle: <h3 class="topic-list">Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk</h3> |
14 |
This is the raw link: <a href="episodes/142/">David Heinemeier Hansson</a> |
15 |
|
16 |
This is the text header: David Heinemeier Hansson |
17 |
This is the text date: Oct 18 | 2016
|
18 |
This is the text subtitle: Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk
|
19 |
This is the href: http://betweenscreens.fm/episodes/142/ |
20 |
|
21 |
This is the raw header: <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2> |
22 |
This is the raw date: <span class="post-date">Oct 12 | 2016</span> |
23 |
This is the raw subtitle: <h3 class="topic-list">Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication</h3> |
24 |
This is the raw link: <a href="episodes/141/">Zach Holman</a> |
25 |
|
26 |
This is the text header: Zach Holman |
27 |
This is the text date: Oct 12 | 2016
|
28 |
This is the text subtitle: Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication |
29 |
This is the href: http://betweenscreens.fm/episodes/141/ |
30 |
|
31 |
This is the raw header: <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2> |
32 |
This is the raw date: <span class="post-date">Oct 10 | 2016</span> |
33 |
This is the raw subtitle: <h3 class="topic-list">Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source</h3> |
34 |
This is the raw link: <a href="episodes/140/">Joel Glovier</a> |
35 |
|
36 |
This is the text header: Joel Glovier |
37 |
This is the text date: Oct 10 | 2016
|
38 |
This is the text subtitle: Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source |
39 |
This is the href: http://betweenscreens.fm/episodes/140/ |
40 |
|
41 |
This is the raw header: <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2> |
42 |
This is the raw date: <span class="post-date">Aug 26 | 2015</span> |
43 |
This is the raw subtitle: <h3 class="topic-list">Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3> |
44 |
This is the raw link: <a href="episodes/139/">João Ferreira</a> |
45 |
|
46 |
This is the text header: João Ferreira |
47 |
This is the text date: Aug 26 | 2015
|
48 |
This is the text subtitle: Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values |
49 |
This is the href: http://betweenscreens.fm/episodes/139/ |
50 |
|
51 |
This is the raw header: <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2> |
52 |
This is the raw date: <span class="post-date">Aug 06 | 2015</span> |
53 |
This is the raw subtitle: <h3 class="topic-list">Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3> |
54 |
This is the raw link: <a href="episodes/138/">Corwin Harrell</a> |
55 |
|
56 |
This is the text header: Corwin Harrell |
57 |
This is the text date: Aug 06 | 2015
|
58 |
This is the text subtitle: Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays | |
59 |
This is the href: http://betweenscreens.fm/episodes/138/ |
60 |
|
61 |
This is the raw header: <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2> |
62 |
This is the raw date: <span class="post-date">Aug 03 | 2015</span> |
63 |
This is the raw subtitle: <h3 class="topic-list">CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs</h3> |
64 |
This is the raw link: <a href="episodes/137/">Roberto Machado</a> |
65 |
|
66 |
This is the text header: Roberto Machado |
67 |
This is the text date: Aug 03 | 2015
|
68 |
This is the text subtitle: CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs |
69 |
This is the href: http://betweenscreens.fm/episodes/137/ |
Genial, ¿no? Puede hacer lo mismo para extraer la [:class] de un selector.
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
body_classes = page.at_css("body")[:class] |
Si ese nodo tiene más de una clase, obtendrá una lista de todas ellas.
Navegación del nodo
- parent
- children
- previous_sibling
- next_sibling
Estamos acostumbrados a tratar con estructuras de árbol en CSS o incluso jQuery. Sería un dolor si Nokogiri no ofreciera una práctica API para moverse dentro de esos árboles.
some_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
header = page.at_css("h2.post-title") |
10 |
header_children = page.at_css("h2.post-title").children |
11 |
header_parent = page.at_css("h2.post-title").parent |
12 |
header_prev_sibling = page.at_css("h2.post-title").previous_sibling |
13 |
|
14 |
puts "#{header}\n\n" |
15 |
puts "#{header_children}\n\n" |
16 |
puts "#{header_parent}\n\n" |
17 |
puts "#{header_prev_sibling}\n\n" |
Salida
1 |
#header
|
2 |
<h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
3 |
|
4 |
#header_children
|
5 |
<a href="episodes/143/">Jason Long</a> |
6 |
|
7 |
#header_parent
|
8 |
<article class="index-article"> |
9 |
<span class="post-date">Oct 25 | 2016</span><h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
10 |
<h3 class="topic-list">Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> |
11 |
<div class="soundcloud-player-small"> |
12 |
</div> |
13 |
</article> |
14 |
|
15 |
#header_previous_sibling
|
16 |
<span class="post-date">Oct 25 | 2016</span> |
Como puede ver por sí mismo, esta es una de las cosas más poderosas, especialmente cuando ve lo que .parent pudo recopilar de una vez. En lugar de definir un grupo de nodos a mano, puede recopilarlos al por mayor.
Incluso puede encadenarlos para más cruces involucrados. Puede tomar esto tan complicado como quiera, por supuesto, pero le advierto que mantenga las cosas simples. Puede volverse un poco difícil y difícil de entender rápidamente. Recuerda, "¡Hazlo simple, estúpido!"
1 |
...
|
2 |
|
3 |
header_parent_parent = page.at_css("h2.post-title").parent.parent |
4 |
header_prev_sibling_parent_children = page.at_css("h2.post-title").previous_sibling.parent.children |
5 |
|
6 |
...
|
some_scraper.rb
1 |
require 'nokogiri' |
2 |
|
3 |
require "open-uri" |
4 |
|
5 |
url = 'http://betweenscreens.fm/' |
6 |
|
7 |
page = Nokogiri::HTML(open(url)) |
8 |
|
9 |
header = page.at_css("h2.post-title") |
10 |
header_prev_sibling_children = page.at_css("h2.post-title").previous_sibling.children |
11 |
header_parent_parent = page.at_css("h2.post-title").parent.parent |
12 |
header_prev_sibling_parent = page.at_css("h2.post-title").previous_sibling.parent |
13 |
header_prev_sibling_parent_children = page.at_css("h2.post-title").previous_sibling.parent.children |
14 |
|
15 |
puts "#{header}\n\n" |
16 |
puts "#{header_prev_sibling_children}\n\n" |
17 |
puts "#{header_parent_parent}\n\n" |
18 |
puts "#{header_prev_sibling_parent}\n\n" |
19 |
puts "#{header_prev_sibling_parent_children}\n\n" |
Salida
1 |
#header
|
2 |
<h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
3 |
|
4 |
#header_previous_sibling_children
|
5 |
Oct 25 | 2016 |
6 |
|
7 |
#header_parent_parent
|
8 |
<li> |
9 |
<article class="index-article"> |
10 |
<span class="post-date">Oct 25 | 2016</span><h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
11 |
<h3 class="topic-list">Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> |
12 |
<div class="soundcloud-player-small"> |
13 |
</div> |
14 |
</article> |
15 |
</li> |
16 |
|
17 |
#header_previous_sibling_parent
|
18 |
<article class="index-article"> |
19 |
<span class="post-date">Oct 25 | 2016</span><h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
20 |
<h3 class="topic-list">Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> |
21 |
<div class="soundcloud-player-small"> |
22 |
</div> |
23 |
</article> |
24 |
|
25 |
#header_previous_sibling_parent_children
|
26 |
<span class="post-date">Oct 25 | 2016</span><h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> |
27 |
<h3 class="topic-list">Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> |
28 |
<div class="soundcloud-player-small"> |
29 |
</div> |
Pensamientos finales
Nokogiri no es una gran biblioteca, pero tiene mucho que ofrecer. Te recomiendo que juegues con lo que has aprendido hasta ahora y amplíes tu conocimiento a través de su documentación cuando chocas contra una pared. ¡Pero no te metas en problemas!
Esta pequeña introducción debería ayudarlo a comprender qué puede hacer y cómo funciona. Espero que lo exploren un poco más por su cuenta y se diviertan con él. Como descubrirá por su cuenta, es una herramienta rica que sigue dando.



