#!/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