2020年7月4日土曜日

marionetteを使ってfirefoxを操作する

phantomjsからchromiumのheadlessモードに引っ越してからしばらく経ちましたが、やはりスクリプト実行後のDOMを取得するだけでは、やりたいことに足らないケースが多いです。
より広範囲のスクレイピングを考えるならば、ログインなどの自動化は必須です。

とはいえ、chromiumとSeleniumの組み合わせは心理的なハードルが高いし、どうしようかなと悩んでいたところ、ふとしたきっかけでfirefoxにもmarionetteという自動操作のフレームワークがあることを知りました。

今回はこのfirefoxのmarionetteを使って、より高度なブラウザの操作を試みてみたいと思います。

まず、marionetteですが、前述の通りfirefoxを操作する為のフレームワークです。
marionetteを有効にすると、firefoxは2828番ポートで待ち受け、操作の為のコマンドを受け付けるようになります。
クライアントは、このポートにコマンドを送り、ブラウザを操作します。
firefoxの開発者がブラウザのテストに利用しているとのことですので、廃れることはなさそうな雰囲気です。

ちなみに、Seleniumからもfirefoxを操作できますが、これはfirefoxのWebドライバであるGeckodriverが内部でmarionetteのコマンドに置き換えてfirefoxに送っています。
つまり、firefoxを操作する為だけにSeleniumを導入するなら、直接marionetteを叩いたほうがリーズナブルです。

mozillaから、marionetteを操作するための謹製Webドライバ(marionette_driver)が提供されています。
これを使ってpythonでmarionetteを制御してみたいと思います。

まず、はじめにfirefoxのインストールから、
FreeBSDでは、pkgを使って以下のようにインストールします。

pkg install firefox

次に、pythonがインストールされていなければ、pythonをインストールします。
私の環境だけかもしれませんが、marionette_driverはpython 2.7でないとうまく動きませんでした。

pkg install python27

次に、marionette_driverをインストールするために、pythonのパッケージ管理システムであるpipをインストールします。

pkg install py27-pip

最後に、pipを利用してmarionette_driverをインストールします。

pip-2.7 install marionette_driver

これで環境の構築は完了です。

インストールが完了したら、早速marionetteを試してみたいと思います。
まずは、marionetteを有効にしてfirefoxを起動します。

firefox -private -marionette -p "default" &

Xがない環境では、ヘッドレスモードで起動します。

firefox -headless -private -marionette -p "default" &

これでmarionetteが有効になり、2828番ポートでコマンドを受け付けるようになります。

marionette_driverのAPIリファレンスは以下の場所にあります。

https://firefox-source-docs.mozilla.org/python/marionette_driver.html

それほど大きいものではないですし、直感的に理解できるようにAPIが切られているので、私のようなpython初心者でもなんとかなるのではないかと思います。

とはいいつつも、すぐに試したい人のために、自作の自動ログインとDOM表示のサンプルコードをつけておきます。
自動ログインの方は、ログインするページのHTMLソースを見て、ログインIDとパスワードに割り当てられたnameを確認した上で適宜修正してください。

自動ログイン用スクリプト

import sys

from marionette_driver.marionette import Marionette
from marionette_driver.legacy_actions import Actions
from marionette_driver.errors import NoSuchElementException

gURL="https://www.kishiro.com/login/"
gID="id"
gPassword="password"

def findHTMLElement(aHTMLElements,aAttribute):
    tResult=None
    for tHTMLElement in aHTMLElements:
        if tHTMLElement.get_attribute("name")==aAttribute:
            tResult=tHTMLElement
            break
    return tResult

tCurrentBrowser=Marionette('localhost',port=2828)
tCurrentBrowser.start_session()
 
tCurrentBrowser.navigate(gURL)

try:
    tElements=tCurrentBrowser.find_elements("tag name","input")
except NoSuchElementException:
    tElements=None

if tElements is not None:
    tElementID=findHTMLElement(tElements,"id")
    tElementPassword=findHTMLElement(tElements,"password")
    tElementSubmit=findHTMLElement(tElements,"submit")

    tElementID.send_keys(gID)
    tElementPassword.send_keys(gPassword)

    tAsction=Actions(tCurrentBrowser)
    tAsction.click(tElementSubmit).wait(2)
    tAsction.perform()

tCurrentBrowser.delete_session()

DOM取得用スクリプト

import sys

from marionette_driver.marionette import Marionette
from marionette_driver.legacy_actions import Actions

gURL="https://www.kishiro.com/content/"

tCurrentBrowser=Marionette('localhost',port=2828)
tCurrentBrowser.start_session()
 
tCurrentBrowser.navigate(gURL)

print(tCurrentBrowser.page_source.encode('utf_8'))

tCurrentBrowser.delete_session()

「python2.7 sample.py」みたいな感じで1つ目のスクリプトを実行した後、2つ目のスクリプトを実行すると、ログイン後のページを取得できると思います。

0 件のコメント:

コメントを投稿