パーサーを作成するすべての人は、100のサイトを解析でき、100のサイトで数日間動けなくなることを知っています。 次の凍傷サイトの構造は任意に複雑になる可能性があり、圧縮されたjavascriptおよびajaxリクエストに関しては、それらを解読し、通常のcurlおよびregexpを使用して情報を抽出すると、情報自体よりも高価になります。
大雑把に言えば、問題はjavascriptがブラウザーでは実行されているが、サーバーでは実行されていないことです。 サーバー言語(
jParserおよびjTokenizer )のいずれかでjsインタープリターを作成するか、サーバーにブラウザーを配置してリクエストを送信し、最終的なdom-treeを取り出す必要があります。
古代、そのような場合、私たちはバイクを構築しました:別のマシンでブラウザーを起動し、サーバーで絶えずノックし、そこからジョブを受け取り(ジョブ)、サイト自体がiframeにロードされ、スクリプトがiframe domツリーを送り返しましたサーバー
現在、より高度なツール
-xulrunner (
crowbar )および
watirがあります。 1つ目は、ヘッドレスFirefoxです。 クローバーには
、必要なデータを視覚的に強調表示するためのff-pluginもあり
ます 。これにより、特別なparser-js-codeが生成されますが、Cookieはサポートされていませんが、終了することをためらいます。 Watirはデベロッパーによってデバッグツールとして位置付けられていますが、それを意図された目的に使用し、例として
travelocity.comからデータを
取得します。
Watirは、ブラウザーと対話するためのルビー宝石です。 異なるプラットフォーム用のバージョンがあります-watir、firewatir、safariwatir。 詳細な
インストールマニュアルにもかかわらず、Windowsとubuntの両方で問題が発生しました。 Windows(ie6)では、Ruby 1.9.1ではwatirは機能しません。 バージョン1.8.6をインストールする必要があり、それが機能しました。 ubuntでは-FireWatirが(またはfirefox経由の通常のwatir)動作するためには、ブラウザにjsshプラグインをインストールする必要があります。 しかし、
インストールページでFireWatirに提供されたバージョンは、Ubuntu 10.04上のFireFox 3.6では動作しませんでした。
jsshが機能しているかどうかを確認するには、
firefox -jssh
を実行してから、ポート9997(
telnet localhost 9997
)に何かを送信する必要があります。 ポートが開かない場合、またはfirefoxがクラッシュする場合(私のように)、jsshをビルドする必要があり
ます 。詳細なアセンブリ手順は
こちらです。
travelocity.comでホテルパーサーの作成を始めましょう。 たとえば、米国ニューヨーク州ニューヨークのすべてのホテルの今日の部屋の価格を選択してみましょう。 Ubuntu 10.4でFireWatirと連携します。
ブラウザを起動し、次のフォームでページをロードします。
require "rubygems"<br>require "firewatir"<br>ff = FireWatir::Firefox.new<br>ff.goto("http://www.travelocity.com/Hotels")<br>
フォームに必要な値を入力し、送信します。
ff.text_field(:id,"HO_to").val("New York, NY, USA")<br>ff.text_field(:id,"HO_fromdate").val(Time.now.strftime("%m/%d/%Y"))<br>ff.text_field(:id,"HO_todate").val(Time.tomorrow.strftime("%m/%d/%Y"))<br>ff.form(:name,"formHO").submit<br>
ダウンロードが完了するのを待っています:
ff.wait_until{ff.div(:id,"resultsList").div(:class,"module").exists?}<br>
wait_untilは非常に重要な命令です。 フォームを送信すると、サイトでいくつかのリダイレクトが行われ、その後、ajaxリクエストが行われます。 最終ページのロードを待つ必要があり、dom-treeでこの作業を行った後のみです。 ページが読み込まれたかどうかを知るにはどうすればよいですか? ajaxの実行後にページに表示される要素を確認する必要があります。 この場合、/ pub / gwt / hotel / esf / hotelresultlist.gwt-rpcへのリクエストの後、resultPageにいくつかの
<div class="module">
要素が表示されます。 それらが現れるまで待ちます。 text_field、submitなどの一部のコマンドにはすでにwait_untilが含まれているため、このコマンドはそれらの前に必要ないことに注意してください。
次に、ページナビゲーションを実行します。
while true do<br> ff.wait_until{ff.div(:id,"resultsList").div(:class,"module").exists?}<br> ...<br> next_link = ff.div(:id,"resultcontrol-top").link(:text,"Next")<br> if (next_link.exists?) then next_link.click else break end<br>end<br>
コードに省略記号がある場合、データの直接プルがあります。 watirを使用する誘惑があり、この場合、たとえば、このコマンドでresultsListのすべてのディーバを実行します。
ff.div(:id,"resultsList").divs.each.do |div|<br> if (div.class_name != "module") then next end<br> ...<br>end<br>
そして、各歌姫からホテル名と価格を引き出します:
m = div.h2(:class,"property-name").html.match(/propertyId=(\d+)[^<>]*>([^<>]*)<\/a[^<>]*>/)<br>data["id"] = m[1] unless m.nil?<br>data["name"] = m[2] unless m.nil?<br>data["price"] = div.h3(:class,"price").text<br>
しかし、これを行うべきではありません。 DOMツリーの要素に対する各watirコマンドは、ブラウザーへの追加の要求です。 それは私のために約一秒間働きます。 一度に同じ秒でdom全体を引き出して、通常のレギュラーで即座に解析する方がはるかに効率的です。
ff.div(:id,"resultsList").html.split(/<div[^<>]*class\s*=\s*["']?module["']?[^<>]*>/).each do |str|<br>m = str.match(/<a[^<>]*propertyId=(\d+)[^<>]*>([\s\S]*?)<\/a[^<>]*>/)<br> data["id"] = m[1] unless m.nil?<br> data["name"] = m[2] unless m.nil?<br> m = str.match(/<h3[^<>]*class\s*=\s*["']?price["']?[^<>]*>([\s\S]*?)<\/h3[^<>]*>/)<br> data["price"] = m[1] unless m.nil?<br>end<br>
必要な場合にのみwatirを使用することをお勧めします。 フォームに入力して送信し、ブラウザがjsコードを実行するのを待ってから、最終的なhtmlコードを受け取ります。 はい、watirを介した要素値へのアクセスは、dom構造なしでコードストリームを解析するよりも信頼性が高いようです。 他のディーバがいる可能性のあるあるディーバの内側を引き出すには、読みにくい正規表現を書く必要があります。 しかし、それでもはるかに高速です。 そのような歌姫が多い場合、最も簡単な解決策は、単純な再帰関数を使用して、すべてのコードをタグのネストレベルに分割することです。 私は
phpのクラスの1つでそのようなことを書きました。