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つ目のスクリプトを実行すると、ログイン後のページを取得できると思います。

2020年6月20日土曜日

蘇る5.25インチFDD(但しIBMフォーマットのみ)

だいぶ前にハードオフで5.25インチのFDD(フロッピーディスクドライブ)を見つけ、購入してストックしていたのですが、今まで動作確認を怠っていたので、今日はこれを試してみたいと思います。


型番は「TEAC FD-55GFR 7393」で、PC/AT器用のドライブです。


ネット上の情報では、IBMフォーマットの2HD(1.2Mフォーマット、NECフォーマットと区別するために2HCとも呼ぶ)に対応しているようです。

動作確認に必要なメディアは、メルカリで購入した5.25インチFDの新品メディアを使って行いました。


また、マザーボードとの接続にはエッジコネクタ型のケーブルが必要ですが、これは別のハードオフで見つけておいたものを使います。


動作確認の最大の難関は、マザーボードが5.25インチFDDに対応しているかどうかです。
対応しているかどうかは、マザーボードのBIOSに入り、Drive AまたはBの選択肢に「1.2M」が表示されるか否かで確認できます。
家にあるマザーボードでは、Pentium 4世代のマザーボード(AOPEN AX4SG-UL)あたりまでは使えました。

MS-DOSのシステムを転送したUSBメモリから、formatコマンドでフォーマットし、適当なファイルを転送して読み書きできるかを確認しましたが、問題なしでした。

家にまた一つ、動くけど使わないガラクタが増えました。

2020年6月12日金曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア制作

まずはハードウェアの制作からですが、回路設計はそれほど難しくありません。

基本的に「青色7セグメントLEDシリアルドライバモジュール」のVcc、GND、SDI、SCLK、LATCHをRaspberry piの40ピン端子と繋げばよいです。
ドライバモジュール間は、Raspberry pi 2から見て手前のモジュールのSDOを次のモジュールのSDIに接続します。

また、7セグメントモジュール以外に、秒針として点滅するLEDを時分秒の間に2つずつ、計4つ挟みたいと思います。

回路図はこんな感じです。


方眼紙に書いてあるのは、秒針+7セグメントLED 2つ分の回路です。
これを左側に直列に繋げて、合計6つの7セグメントLEDをドライブします。

1つのGPIOで4つのLEDを点灯させるのはやや力不足な為、スイッチ使うトランジスタも追加しています。

最終的に、以下の部品を秋月電子で注文しました。
※ これ以外に、10KΩと100Ωの抵抗が2本ずつ必要です。

最終的に、こんな感じに実装しました。


Raspberry piと接続する端子は、
  • Vcc
  • SDI(シリアルデータ入力)
  • CLK(シリアルクロック入力)
  • BLK(秒針LED点滅用)
  • LATCH(7セグメントLED表示ラッチ用入力)
  • GND
の6端子になります。

2020年6月10日水曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - はじめに

今まで自分の部屋では、青色7セグメントLEDの電波時計を使っていたのですが、最近近所に高い建物が建ったためか、電波を受信しなくなり、時間が狂うようになってしまいました。
幸いにも、我が家には以前購入したままになっていたRaspberry pi 2があったので、NTPで正確な時刻を取得するのは容易です。
これを使って、電波が届かなくても狂わない時計を作ることにしました。


■表示部分に何を使うか?

アナログ時計はハードルが高そうなので、電子部品を使ってなんとかしてみたいと思います。
  • 7セグメントLED
  • ドットマトリクスLED
あたりがぱっと思いつきますが、ドットマトリクスLEDは制御が難しいし、ほぼダイナミック点灯(高速で点滅を繰り返す表示方法、人間の目には、残像により常時点灯しているように見える)しかできないので、7セグメントLEDをスタティック点灯駆動させて作ってみたいと思います。

■利用できそうな汎用ロジックIC

Raspberry pi 2にはGPIOが豊富にありますが、後先考えずにたくさん使ってしまうのももったいないので、汎用ロジックICを使って、使用するGPIOポートをなるべく節約してみたいと思います。
7セグメントLEDを駆動させるのに使えそうな汎用ロジックICをざっと洗い出してみました。

74HC4511

4bitの数値入力を7セグLEDの8出力に変換してくれる優れものです。
しかも、ラッチ回路があるので、一度入力した値を記憶してくれ、保持している間はRaspberry pi 2本体は他の処理をすることができます。
ただ、フォントは製造元によりまちまちで、「6」および「9」が5セグメントで点灯するものがあるので、意図した表示にするにはデータシートの確認が必要です。
また、「.」や、ABCDEFなどのアルファベット表示に対応していないのがイマイチです。
加えて、値段が他の汎用ロジックICよりやや高めです。

74HC137/74HC138

3入力→8出力のデコーダで、3bitの数値出力を8bit出力に変換してくれるICです。
74HC137はラッチ回路あり、74HC138はラッチなしの違いです。
7セグLEDの出力を保持する為に使うのではなく、ドライバICのセレクタに使う感じです。
これ1つで、3bit入力で8個の7セグメントLEDをセレクトすることができます。

74HC4514

4入力→16出力デコーダーで、4bitの数値出力を16bit出力に変換してくれるICです。
あまり生産されておらず、入手が難しいです。
74HC137/74HC138 2つで代用可能です。
74HC137/74HC138同様に、7セグLEDのドライバICのセレクタに使うのが良いかもしれません。

74HC595

ストレージ機能付きのシフトレジスタで、シリアル入力をパラレル出力に変換してくれるICです。
シリアル出力端子もあるため、連結して複数つなげることが可能です。
Raspberry pi 2からはSPIなどで比較的容易に駆動できます。

SPIが使えるなら、汎用ロジックICの総数を少なく済ませられる74HC595を7セグメントLED分直接に接続するのが一番リーズナブルです。

■便利な既製品

実は、74HC595と7セグメントLEDを駆動する為の抵抗やキャパシタがセットになった商品があります。


さすが秋月電子です、心得ていらっしゃいます。
今回は手っ取り早くこれを使いたいと思います。

2020年3月11日水曜日

Xubuntuの「システムプログラムの問題が見つかりました」のダイアログ表示対策、再び

家使っているXubuntuですが、最近また、
 システムプログラムの問題がみつかりました
のダイアログが表示されるようになりました。

「/var/crash」には、起動ごとに「_usr_lib_xorg_Xorg.0.crash」というダンプが吐かれているので、xorg関連の不具合だと思うのですが、一向に修正される気配がないので、クラッシュレポート自体を止めることにしました。

Ubuntu/Xubuntuでは、クラッシュレポート機能はapportで提供されていますので、これ自体を止めてしまいます。
apportの設定ファイルは「/etc/default/apport」ですので、この中にある「enabled=1」の記述を「enabled=0」に変更します。

多少乱暴な気がしますが、これでもう暫く様子を見てみることにします。

2020年1月12日日曜日

phantomjsからchromiumのheadlessモードへ引っ越す

今まで、livedoorブログやfc2ブログなどの定期監視にphantomjsを使っていました。
 本来ならwgetが使えればよいのですが、最近のブログサービスはjavascriptバリバリで、javascriptを解さないUAでは意味のあるHTMLソースを取得できません。
そんな中、FreeBSDのjail環境上で比較的軽量に動くphantomjsは大変重宝していました。

phantomjsはすでにプロジェクトが終了しているので、そのうち代替のソリューションに乗り換えなければ、とは思っていました。
そうこうしているうちに、phantomjsがFreeBSDのportsから削除されました、容赦なく。
メンテナンスのために、pkg update/upgradeしたら削除されてしまいました、マジか。

phantomjsが打ち切られた理由の一つに、chromeにheadlessモードが追加されたことがあるようです。
chromeは、FreeBSD向けにはビルド済みバイナリは提供されていませんが、chromeのコミュニティ版であるchromiumならばpkg形式で提供されています。

ということで、chromiumのheadless機能を使ってphantomjsの代替にしたいと思います。

google検索をかけると、seleniumやWebDriverに関して書かれた記事がたくさん引っかかるのですが、私が欲しいのは「javascriptを解するwget」ですので、chromiumだけでjavascript実行後のHTMLソースを取得してみたいと思います。

まずはchromiumの導入から。
pkg install chromium
これでchromiumがインストールされます。
インストールに伴い、chromium以外にも依存関係のあるライブラリ群も導入されます。
まっさらなjail環境にインストールすると、ライブラリ等含め、1GB程度のディスクスペースが必要なようです。

インストールが済んだら、以下のようにしてheadlessモードで動作するか確認します。
chrome --headless --disable-gpu --dump-dom https://www.kishiro.com/ > ./result.html
これでchromiumが持つDOMのデータをHTML形式で取得できます。

Xの設定とか色々いるのかと思いきや、普通にコマンドラインで動きますね。
確かにphantomjsの開発意義が失われてしまう感じかも。

なお、chromeはブラウザを終了させてもプロセスが残ることがあり、上記のようなコマンドをシェルスクリプトで連続して呼び出した場合、残ったプロセスによりchromeがうまく起動しない場合があります。
この場合には「--disable-background-mode」 を指定するとよいです。

ということで、chromiumのheadlessモードでphantomjsの代替はできそうです、よかった。

最後に、phantomjsの作者さん、今までどうもありがとう!