最近、ソファに座って、ウェブサイトから何かをダウンロードできる自分のクモをどうやって作りたいか考えました。 ただし、単純なダウンロードではなく、実際のかわいい種類のブラウザ(つまり、実行されるJavaScript)のようにダウンロードされている必要があります。
Selenium、PhantomJS、Splashなどの興味深いものが頭に浮かびました。 これらすべては私にとってはちょっとした引き込みでした。 私が特定した理由は次のとおりです。
- 実際のところ、私はJavaScriptがあまり好きではないので、お気に入りのpythonで書きたいと思っています。これはすでにJavaScriptのほとんどが機能しないことを意味します(または、どうにかしてそれらを接着する必要があります)。
- また、これらのヘッドレスブラウザーは、いつでも更新されます。
- しかし、Seleniumは非常に良いことですが、ページの読み込みを追跡する方法、または少なくともCookieを削除したり、要求したりする適切な方法が見つかりませんでした。 セレンの多くの愛好家がJavaScriptをページに挿入することを聞いたが、これは私にとってはワイルドです。約6か月前に、サイトからのJavaScript呼び出しを遮断し、クモを特定できるWebサイトを作成したからです。 私はそのような事件を本当に嫌います。 スパイダーをできるだけ正確にブラウザのように見せたいです。
実際には、ポイントに。 ヘッドレスChromeが最近リリースされました。 これは、クロームを既にクローラーとして使用していることを意味します(ただし、これは不正確です)。 ただし、クローラーとして使用するための通常のユーティリティは見つかりませんでした。 サードパーティのクライアントのリストからchrome-remote-interfaceのみを見つけました(残りは非常に退屈で一見完全に理解できませんでした)。
ドキュメントと完成したプロジェクトを実行し、誰も実際にPythonでクライアントを実装していないことを確認して、私は自分のクライアントを作成することにしました。
Chromeリモートデバッグのプロトコルは非常に簡単です。 開始するには、次のパラメーターを使用してChromeを実行する必要があります。
chrome --incognito --remote-debugging-port=9222 --headless
これで、 http://127.0.0.1:9222 / json /でAPIを使用できます。このAPIには、タブの管理に使用されるlist 、 new 、 activate 、 versionなどのメソッドがあります。
また、 http://127.0.0.1:9222/に移動するだけで、標準のWebデバッガーを完全に模倣する素晴らしいWebデバッガーに切り替えることができます。 Chromeのapishメソッドの動作を監視することは非常に便利です(右側のデバッグウィンドウはウィンドウ内でエミュレートされ、ブラウザーウィンドウはキャンバスに描画されます)。
実際、 リストタブに移動することで、タブと通信できるWebソケットのアドレスを見つけることができます。
次に、Webソケットを介して目的のタブに接続し、通信します。 できること:
- 実行リクエスト
- タブでイベントを購読する
数日かけて自分のために機能的に書いた後、私はそのようなライブラリを手に入れました。
中身は何ですか:
- 最新のプロトコルバージョンを自動的に交換する
- Pythonic wrapper protocol
- 同期および非同期クライアント(デバッグ用のみ同期)
- 便利なタブ抽象化を望みます
これはプログラムがどのように見えるかで、ページをロードし、リクエストに対する各レスポンスの長さを示します:
import asyncio import chrome_remote_interface if __name__ == '__main__': class callbacks: async def start(tabs): await tabs.add() async def tab_start(tabs, tab): await tab.Page.enable() await tab.Network.enable() await tab.Page.navigate(url='http://github.com') async def network__loading_finished(tabs, tab, requestId, **kwargs): try: body = tabs.helpers.unpack_response_body(await tab.Network.get_response_body(requestId=requestId)) print('body length:', len(body)) except tabs.FailReponse as e: print('fail:', e) async def page__frame_stopped_loading(tabs, tab, **kwargs): print('finish') tabs.terminate() async def any(tabs, tab, callback_name, parameters): pass # print('Unknown event fired', callback_name) asyncio.get_event_loop().run_until_complete(chrome_remote_interface.Tabs.run('localhost', 9222, callbacks))
ここでは、コールバックシステムを使用します。 最も興味深い: startとany :
- start(タブ、タブ)-起動時に呼び出されます。
- any(tabs、tab、callback_name、parameters)-コールバックリストでイベントが見つからなかった場合に呼び出されます。
- network__response_received(タブ、タブ、** kwargs)は、Network.responseReceivedライブラリイベントの例です。
私のコードは私には十分にエレガントであるように見え、プロトコルは少し湿っていますが、さらに使用します。 誰かが議論したい場合は、 Githubまたはメールでこちらに書いてください。
しかし、一つだけありましたが、そのために私はまだ泣いています。 リモートAPIを使用して、要求と応答をインターセプトおよび変更することはできません。 私がそれを理解しているように-これはmojoを介して可能であり、これによりchromeをライブラリとして使用できます。
しかし、不安定なクロムのコンパイルとPythonレイヤーの欠如は、私にとって大きな悲しみになると思いました(現在、開発プロセスにはC ++とJavaScriptがあります)。
記事がお役に立てば幸いです。 ありがとう