modalsoul’s blog

これは“失敗”と呼べるかもしれないが、ぼくは“学習体験”と呼びたい

Headlessモード時、ChromeDriverでファイルダウンロードに失敗した話

ChromeDriverでファイルダウンロードするSeleniumスクリプトを書いたとき、非Headlessモードでは成功するのに、Headlessモードで失敗した件のメモ

tl;dr

Headlessモードの場合、

  • target=_blankなアンカーをclick()してもファイルダウンロードできない
  • target属性を削除してclick()するとダウンロードできる

前準備

基本は前の記事と同じです

modalsoul.hatenablog.com

当たり前ですが、docker上では非Headlessモードでは動作させられないので注意

スクリプト

コマンドライン引数でHeadlessモードのON/OFFを替えられるようにしています

検証

ex.) 非Headlessモードで動作

python scraping.py

ex.) Headlessモードで動作

python scraping.py --headless=True

非Headlessモードでは、sample_zengin.csvがダウンロードできますが、Headlessモードではダウンロードされません。

対処

↓では、同じくアンカーをclick()することでファイルダウンロードに成功しています

modalsoul.hatenablog.com

この違いは、ダウンロードに失敗したページのアンカーにはtarget=_blankがあったことで

target属性を削除し、アンカーをclickするようにします

変更点は、L.35の追加のみです

これで、Headlessモードでもファイルダウンロードができようになりました


あまり良い例じゃないですが、挙動から原因を切り分けにくくなかなか面倒だったので、覚えておくといいことあるかも

HeadlessモードのChromeDriverでファイルダウンロードに失敗する現象のトラブルシューティング

ファイルのダウンロードをするSeleniumスクリプトを、docker上でheadlessモードのChromeDriverで動作させた際、ファイルのダウンロード処理に失敗する現象が起きたので、その対処方法のメモ書き

前準備

基本的なところは前回と同じ modalsoul.hatenablog.com

ファイルのダウンロードをする処理を追加しています(と言っても、anchorをクリックするだけですが

ダウンロード先ディレクトリを掘って

> make scraping
> docker-compose run scraping /bin/sh
> python scraping.py

のような感じで実行できます

現象その1

headlessモードで動作させると、ファイルのダウンロードがされない

  • スクリプトが正常終了しても、保存先パスにファイルが存在しない
  • dockerではなくローカルのmacで非headlessモードで実行すると成功する

対処

ggってこれを見つけました

python - Downloading with chrome headless and selenium - Stack Overflow

セキュリティの制約?で失敗しているようで、chromedriverのコマンドにPage.setDownloadBehaviorを追加する必要があるようです

スクリプトを以下のように変更しました

これを実行すると、今度は別のエラーが

現象その2

selenium.common.exceptions.WebDriverExceptionで失敗する

/opt/app/scraping # python scraping.py
Traceback (most recent call last):
  File "scraping.py", line 33, in <module>
    main()
  File "scraping.py", line 23, in main
    enable_download_in_headless_chrome(driver, DOWNLOAD_PATH)
  File "scraping.py", line 11, in enable_download_in_headless_chrome
    driver.execute("send_command", params)
  File "/usr/local/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/usr/local/lib/python3.6/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: unknown error: unhandled inspector error: {"code":-32601,"message":"'Page.setDownloadBehavior' wasn't found"}
  (Session info: headless chrome=61.0.3163.100)
  (Driver info: chromedriver=2.30 (30087a73289cd80b9611f658ed81448badaf549e),platform=Linux 4.9.93-linuxkit-aufs x86_64)

原因

Chromedriverのバージョンが古いために、Page.setDownloadBefaviorに対応していなかったようです

この時点でのChromedriverのバージョンは

/opt/app/scraping # chromedriver -v
ChromeDriver 2.30 (30087a73289cd80b9611f658ed81448badaf549e)

対処

alpine3.8を使います

これで2.38がインストールできました

/opt/app/scraping # chromedriver -v
ChromeDriver 2.38 (f91d32489882be7df38da3422a19713bfd113fa5)

スクリプトを実行し、無事ファイルのダウンロードが確認できました


これ以外にもいくつかエラーに遭遇したので、また別にまとめようと思います

Raspberry Pi Zero WHでリレーモジュールを使って電源制御

Raspberry Pi Zero WHで、大きい電圧(ex. 12V)で動作する機器の電源を制御するために、リレーモジュールをつなげてみた。1

リレーモジュール

Raspberry Piでの使用例が多かったこのリレーモジュールをポチった

SODIAL(R) リレーモジュール

SODIAL(R) リレーモジュール

2チャンネルにしたのはなんとなくです

接続機器

リレーモジュールに繋いで動作確認に使ったもの

ケースファン

リレーモジュールで動かす機器は、余っていたケースファンを使用

USBケーブル

USBケーブルでファンに給電し、今回はこれをリレーモジュールで制御する

配線

Raspberry Pi Zero リレーモジュール
5V JD-VCC
GPIO14 VCC
GND GND/GND/IN1/IN2

ファン <-> リレーモジュールCOM1 電源 <-> リレーモジュールNO1 ファン <-> 電源

動作確認

コマンドライン

pi@raspberrypi:~$ echo 14 > /sys/class/gpio/export
pi@raspberrypi:~$ echo out > /sys/class/gpio/gpio14/direction
pi@raspberrypi:~$ echo 1 > /sys/class/gpio/gpio14/value
pi@raspberrypi:~$ echo 0 > /sys/class/gpio/gpio14/value

これで、ファンの電源がON->OFFにかわります

プログラム

pythonで制御するコードを書くとこんな感じ

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.OUT)
GPIO.output(14, True)
time.sleep(3)
GPIO.output(14, False)
GPIO.cleanup()

Raspberry Pi Zero WHでサーボモーターSG92Rを動かす

Raspberry Pi Zeroでサーボモーターを動かしてみた1

SG92R

SG90の使用例が多いですが、今回はSG92Rを使用。特に理由はなし。

マイクロサーボ SG92R (1個)

マイクロサーボ SG92R (1個)

配線

真ん中の赤を、5V

茶色を、GND

黄色を、GPIO4

プログラム

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

gp_out = 4
GPIO.setup(gp_out, GPIO.OUT)

servo = GPIO.PWM(gp_out, 50)

servo.start(0)
time.sleep(1)

angle = (12 - 2.5)/180
for i in range(180):
  servo.ChangeDutyCycle(2.5+i*angle)
  time.sleep(0.1)

for i in range(180):
  servo.ChangeDutyCycle(12-i*angle)
  time.sleep(0.1)

servo.stop()
GPIO.cleanup()

動作確認

Raspberry Pi Zero WHとMPU9250で、9軸センサー(ジャイロ、加速度、磁気)を取得する

9軸センサーが安かったので、ポチってRaspberry Pi Zeroと繋いでみた1

MPU9250

はんだ付け

付属のピンをはんだ付け

小手とはんだ、はんだ吸取線、簡易こて台がセットになったものをポチった [asin:B0072QN66U:detail]

はんだ付けなんて小学校以来?くらいだけど、なんとかなった

配線

Raspberry Pi Zero MPU9250
3V3 VCC
GPIO02 SDA
GPIO03 SCL
GND GND

I2Cの有効化

sudo raspi-config
  • 5 Interfacing Options
  • P5 I2C
  • YES

でI2Cを有効化

バイスのチェック

I2Cデバイスとして認識されているかをチェック

i2ctoolsのインストール

sudo apt install i2c-tools

I2Cデバイス一覧の表示

sudo i2cdetect -y 1

これで68が表示されていればOK

pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- 0c -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

プログラム

pipのインストール

sudo apt install python-pip python-smbus

ライブラリのインストール

github.com

sudo pip install FaBo9Axis_MPU9250

動作確認

import FaBo9Axis_MPU9250
import time

mpu9250 = FaBo9Axis_MPU9250.MPU9250()

while True:
  accel = mpu9250.readAccel()
  print('accel:' + str(accel))
  gyro = mpu9250.readGyro()
  print('gyro:' + str(gyro))
  magnet = mpu9250.readMagnet()
  print('magnet:' + str(magnet))
  time.sleep(0.1)
pi@raspberrypi:~$ python scan.py
accel:{'y': 0.101, 'x': -0.957, 'z': -0.111}
gyro:{'y': -0.488, 'x': 3.143, 'z': 0.771}
magnet:{'y': 50.33, 'x': -43.91, 'z': 36.58}
accel:{'y': 0.1, 'x': -0.958, 'z': -0.108}
gyro:{'y': -0.328, 'x': 3.059, 'z': 0.801}
magnet:{'y': 0, 'x': 0, 'z': 0}
...

これでセンサーの値が取得できた

Raspberry Pi Zero WHでGPIOを使って音声出力

Raspberry Pi Zeroにはオーディオジャックがないので、GPIOに繋いで使えそうなスピーカーを用意した


配線

スピーカーをそれぞれ、GPIO 18(右)とGPIO 13(左)とGroundへ接続

GPIO設定変更

pi@raspberrypi:~ $ gpio -g mode 18 ALT5
pi@raspberrypi:~ $ gpio -g mode 13 ALT0
pi@raspberrypi:~ $ gpio readall
 +-----+-----+---------+------+---+-Pi ZeroW-+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | ALT5 | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 1 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 | ALT0 | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+-Pi ZeroW-+---+------+---------+-----+-----+

Audio設定

pi@raspberrypi:~ $ sudo raspi-config
  • Advanced Options
  • Audio
  • Force 3.5mm ('headphone') jack

を選択して終了

動作確認

pi@raspberrypi:~ $ aplay /usr/share/sounds/alsa/Front_Center.wav
Playing WAVE '/usr/share/sounds/alsa/Front_Center.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono

このサンプルはモノラルなので、左右の動作確認をするには適当なステレオ音源で

ちなみにaplayは.mp3や.m4aは再生できなかったので、mplayerをインストール

sudo apt-get install mplayer

音量は控えめだけど、とりあえずOK

Raspberry Pi Zero WHにブート/シャットダウンの物理ボタンを付けた

Raspberry Pi Zeroをポチったので、ちょっとずついじってみる

OSはRaspbian stretch lite www.raspberrypi.org

初期設定諸々はここでは割愛


物理スイッチを取り付ける

シャットダウンするためにsshするのはダルいので、安全に起動・停止できる物理ボタンを付けます

こちらを参考にさせてもらいました

Raspberry Pi 3 にシャットダウン/リブート/再開ボタンを追加する - はむ!の空想具現化したいブログ

RaspberryPiにshutdownボタンを付けよう

配線

スイッチとGPIO18とGPIO3、GNDを接続

シャットダウンスクリプト

/home/pi/service/shutdownd.py

#!/usr/bin/python
# coding:utf-8
import time
import RPi.GPIO as GPIO
import os

GPIO.setmode(GPIO.BCM)
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)

while True:
    GPIO.wait_for_edge(18, GPIO.FALLING)
    sw_counter = 0

    while True:
        sw_status = GPIO.input(18)
        if sw_status == 0:
            sw_counter = sw_counter + 1
            if sw_counter >= 50:
        os.system("sudo shutdown -h now")
                break
        else:
            break

        time.sleep(0.01)

systemd設定

サービスファイル

/usr/lib/systemd/system/shutdownbuttond.service

[Unit]
Description=Shutdown Daemon

[Service]
ExecStart =/home/pi/service/shutdownd.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

サービス有効化

pi@raspberrypi:~ $ systemctl enable shutdownbuttond.service

デーモン再起動

pi@raspberrypi:~ $ sudo systemctl daemon-reload

これで起動時にスイッチ長押しでシャットダウン、停止時にスイッチで起動できるようになった