先日の続き。
Rubyで自動的にリンクを拾ってきてくるツールを作ったのだが・・・
もう少し具体的に処理内容を書く。
- 2万件近くのhtmlファイルをHTTPで取得。
- 取得したhtmlを解析し、リンクを見つる。
- ある一定条件のリンクであれば、さらにそこから飛んで、とんだ先のhtmlファイルを取得。
- その中にある特定条件のリンクを抽出する。
というものである。
このリンクをデータベース化しておく(サーバー側)。
こうすることの利点は2点ある。
- クライアントアプリが必要なデータにダイレクトにアクセスできるようにする。
- 将来URLの変更がおこなわれても、クライアント側の修正は不要。
まずはどこに時間がかかったのかを調べようと思って、プロファイラを使うことに決めた。
Rubyのプロファイラには標準でついてくるprofileと、それよりも高速なruby-profがある模様。
profile – Rubyリファレンスマニュアル
ruby-prof
はじめは高速なということに引かれてruby-profをインストール
sudo gem install ruby-prof
使用方法は
ruby -runprof hoge.rb
だそうなので、実際にやってみたところ
ruby: no such file to load -- unprof (LoadError)
と怒られてしまい、うまくいかなかった。
仕方がないので、標準のprofileを使う。
使い方は
ruby -rprofile piyo.rb
今処理の全部をやってしまうと時間がかかりすぎるので100件に絞っておこなった。
プロファイルをやってみたところ(上位5つの処理を抜き出している)
% cumulative self self total time seconds seconds calls ms/call ms/call name 31.08 81.24 81.24 197 412.39 760.41 Hpricot.scan 7.87 101.82 20.58 18912 1.09 19.36 Hpricot.build_node 5.75 116.86 15.04 32238 0.47 9.89 Array#each 5.53 131.31 14.45 99767 0.14 0.19 Hash#[] 5.26 145.07 13.76 232541 0.06 0.06 Array#[]
こんな結果になって、(右端の% time全体時間のパーセンテージということであるから)が全体の40%ほどがhpricotがhtmlを解析する処理に使われていることがわかる。
プロファイルなしで実際の処理時間を測定してみると、35秒ぐらいかかった 1 。
どうも正規表現の処理は遅くなさそうだということで、(hpricotを使う前はhtml全体をパースしてしまっていたので、それを止めて)anchorタグだけをパースさせることにする。
参考はたのしいRubyのHTMLパーサー
AnchorTagRegexp = /<a\s+[^>]*(?:href=(?:"([^"]*)"|'([^']*)'|([^'"\s]*)))[^>]*>/mi response.body.scan(AnchorTagRegexp) { |tag| href = $1 || $2 || $3 }
結果は、、、同じぐらい。
やはり35秒ぐらいかかる。
ええい、こうなったらもうC++で書いてしまえということで書いてみた。
結果は、、、同じくらい、というかもっと遅い???(ぉぃ
単純にプログラムの書き方が悪いのか?
ちなみにC++ではlibxml2を使って書こうとしたところ、HTMLのパースでエラーがいっぱい出た。
どういうことかというと、たとえば<meta ・・・/>ではなくて、<meta・・・>となっているからタグの対応関係があっていないとか・・・。
厳密に見ないやり方はよくわからなかったので、仕方がないので上で使った正規表現を用いることにした。
使ったもの
- 正規表現にはboost::regex。
- HTTPでの通信部分はboost::asio。
- EUC-JP → UTF-8への変換はiconv。
たぶん、何で書いても速度が変わらないということはネットワークがボトルネックなんだということで、とりあえず今は納得した。
HTTPの同時接続数を増やすというやり方はどうかと思うので、やらないけれども(やっても4接続ぐらいにしておく)。
もう一つやるべきこととすれば、今はデータベースを丸ごと更新しているので(やり方としてはどうかと思う)、更新されている場合にだけリンクを深追いするという方式にすればもう少し速くなるかもしれない。
ただし、このデータベースにURLを入れておくというやり方は止めて、必要なデータをその都度引っ張ってくる方が色々な意味で優しいのではないかと思えてきた。
- エントリを書きながら、プログラムを調節すればいいのですが、あとでエントリを書いているため正確な数字がわからない [↩]