Desde hace unos días tenía pendiente un favorcillo que alguien me pidió. No es (reconozcámoslo) que me apeteciera mil porque me olía que la solución, conociéndome a mí y conociendo el problema, sería guarra a más no poder. Pero como quien me lo pidió es alguien adorable que bien merece dedicarle algunos minutos (y mucho más), pues me puse a la tarea, ayer, con pijama, manta, y la ola de frío al otro lado de la puerta. Por supuesto, en Ruby, el lenguaje sexy y buzz-compliant.
Hete aquí que cuando acabo (sí, amigos, por una vez el cuento acaba bien) me encuentro con que mi buen amigo Felipe acaba de publicar una solución completamente diferente a un problema completamente igual (aunque en realidad su script hace alguna cosa más). También en Ruby. Y como, para mi gusto (esto es como con los hijos), mi solución es más limpia, bonita y fácil, heme aquí compartiéndola, con él, y con todos los demás.
La tarea es simple, obtener un feed de una página que no lo tiene, para poder usarlo en un agregador, incluírlo en otra página, o para cualquiera de las cosas chupis y bonitas que se pueden hacer con un feed. Para ello no queda más remedio que parsear el HTML de la página, buscar el contenido relevante, y luego construir con él el feed. Se dice rápido. Y se hace casi más.
scrAPI es una librería de Ruby que sirve exactamente para eso: scrapear (si tuviera que traducir la palabra, diría guarrear, y me parece un poco ofensivo =:-P) HTML.
Para hacerlo, hemos de definir, con un minilenguaje al efecto, las estructuras de datos que se van a buscar, y mapear en qué elementos HTML se encuentran, usando selectores CSS. Se entiende más fácil que se explica (perdón por la ausencia de sangrado):
recording = Scraper.define do process 'a img', :img => "@src" process 'div.datosGrabacion h3 a', :author => :text, :url => "@href" process 'p', :title => :text result :url, :author, :title, :img end
Con ese código definimos una estructura llamada recording, formada por url, autor, título e imagen, y los selectores que nos permiten encontrar esos atributos.
Alguno dirá (yo lo diría): "¡Qué mierda de filtro! Si buscas por 'a img' o por 'p' vas a encontrar mogollón de resultados que no te interesan". Una de las cosas que más molan de scrAPI es que se pueden definir las estructuras de forma anidada, lo cual permite afinar mucho más, y además es DRY:
recordings = Scraper.define do array :recordings process "div#contenido ul.listado li", :recordings => recording result :recordings end
Con ésto definimos una estructura llamada recordings (nótese el plural), compuesta de muchos recording (la estructura que definimos antes), cada uno de ellos dentro de un "div#contenido ul.listado li". Ahora sí que no podemos fallar =;-)
Así pues, ya sólo nos queda aplicar este scraper a la página en cuestión:
recordings_data = recordings.scrape(URI.parse("http://thebellemusic.com/grabaciones.asp"))
Y crear el feed con FeedTools, que además de ser muy fácil ya lo contó Manuel hace poco", así que no hace falta mucho detalle, ¿no?
require 'rubygems'
require 'scrapi'
require 'feed_tools'
recording = Scraper.define do
process 'a img', :img => "@src"
process 'div.datosGrabacion h3 a', :author => :text, :url => "@href"
process 'p', :title => :text
result :url, :author, :title, :img
end
recordings = Scraper.define do
array :recordings
process "div#contenido ul.listado li", :recordings => recording
result :recordings
end
recordings_data = recordings.scrape(URI.parse("http://thebellemusic.com/grabaciones.asp"))
recordings_feed = FeedTools::Feed.new
recordings_feed.title = 'TBM - Grabaciones'
recordings_feed.author = 'The Belle Music'
recordings_feed.link = 'http://thebellemusic.com/'
recordings_feed.language = 'es-ES'
recordings_data.each do |recording|
item = FeedTools::FeedItem.new
item.title = "#{recording[:author]} - #{recording[:title]}"
item.link = "#{recordings_feed.link}#{recording[:url]}"
item.description = "
"
recordings_feed.items << item
end
puts recordings_feed.build_xml('rss', 2.0)
Por Dios, qué horror, sin sangrado ni colores; se recomienda bajarlo y abrirlo con un editor decente =;-)
Y bueno, para otro día dejamos la reflexión sobre qué pueden significar este tipo de técnicas para la web, permitiendo ir más allá, en cuanto al intercambio de contenidos, que los feeds (que ya es), y en definitiva de lo que el autor de los mismos haya tenido a bien prever. Y sin olvidar los microformatos, claro.

Hombre, pues esto nos va a venir de coña para la próxima barrabasada del departamento de chapa y pintura :D
Mi pregunta es... scrAPI o hPricot?
Pues... si te fijas el post va d eeso porque Felipe usó hPricot.
La verdad, no sabría decir porque no he usado ninguno de los dos en profundidad. Mi intuición y primera impresión es que scrAPI es más limpio, fácil y bonito, y hPricot más potente. Potencia que el 90% de las veces no hace falta.
Pero prefiero no mojarme, no he entrado en profundidad...
Aunque... el Scraper.define do... me tiene enamorao! =xD
Pues está quedando bien-bonito :) ...
Qué paciencia tenés! ;-)
Jurrr!! No conocia esa libreria, la verdad no conocía ninguna y la primera que encontré fue hPricot, y me puse con ella.... pero vamos la verdad es que esta la veo mas elegante, habrá que usarla para el siguiente parseo...
De todas formas según lo que he podido ver en las dos, si el html a parsear esta bien marcado y usa css o usan xhtml no tendremos problemas con ninguna de las dos, pero si te hacen guarreridas de la calaña de meterte urls dentro de javascript y cositas así pues la cosa se complica y tienes que empezar a usar expresiones regulares y hacer pirulas como las que me tocó hacer a mi en el script.
Un saludo :-D