#!/usr/bin/env ruby require 'SVG/Graph/TimeSeries' require 'time' require 'net/http' require 'rubygems' require 'mongrel' #Thu Feb 1 15:44:59 PST 2007 #Keith Fahlgren # see http://kfahlgren.com:4117/graphs/rails_cookbook.svg for an example # and , for the scrape class Puller def self.data_from_url(url) begin data = nil uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) if url =~ /^https|:443\// # hmm this looks brittle http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end http.start{|h| query = uri.query ? "?" + uri.query : "" h.get2(uri.path + query, {"Accept" => 'text/xml'}) {|resp| puts resp.body if $DEBUG data = resp.body } } rescue => e raise ArgumentError.new("Url (#{url}) error! [#{e}]") end return data end def self.rexml_from_url(url) return REXML::Document.new(data_from_url(url)) end end # of class Puller class ScrapeGrapher < Mongrel::HttpHandler PORT = 4117 SERVER = "0.0.0.0" AMAZON_SCRAPE = "http://www.tupleshop.com/sales-rank.html" BOOK_TITLE = "Rails Cookbook" MAX_ITEMS = 100 def process(request, response) begin raw_data = Puller.data_from_url(AMAZON_SCRAPE).sub(/^

#{BOOK_TITLE} - Amazon Sales Rank<\/h1>\n\n/, '').split("
\n") ranks = [] # was map then flatten! but that won't scale raw_data.each_with_index {|x,i| # slow if (i % (raw_data.length / MAX_ITEMS)) == 0 ranks << x.sub(/ - .+$/, '') ranks << x.sub(/^.+ - /, '').sub(/,/, '').to_i end } title = "Sales Rank (lower better)" graph = SVG::Graph::TimeSeries.new({ :width => 1200, :height => 600, :graph_title => title, :show_graph_title => true, :no_css => true, :key => false, :scale_x_integers => true, :scale_y_integers => false, :min_x_value => ranks.first, :min_y_value => 1, :show_data_labels => true, :show_data_values => false, :show_x_guidelines => true, :show_x_title => true, :x_title => "Time", :show_y_title => true, :y_title => "Amazon Ranking", :y_title_text_direction => :bt, :stagger_x_labels => true, :x_label_format => "%m/%d/%y:%I%p", }) graph.add_data({ :data => ranks, :title => 'Amazon Sales Rank', }) response.start(200) {|head, out| head["Content-Type"] = "image/svg+xml" return out.write(graph.burn) } rescue => e puts "Server error (#{e}) [#{e.backtrace.inspect}]" end end end h = Mongrel::HttpServer.new(ScrapeGrapher::SERVER, ScrapeGrapher::PORT) h.register("/graphs/rails_cookbook.svg", ScrapeGrapher.new) trap("INT"){ h.stop } h.run.join