2011年3月31日木曜日

はじめに


XXMLサイトマップ(sitemap.xml)をトップページに配置する事で検索エンジン(google等)にページの情報を通知する事が出来ます。
必ずクロールされるとは限りませんが検索エンジンのインデックスに登録するURLの候補にはなります。
Webクローラ(Anemone)を利用して 簡易 simap ジェネレータを作ってみました。ページが多いサイトでは実行する時にはサーバに負荷が掛かりますので注意が必要です。

ソースコード


pythonで書いた場合

Anemone(Webクローラフレームワーク)から取得したURLの結果を使用してpythonでsitemap.xmlに加工しています。
コマンドを組み合わせる shell scriptに近い作り方かもしれません。

import datetime
import os

URLSET = {
          "xsi":"http://www.w3.org/2001/XMLSchema-instance" ,
          "schema":'''http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd''',
          "xmlns":"http://www.sitemaps.org/schemas/sitemap/0.9"
           }



def update_date():
    """
    更新日付を取得 ⇒ 現在日付で更新
    """
    return datetime.date.today().isoformat()

def create_sitemap_xml(url_list_path,sitemap_path):
    """
    サイトのurlのリストから サイトマップ(sitemap.xml)を作成します
    """
    sitemap_file = open(sitemap_path,'w')
    url_list_file = open(url_list_path,'r')
    
    #header 
    sitemap_file.write( '''
''' % (URLSET))
    sitemap_file.write(os.linesep)
    #body
    for url in url_list_file:
        sitemap_file.write('    %s' % (os.linesep))
        sitemap_file.write(        
        '''        %s
        %s
        daily
        0.5
''' % (url.strip(),update_date())
                           )
        sitemap_file.write('    %s' % (os.linesep))    
    #footer
    sitemap_file.write('')

if __name__ == "__main__":
    #sitemap.xmlを作成
    create_sitemap_xml("url_list.txt","sitemap_from_list.xml")

Rubyで書いた場合

依存)

anemone
builder XMLの作成の記述がシンプルに出来る。

Rubyではライブラリを使用している分シンプルで分かりやすいかもしれません。

ソース)

require 'rubygems'
require 'anemone'
require 'builder'


opts = {
  :storage => Anemone::Storage.MongoDB,
  :skip_query_strings => true,
}

sitemap = ""
xml = Builder::XmlMarkup.new(:target => sitemap, :indent=>2)

xml.instruct!
xml.urlset(:xmlns=>'http://www.sitemaps.org/schemas/sitemap/0.9',
           "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"
) {

  Anemone.crawl("http://work.blog.eggplant.org.uk",opts) do |anemone|
    anemone.on_every_page do |page|
      xml.url {
        xml.loc page.url
        xml.lastmod Time.now.strftime("%Y-%m-%d")
        xml.changefreq "daily"
        xml.priority 0.5
      }
    end
  end
 }
File.open('sitemap.xml', 'w') do |f|
  f.write sitemap
end

参考サイト

※ サイトマッププロトコル ⇒ sitemaps.org
Generate sitemap.xml using a ruby script

2011年3月28日月曜日

はじめに


Anemoneはrubyで作られたWebクローラフレームワークです。実行した環境は Max OS X 10.6 です。

特徴は

  • The multi-threaded design makes Anemone fast.
  • The API makes it simple. And the expressiveness of Ruby makes it powerful

です。
意訳するとマルチスレッド対応でAPIがシンプルなとことが売りという感じでしょうか。
クライアントのプログラムがシンプルに記述出来てCLIで実行出来るので他の(言語の)プログラムとの連携が取りやすい所が気に入り試してみる事にしました。

紹介記事:クローラーを作るためのフレームワーク「Anemone」

Anemoneのインストール


$ gem install anemone

lxml,lxslt2 関連でエラーが出た場合

libxml2 is missing.
========
ERROR: Error installing nokogiri:
ERROR: Failed to build gem native extension.

/usr/local/bin/ruby extconf.rb
checking for libxml/parser.h... no
-----
libxml2 is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----

上記のエラーが出た場合は nokogiri のサイトを参考に します。

私のサーバのOSは Cent OSですので下記を実行しました。

$ yum install -y libxml2 libxml2-devel libxslt libxslt-devel
$ gem install anemone

問題点


最初にサンプルページにある下記のプログラムで実行しました。

require 'anemone'

Anemone.crawl("http://www.example.com/") do |anemone|
  anemone.on_every_page do |page|
      puts page.url
  end
end

Anemoneはメモリーにデータを保存するため、ページが多い時に負荷が掛かりパソコン自体の動作にも影響が出ました。
方法がないかと探すとストレージをサポートしているとの記述があります。説明は本家のサイトの下記の辺りです。

By default Anemone stores all the pages it crawls in an in-memory hash. This is fine for small sites, and is very fast. How many pages it can handle will depend on the amount of RAM in your machine, but after a certain point you will want to start storing the pages on disk during the crawl.

ストレージの選択


Anemone がサポートしているストレージは下記の通りです。

  • Redis (>= 2.0.0)
  • MongoDB
  • TokyoCabinet
  • PStore

メモリ上のデータを書き込むという目的だけなので自分の興味で MongoDB を選択しました。

mongoのインストール・起動


(1) mongoのインストール

$ wget http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-1.8.0.tgz
$ tar xvfz mongodb-osx-x86_64-1.8.0.tgz
$mv mongodb-osx-x86_64-1.8.0 /usr/local/mongodb

(2) mongoの起動

バックグラウンドで起動

$ mkdir ~/tmp/mongodb
$ sudo /usr/local/mongodb/bin/mongod --dbpath ~/tmp/mongodb &

(3) Ruby ドライバのインストール

$ sudo gem install mongo
$ sudo gem install bson_ext

bson_ext は ruby 1.8.5 ではインストール出来ないようです。
CentOSのruby(1.8.5)で実行した時に下記サイトと同じ症状が発生しました。

gem install mongo_ext はruby-1.8.5 じゃダメでruby-1.8.7でおkだった

プログラム


require 'rubygems'
require 'anemone'

Anemone.crawl("http://www.example.com/") do |anemone|
  anemone.storage = Anemone::Storage.MongoDB
  anemone.on_every_page do |page|
    puts page.url
  end
end
追加したのは
anemone.storage = Anemone::Storage.MongoDB
だけ。

要望

クローラの処理で毎回全部のページを走査するのは無駄だし重いので、一回取得したサイトは一定期間処理しないとかの処理が欲しいです。折角ストレージに保存しているからあるはずと思ったのですが見当たらないようです。

crawlに指定出来るオプション


DEFAULT_OPTS = {
# run 4 Tentacle threads to fetch pages
:threads => 4,
# disable verbose output
:verbose => false,
# don't throw away the page response body after scanning it for links
:discard_page_bodies => false,
# identify self as Anemone/VERSION
:user_agent => "Anemone/#{Anemone::VERSION}",
# no delay between requests
:delay => 0,
# don't obey the robots exclusion protocol
:obey_robots_txt => false,
# by default, don't limit the depth of the crawl
:depth_limit => false,
# number of times HTTP redirects will be followed
:redirect_limit => 5,
# storage engine defaults to Hash in +process_options+ if none specified
:storage => nil,
# Hash of cookie name => value to send with HTTP requests
:cookies => nil,
# accept cookies from the server and send them back?
:accept_cookies => false,
# skip any link with a query string? e.g. http://foo.com/?u=user
:skip_query_strings => false,
# proxy server hostname
:proxy_host => nil,
# proxy server port number
:proxy_port => false,
# HTTP read timeout in seconds
:read_timeout => nil
}

を見ても無さそうです。storageはオプションで指定した方が良かったかも。。。

という事で、今回はクエリーストリングは対象には含めないのでその条件も含めて、

opts = {
:storage => Anemone::Storage.MongoDB,
:skip_query_strings => true,
}

Anemone.crawl("http://www.example.com/",opts) do |anemone|
#anemone.storage = Anemone::Storage.MongoDB
anemone.on_every_page do |page|
puts page.url
end
end

としました。