サイトをクロールしてsitemap.xmlを作るライブラリ
Googleウェブマスターツールやら、Yahoo!サイトエクスプローラーやらのためにsitemap.xmlをつくろうとおもいたった。
動的サイトなら、みんなたぶん自動でsitemap.xmlを作るようにしてるんだろうけど、静的ページがあるとそうはいかない。
いくつかツールはあるけど、
- サイトルート以下のファイルを検索してファイルを作ってくれるツール →公開したくない場合だってある
- Webアプリで提供 →大きなサイトだと制限で全部拾えない
ので、簡単につくってみた。
汚いし、やってることは簡単のなので勝手にもってって煮るなり焼くなりどうぞ。ほしいひとだけ。
# sitemap.rb # サイトマップ構築 require 'digest/md5' require 'uri' require 'net/http' require 'net/https' require 'parsedate' require 'rubygems' require 'hpricot' require 'builder/xmlmarkup' class SiteMap attr_reader :domains, :links def initialize(url, domains = []) @site = URI.parse(url) @domains = domains.dup @domains << @site.host if @domains.empty? end def build(depth = 10, &block) index = @site.to_s @links = { index => { } } # {url => headers} @pages = [] @excepts = [] crawl(index, depth, &block) @excepts.each { |except| @links.delete(except) } @links end def to_s xml = Builder::XmlMarkup.new(:indent => 2) xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8" xml.urlset( 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation' => "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" ) do @links.each do |page, response| time = Time.mktime(*ParseDate.parsedate(response['last-modified'] || Time.now.to_s)) xml.url do xml.loc(page) if time > Time.now - 86400 * 2 xml.changefreq('daily') else xml.changefreq('weekly') end xml.priority('0.7') xml.lastmod(time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")) end end end xml.target! end def save(path) File.open(path, 'w') do |file| file.write self.to_s end end private # targetで指定したページを開き、含まれるリンクを巡回する def crawl(target_url, depth = 10, redirection_limit = 10, &callback) return if depth == 0 raise ArgumentError, 'http redirect too deep' if redirection_limit == 0 target = URI.parse(target_url) http = Net::HTTP.new(target.host, target.port) http.use_ssl = true if target.scheme == 'https' http.start do |request| begin callback.call(target_url, depth) if block_given? response = request.get(target.request_uri) case response when Net::HTTPSuccess digest = Digest::MD5.hexdigest(response.body) if response['content-type'] !~ /text|html/ || @pages.include?(digest) @excepts << target_url next end @pages << digest response.each { |key, value| @links[target_url][key] = value} doc = Hpricot(response.body) links = [] doc.search('a[@href!=""]').each do |linktag| uri = target.merge(URI.parse(linktag.attributes['href'])) domains.each do |domain| if uri.host =~ /#{Regexp.quote(domain)}$/ link = uri.to_s.sub(/#.*$/, '') if !@links.keys.include?(link) && !@excepts.include?(link) && !links.include?(link) links << link @links[link] = {} end break end end end links.each do |link| crawl(link, depth - 1, redirection_limit, &callback) end when Net::HTTPRedirection @excepts << target_url crawl(response['location'], depth, redirection_limit - 1, &callback) end rescue Exception end end end end
使い方
require 'sitemap' sitemap = SiteMap.new('http://www.e-tsuyama.com', ['e-tsuyama.com']) # ドメインに「e-tsuyama.com」を含む(後方一致)場合同じサイト内と判断 sitemap.build { |url, depth| puts ' ' * (10 - depth) + url } sitemap.save('sitemap.xml')