リンクだけじゃなく、フォーム、イメージ、フレームまでがっつり収集してくれるクローラーが欲しかったんだけどwgetではできないようなので自作することにした。
フォームのフィールドを集めたりするの、ちょっと大変そうだな。。と思ったんだけど、WWW::Mechanizeというライブラリを使ったら超簡単だった。ビバMechanize!
require "rubygems" require "mechanize" class CrawlerListener def notify_begin end def pre_request end def notify_response(result) puts %Q{#{result[:method]} #{result[:uri]} #{result[:query] ? result[:query].inspect : ""}} end def post_request end def notify_end end end class WebCrawler PRODUCT = "BitArts Crawler 1.0" attr_accessor :listener, :results, :excludes, :username, :password def initialize(listener=CrawlerListener.new) @listener = listener @excludes = /\.(jpg|png|gif|js|css|ico)$/i end private def create_agent agent = WWW::Mechanize.new @user_agent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; #{PRODUCT})" agent.user_agent = @user_agent agent.auth(username, password) agent end def http_request(method, uri, query={}) agent = create_agent begin if method == "GET" page = agent.get(uri, query) elsif method == "POST" page = agent.post(uri, query) end agent.page.encoding = "UTF-8" page rescue nil end end def get_links(page) links = [] (page.links + page.meta + page.frames + page.iframes).each {|link| begin if link.uri links << page.uri + link.uri end rescue URI::InvalidURIError end } imgsrcs = page.root.search("img").map{|e| e["src"]} imgsrcs.each {|uri| links << page.uri + uri } links end def child_uri?(uri) uri.to_s.index(@root_uri.gsub(/\/[^\/]*?$/, "/")) == 0 end def crawl_r(uri, referer=nil, query=nil, method="GET") return false unless child_uri?(uri) result = {} return false if uri.to_s =~ @excludes @listener.pre_request page = http_request(method, uri, query) @listener.post_request result = { :method => method, :uri => uri, :query => query, :referer => referer, :user_agent => @user_agent } if page result.update({ :code => page.code, :body => page.body, :header => page.response }) end @listener.notify_response(result) @results << result if page.is_a?(WWW::Mechanize::Page) links = get_links(page) links.each {|u| unless @uris.has_key?(u.to_s) @uris[u.to_s] = true crawl_r(u, uri) end } page.forms.each {|f| u = page.uri + f.action k = u.to_s + "?" + f.request_data unless @uris.has_key?(k) @uris[k] = true crawl_r(u, uri, f.build_query, f.method) end } end true end public def crawl(uri) @uris = {} @results = [] @root_uri = uri @listener.notify_begin crawl_r(uri.is_a?(URI) ? uri : URI.parse(uri)) @listener.notify_end true end end crawler = WebCrawler.new crawler.crawl("https://bitarts.jp/")