原因はreCAPTUCHA。こいつのせいでログインできず公開範囲が「自分のみ」のフォルダにある画像一覧をRSSから取得できない。
はじめに
かつて私は表題の件を解決していた。
しかし時と共に、はてなサイトの仕様が変更された。当時のコードではもはやログイン処理が失敗してしまう。その原因について調査したところ、reCAPTUCHAの仕業だと思われることが判明した。
そもそもAPIがクソ💩
はてなフォトライフFeedAPIがある。これで取得できればよかったのだが、できない。こいつで取得できるのはトップフォルダのみ。たとえば「Hatena Blog」など任意フォルダに配置された画像は取得できない。
しかたなくRSSで
なので、仕方なくはてなフォトライフのサイトにあるRSSリンクからRSSデータを取得することで一覧を取得する。
だが、フォルダの公開範囲が「トップフォルダと同じ」でなければならない。もし「自分のみ」などであった場合、RSSを参照するならログインしている必要がある。昔はログインできたが、今はreCAPTCHAに阻まれてログインできない。よって自分のみが参照できる画像のリンクを取得することができない。これが今回の本題である。
どうでもいいけど、私にはFeedAPIの存在意義がわからなかった。RSSの下位互換じゃん。
調査
ログインページ
HTMLコード
HTMLコードを見る。このうちログイン処理をするフォーム部分のみ抜粋する。
<form name="login" action="/login" method="post"> <div> <div class="error-message"> <p>時間を空けて再度お試しください。または<a href="https://hatena.zendesk.com/hc/ja">サポート窓口</a>にお問い合わせください。</p> </div> <div class="input-item"> <div class="input-item-inner"> <input placeholder="はてなID または メールアドレス" value="*******************" pattern=".{3,}" id="login-name" type="text" class="text" required="required" autofocus="autofocus" name="name"> </div> </div> <div class="input-item"> <div class="input-item-inner"> <input class="password" type="password" name="password" required="required" placeholder="パスワード" value="*************"> </div> </div> <div id="option" class="config-button"> <div class="auto-login"> <a href="#" class="checkbox-tab"><label for="auto_login"></label></a> <div class="checkbox-item"> <input type="checkbox" class="checkbox" checked="checked" name="persistent" value="1" id="auto_login" /> <label for="auto_login" class="checkbox-text"> <span>次回から自動的にログイン</span> </label> </div> </div> <input value="" name="recaptcha_token" type="hidden" /> <button type="button" id="login-button" class="submit-button">送信する</button> </div> </div> </form>
フォーム解析
- HTTP通信方法:
POST
- データ(
<input name>
)name
: はてなID または メールアドレスpassword
: パスワードpersistent
: 次回から自動的にログインするか否かrecaptcha_token
: 「私はボットではありません」チェックの進化版。ユーザが操作せずとも自動的にBOTチェックする。そのためにJavaScriptで実行することが必要である。
これらのデータを送信せねばならない。そのうちrecaptcha_token
というのが問題。
なにこれ? どうしたらいいの? とりあえず調査してみる。
reCAPTCHA
reCAPTUCHAとはBOTを防ぐための技術。自動ツールによる投稿を防ぐのが目的。
たぶんこいつのせい。
reCAPTUCHAのせいでログインするときJavaScriptの実行が必要になってしまった。今までのようにCookieの実装だけではダメ。
はてなフォトライフAPI
ふつうならやりたい処理にマッチしたWebAPIを使いたいところ。だが今回のはてなフォトライフAPIでは、公開範囲が「自分のみ」のフォルダを取得する術がない。トップフォルダの取得しかできない。APIがしょぼい。
なので仕方なくサイトからログインしてRSSを取得することでゲットしようと考えたわけだ。HTTP通信するプログラムを書いたが、ログインでreCAPTUCHAに阻まれてしまった。reCAPTUCHAの処理を動作させるためにJavaScript実行させるような処理が必要だ。
突破する方法
2Captcha
2Captchaという有料ツールによってrecaptchaを突破できるらしい。有料なのは嫌だし、ロシアというのも嫌だ。北方領土から出ていけ侵略者め。
Selenium
予めブラウザでログインをしておいて、そのプロファイルを読み込めば良い。
これなら半自動化はできそうだ。おそらくreCAPTUCHA対策はこれくらいしかないのだろう。
fp = webdriver.FirefoxProfile("/home/[ユーザー名]/.mozilla/firefox/[プロファイル名].default")
browser = webdriver.Firefox(fp)
既存プロフィールで運用する場合には悪意ある運用を避けるためのセーブ機構の為に、すでにSelenium以外から立ち上げられた同プロフィールのブラウザがある場合にエラーが吐かれるのでご注意を。
環境構築できず
ラズパイ4で環境構築に失敗した。selenium
をchrome
で使うためにchromedriver-binary
を入れたい。だが、arm32用バイナリがないと怒られた。
pip3 install selenium pip3 install chromedriver-binary
エラーログ
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting chromedriver-binary Downloading https://files.pythonhosted.org/packages/8b/c5/77a9822034f3e7c244dab99f92a7a4923ac4372623eca9bf683801c17003/chromedriver-binary-95.0.4638.17.0.tar.gz Building wheels for collected packages: chromedriver-binary Running setup.py bdist_wheel for chromedriver-binary ... error Complete output from command /usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" bdist_wheel -d /tmp/pip-wheel-yhdfg6ec --python-tag cp37: running bdist_wheel running build running build_py Downloading Chromedriver... Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py", line 84, in <module> cmdclass={'build_py': DownloadChromedriver} File "/usr/lib/python3/dist-packages/setuptools/__init__.py", line 145, in setup return distutils.core.setup(**attrs) File "/usr/lib/python3.7/distutils/core.py", line 148, in setup dist.run_commands() File "/usr/lib/python3.7/distutils/dist.py", line 966, in run_commands self.run_command(cmd) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/wheel/bdist_wheel.py", line 188, in run self.run_command('build') File "/usr/lib/python3.7/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/usr/lib/python3.7/distutils/command/build.py", line 135, in run self.run_command(cmd_name) File "/usr/lib/python3.7/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py", line 42, in run url = get_chromedriver_url(version=chromedriver_version) File "/tmp/pip-install-dgv6d01o/chromedriver-binary/chromedriver_binary/utils.py", line 57, in get_chromedriver_url raise RuntimeError('Could not determine chromedriver download URL for this platform.') RuntimeError: Could not determine chromedriver download URL for this platform. ---------------------------------------- Failed building wheel for chromedriver-binary Running setup.py clean for chromedriver-binary Failed to build chromedriver-binary Installing collected packages: chromedriver-binary Running setup.py install for chromedriver-binary ... error Complete output from command /usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-0w4awj1b/install-record.txt --single-version-externally-managed --compile --user --prefix=: running install running build running build_py Downloading Chromedriver... Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py", line 84, in <module> cmdclass={'build_py': DownloadChromedriver} File "/usr/lib/python3/dist-packages/setuptools/__init__.py", line 145, in setup return distutils.core.setup(**attrs) File "/usr/lib/python3.7/distutils/core.py", line 148, in setup dist.run_commands() File "/usr/lib/python3.7/distutils/dist.py", line 966, in run_commands self.run_command(cmd) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/usr/lib/python3/dist-packages/setuptools/command/install.py", line 61, in run return orig.install.run(self) File "/usr/lib/python3.7/distutils/command/install.py", line 589, in run self.run_command('build') File "/usr/lib/python3.7/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/usr/lib/python3.7/distutils/command/build.py", line 135, in run self.run_command(cmd_name) File "/usr/lib/python3.7/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py", line 42, in run url = get_chromedriver_url(version=chromedriver_version) File "/tmp/pip-install-dgv6d01o/chromedriver-binary/chromedriver_binary/utils.py", line 57, in get_chromedriver_url raise RuntimeError('Could not determine chromedriver download URL for this platform.') RuntimeError: Could not determine chromedriver download URL for this platform. ---------------------------------------- Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-dgv6d01o/chromedriver-binary/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-0w4awj1b/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-install-dgv6d01o/chromedriver-binary/
エラーログのうち以下がポイントと思われる。
RuntimeError: Could not determine chromedriver download URL for this platform. ---------------------------------------- Failed building wheel for chromedriver-binary
私の環境はRaspberry PI 4B。たぶんARMv7用バイナリがないのだろう。
うーん、バージョンは次々とあがっていくし、きっとその都度バイナリやインストール方法も変わるし時間もかかるだろう。きっとセキュリティ関係で最新バージョンにしないと動作しないとか、そういったことになりそうな予感。
面倒くさすぎる。これならブラウザのUI操作を自動化するテスト用ツールを使ったほうがいいかもしれない。でも今度はそれを調査する必要があるわけで。ああ面倒だ。
一旦ここで諦めよう。
妥協案
はてなフォトライフのフォルダの公開範囲を「トップと同じ」にすれば誰でもアクセスできるようになる。ログイン処理が不要になる。それで自動化できるようになる。本当は公開範囲を「自分のみ」のままにしておきたかったが、仕方ない。すべてはAPIの貧弱さが悪い。
所感
セキュリティは大事だが、自動化できないとかPC使う意味ない。reCAPTCHAで妨害するなら、せめてAPIを整えて欲しかった。まっとうなユーザが自分の情報をAPIで取得できない。あまりに残念すぎる現状。はてなさん、なんとかして。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux