J’ai besoin une version de quelques sites réalisés avec Rails en version statique pour une présentation offline.
J’aurai pu utiliser wget, mais franchement j’avais envie et besoin d’un outil plus personnalisable. Ou bien étendre les controller pour récupérer la sortie mais trop de problème et pas assez de souplesse. C’est là qu’intervient Anemone.
La mise en place est simple (via gem bien sûr), et la prise en main l’est tout autant, comme le montre l’exmple de leur site :
require 'anemone' Anemone.crawl("http://www.example.com/") do |anemone| anemone.on_every_page do |page| puts page.url end end
C’est assez rudimentaire, mais en couplant avec des outils puissant comme Hpricot, et une surcharge de la classe URI, on en sort un outil vraiment personnalisé et puissant.
J’ai pu réécrire les chemins statiques de mon code en chemin relatif pour une visualisation offline en surchargeant la classe URI
require 'uri' require 'rubygems' require 'anemone' require 'hpricot' OUTPUT_DIR = File.join(File.dirname(__FILE__), 'export', 'sites') module UriExt def last_item_of_path_is_used_for_filename self.query || (File.basename(self.path) =~ /^(.*)\..*$/) end def to_static_filename filename = '' if self.query if (File.basename(self.path) =~ /^(.*)\..*$/) filename = $1 else filename = File.basename(self.path) end filename += '-' + self.query_to_filename_ext + '.html' else if (File.basename(self.path) =~ /^(.*)\..*$/) filename = File.basename(self.path) else filename = 'index.html' end end filename end def query_to_filename_ext self.query ? self.query.gsub(/[=;&]/, '-') : nil end def build_relative_path(with_host=true) if self.path !~ /^\/$/ items = with_host ? [self.host] : [] original_path = (self.path =~ /^\/(.*)$/ ? $1 : self.path) items_original_path = original_path.split('/') items_original_path.shift if items_original_path.first == '/' items_original_path.pop if self.last_item_of_path_is_used_for_filename items += items_original_path File.join(items) end end def complete_path_filename(with_host=true) items = [] relative_path = self.build_relative_path(with_host) items << relative_path if relative_path items << self.to_static_filename File.join(items) end def create_dir relative_path = self.build_relative_path if relative_path FileUtils.mkdir_p(File.join(OUTPUT_DIR, relative_path)) end end def create_file(content) self.create_dir File.open(File.join(OUTPUT_DIR, self.complete_path_filename), 'w') do |file| file.write(content) end end def build_relative_path_for_html if self.path !~ /^\/$/ (self.path =~ /^\/(.*)$/ ? $1 : self.path) else 'index.html' end end def root_back_relative_path(an_absloute_path) if self.path !~ /^\/$/ return File.join((['..'] * ((self.path.split('/')).length - 1)).join('/'), (an_absloute_path =~ /^\/(.*)$/ ? $1 : an_absloute_path)) else (an_absloute_path =~ /^\/(.*)$/ ? $1 : an_absloute_path) end end end URI::HTTP.send(:include, UriExt) URI::Generic.send(:include, UriExt) Anemone.crawl("http://www.example.com") do |anemone| # cache pages anemone.storage = Anemone::Storage.PStore('arnaudkozlinski.pstore') # avoid JPG crawl (just a particular case for my website) # it's here only for th example anemone.focus_crawl { |page| page.links.select{ |uri| uri.path !~ /\.(jpg|JPG)$/ } } anemone.on_every_page do |page| puts page.url # if craw a html page parse and modify it with Hpricot if page.headers['content-type'].join('') =~ /^text\/html/ doc = Hpricot(page.body) # image source rewriting doc.search('img').each do |img| if img.attributes['src'] uri_img = URI.parse(img.attributes['src']) img['src'] = page.url.root_back_relative_path(uri_img.path) end end .... # stylesheet source rewriting doc.search('link').each do |script| if script.attributes['href'] uri_script = URI.parse(script.attributes['href']) script['href'] = page.url.root_back_relative_path(uri_script.path) end end .... end end
Petit bémol, reste le parsing des feuilles de style CSS pour la réécriture des url() vers les images de style. Mais comme les url des feuilles de style sont déjà en relatif (c’est ma règle en tout cas), je n’ai pas eu besoin de parser, juste copier les images au bon endroit. D’autant que je n’ai pas trouvé le Hpricot du CSS.
Ça peut paraître long à première vue, mais quand on maîtrise Ruby, c’est largement moins prise de tête que d’utiliser un web spider automatique, car il y a toujours des cas particuliers et par expérience aucun ne permet d’obtenir un résultat parfait.