Historias
Slashboxes
Comentarios
 

Login Barrapunto

Login

[ Crear nueva cuenta ]

mig21 (7781)

mig21
  reversethis-{moc.liamg} {ta} {pb12gim}
https://twitter.com/yapw

Hola, soy Miguel. Algo que pueda ser relevante aquí... Uhmm... Me gusta escribir en mi bitácora de BP [barrapunto.com] y en su clon en blogspot: Yet Another Programming Weblog [blogspot.com]
Me gustaría que Barrapunto fuese un sitio con más discusiones técnicas y trato de hacer lo que está en mi mano. De todos modos, también me gusta leer flames ;)

No creo que te interese, pero en Lecturas aleatorias [blogspot.com] dejo registro de los libros que voy leyendo...

Esta es toda mi información de usuario :)

Down Kill Up Publicidad

Bitácora de mig21 (7781)

Lunes, 29 de Enero 2007

Exceso de información, filtros bayesianos y DIY

08:38h.
Tecnología

Desde que leí esta entrada de manje, "Selección de noticias mediante filtro bayesiano" en la que presentaba su proyecto, lo pensé... ¿por que nadie ha hecho un lector al que lo entrenes y te filtre según lo has entrenado? Bueno, manje lo tiene hecho y se pude probar (es TU PERIODICO)

No estoy seguro de que un filtrado automático sea la solución al exceso de fuentes de información, además de tenerlo que entrenar, con la inversión en esfuerzo que eso supone. Pero a este respecto manje y los usuarios de TU PERIODICO nos pueden comentar más. Yo a nivel personal si que digo que me ha resultado más entretenido programar un miniscrí que entrenar el filtro y que sigo usando bloglines a pesar de haber alternativas seguramente mejores...

En fin, como me interesó la idea, al menos un rato :), pues me puse a hacerlo yo mismo... porqué no, ¿verdad? Total que me hice un script en Ruby, porque me apetecía, porque yo lo valgo y para ser buzzword compliant :) (No sé, a otros les ha servido de algo :)). Como no tengo ningún tiempo de seguir su desarrollo (y tampoco se para que...) lo pongo por aquí por si le sirve a alguien, aunque sea solo de mal ejemplo. Aviso que está hecho con la velocidad y el descuido de la pasión, con lo que no es de calidad de producción ni en la forma ni en la organización.

Permite leer feed por feed en modo interactivo o todo seguido y se va guardando las fechas de última lectura de las fuentes, con lo que en teoría debería mostrar solo los ítems nuevos. En la práctica, sospecho que a mitad por el parser de feeds que he usado, Ruby feedparser, a mitad por que los que los forman no tienen mucho cuidado (pero no lo he analizado en serio) hay fuentes que salen entradas ya leídas. El escrí permite ir llenando su "base de datos" de fuentes o importarlas de bloglines. Permite, claro, entrenarlo (con Classifier, que se instala a través de rubygems) y permite que la salida sea en modo texto, para esos frikis como yo a los que nos encanta la consolica, y claro, para todos aquellos que quieran procesar la salida...

Después de estos preámbulos, ahí va:

require 'rubygems'
require 'stemmer'
require 'classifier'
require 'yaml'

class SimpleFeed
attr_reader :url, :date
attr_writer :date
def initialize(url, date)
@url = url
@date = date
end
end

def serialize( c, name )
File.open( name, 'w' ) do |out|
YAML.dump( c, out )
end
end

require 'net/http'
require 'uri'

def fetch(uri_str, date, limit = 10)
#TODO: I should choose better exception.
raise ArgumentError, 'HTTP redirect too deep' if limit == 0

header={'If-Modified-Since' => date.to_s}
uri=URI.parse(uri_str)
http = Net::HTTP.new(uri.host, uri.port)
response=http.get(uri.path,header)
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
else
puts "error getting " + uri_str + response
STDIN.gets()
response.error!
end
end

#const
Filter_file = "filter.yaml"
Feeds_file = "feeds.yaml"

#default options
add_feed = false
train = false
html_content = false
no_interactive = false
bloglines_import = false
bloglines_file = ''

ARGV.each_with_index do |arg,index|
case arg
when '--add_feed'
add_feed = true
when '--train'
train = true
when '--html_content'
html_content = true
when '--no_interactive'
no_interactive = true
when '--bloglines_import'
bloglines_import = true
bloglines_file = ARGV[index+1]
puts bloglines_file
end

end

#load filter
begin
filter =YAML::load_file( Filter_file )
rescue SystemCallError
filter= Classifier::Bayes.new 'Interesting', 'Uninteresting'
end

#load feeds
feeds = Array.new
if(bloglines_import == false)
begin
feeds = YAML::load_file(Feeds_file)
rescue SystemCallError
end
else
require 'rexml/document'
include REXML
file = File.new(bloglines_file)
doc = Document.new(file)
doc.root.each_element('body/outline/outline'){
|outline| feeds.push(SimpleFeed.new(outline.attributes['xmlU rl'],Time.at(0)))
}
end

if(add_feed == true)
print ('Insert a feed, please: ')
f=SimpleFeed.new(STDIN.gets().chop!,Time.at(0))
feeds.push(f)
end

require 'feedparser'
require 'feedparser/html2text-parser'

#iterate the feeds
feeds.each{ |sf|
puts 'Loading '+sf.url + ' ...'
begin
res=fetch(sf.url,sf.date)
s= res.body
rescue
puts "Error getting "+ sf.url
end

begin
#puts res['Date']
mod_date = res['Last-Modified']
puts mod_date
#URI::parse(sf.url), header
rescue
puts res
puts "Error: Web server doesn't implement date: "+ sf.url
end

begin
f = FeedParser::Feed::new(s)
if( f != nil and f.title!=nil )
puts(f.title)
else
puts("Title not available")
end
rescue
puts "Error parsing feed, Not valid: " + sf.url + "\n" +s
end
#Feeds are usually in temporal reverse order
if(f!=nil and f.items!=nil)
f.items.reverse_each {
|i|
#title, date, content
begin
if((i.date ==nil or sf.date==nil or i.date>sf.date) )
if(html_content == false and i.content != nil)

p = FeedParser::HTML2TextParser::new(true)
p.feed(i.content)
p.close
i.content = p.savedata
end
#TODO: -> content NULL
entry = ''
if i.title
entry+=i.title + " "# + i.date.to_s
end
if(i.content != nil)
entry+=i.content
end
puts(entry)
#puts ("Most recent date: " +sf.url+ " "+ i.date.to_s)
if(i.date!=nil)
sf.date=i.date
else
if (mod_date !=nil)
#If mod_date exists
sf.date=mod_date
end
end

if(train)
print(' (Interesting[Y/n]?)')
ans=STDIN.gets().chop!
print(ans)
if ans=='n'
filter.train_uninteresting(entry)
else
filter.train_interesting(entry)
end
else

puts(filter.classify(entry))# returns '[Un]interesting'
if(no_interactive==false)
STDIN.gets()
end
end
end
rescue
puts "Unknown Error"
end

}
end
}
serialize(filter, Filter_file)
serialize(feeds, Feeds_file)
Este hilo ha sido archivado. No pueden publicarse nuevos comentarios.
Mostrar opciones Umbral:
Y recuerda: Los comentarios que siguen pertenecen a las personas que los han enviado. No somos responsables de los mismos.
  • Yo lo hago a pelo

    (Puntos:2)
    por runlevel0 (1932) el Lunes, 29 Enero de 2007, 11:35h (#871568)
    ( http://www.flickr.com/photos/runlevel0/ | Última bitácora: Jueves, 01 Noviembre de 2007, 11:37h )
    Prefiero dar un barridó rápido a dos o tres lectores de feeds y quedarme con lo que más me interese.

    Nunca hay demasiados:

    * Estas en el curro, tienes internet... y claro, hay que hacer algo para rellenar esas horas que luego te pagarán. Así que hau, a leer feeds, entradas en las bitácora de BP, Google pages, feeds en el IE7, el Firefox, listas de correo en MS Live, en Gmail, en Yahoo... juer y aun así hay momentos en que te quedas sin nada y no tienes más remedio que trabajar. Cachis.

    --

    29A the Number of the Beast