martes, 7 de julio de 2009

Mechanize y content-type unknown

En una aplicación, tengo que integrar un informe que se no puede obtener a partir de un Web Service o REST ni nada parecido, así que la única forma es haciendo login en una web y navegando hasta bajarse un CSV. La integración se hace sin problemas con Mechanize, pero me he encontrado con que recibo estas cabeceras en la página del CSV:

Content-Type: unknown; charset=UTF-16LE
Content-Disposition: attachment; filename=report.csv


El problema está en la forma en que Mechanize parsea los datos recibidos. Decide que clase va a utilizar en función del Content Type. Para texto y html utiliza la clase WWW::Mechanize::Page, y si no sabe que utilizar, por defecto utiliza WWW::Mechanize::File.

En mi caso como no reconoce el Content-Type unknown, me devuelve un objeto de tipo WWW::Mechanize::File, a partir del cual no puedo parsear correctamente el CSV, ya que me devuelve un objeto binario sin ningún método fácil para procesarlo.

La primera solución que se me ocurrió a partir de la documentación de la clase PluggableParser es añadir unknown en la lista de Content-Type soportados por Page

agent.pluggable_parser['unknown'] = WWW::Mechanize::Page

Pero eso no vale, porque dentro de WWW::Mechanize::Page hay un chequeo adicional:

response['content-type'] =~ /^(text\/html)|(application\/xhtml\+xml)/i

Así que la solución para mi caso ha sido crearme una nueva clase que extiende a Page en la que su initialize es tan simple (y tan ñapa) como llamar a super y ignorar la excepción de content type inválido


require 'mechanize'
module WWW
class Mechanize
class Unknown < WWW::Mechanize::Page
def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
begin
super(uri, response, body, code)
rescue Mechanize::ContentTypeError
# Do nothing
end
end
end
end
end


Y hacer que el Content-Type unkown lo procese esta clase

agent.pluggable_parser['unknown'] = WWW::Mechanize::Unknown

Y con esto arreglado. Ya me devuelve un objeto que puedo parsear sin problema con FasterCSV.

1 comentario:

  1. Bastante interesante, creo que esto me ayudará en un proyecto personal que dejé hace más de un año...

    ResponderEliminar