Twilog がAPI枠を使い果たしたことにより自動更新を停止。翌月からの有料プラン対応を前に、しばらく 手動更新 が必要になったので、 Python の Selenium を使ってChromeやFirefoxでボタン押下操作の 自動化 を実現します。
Twilogの手動更新ボタンとは
ブラウザで自身のTwilogページを開いたら、開発ツールでページ右にある 最新の情報に更新する ボタンの要素を特定、その要素のXPATHを取得します。
1 |
/html/body/div/div/div[1]/div[2]/div[2]/aside/div[2]/div[1]/div/div/form/input |
手動更新ボタン付近のタグを抜粋すると、
1 2 3 4 5 6 7 8 9 10 |
<div id="side-update" class="css-1wummgi e13l4x533"> <div class="css-5dznxi e13l4x531"> <div class="css-1azakc e1cq0t8n0"> <form action="/ryan_j23/fetch" method="post"> <input type="submit" class="css-1igsnod e1cq0t8n2" value="最新の情報に更新する"> </form> <p class="css-1119bwt e1cq0t8n3"><span>last update 04/26 09:10</span></p> </div> </div> </div> |
となっていることから、 //div[@id='side-update'] の下層にある、 .//form/input[@type='submit'] を押下すれば良いことがわかります。
Ubuntu 18.04デスクトップのChromeで試行
お試しの実行環境は、仕事場で普段使いのUbuntu 18.04デスクトップ。Chromeブラウザもインストール済みです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user@WS-0300u:~$ uname -a Linux WS-0300u 4.15.0-52-generic #56-Ubuntu SMP Tue Jun 4 22:49:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux user@WS-0300u:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.6 LTS Release: 18.04 Codename: bionic user@WS-0300u:~$ google-chrome --version Google Chrome 124.0.6367.118 user@WS-0300u:~$ python3 --version Python 3.8.0 |
ブラウザ操作をプログラミングできる、Selenium WebdriverのPython版パッケージを pip より導入します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
user@WS-0300u:~$ /usr/bin/pip3 install -U selenium Collecting selenium Downloading https://files.pythonhosted.org/packages/04/4d/a6e8afd65b87372e275eb612d564ec68f79195e9b7e27004a3b2cce69686/selenium-4.20.0-py3-none-any.whl (9.5MB) Collecting typing_extensions>=4.9.0 (from selenium) Downloading https://files.pythonhosted.org/packages/01/f3/936e209267d6ef7510322191003885de524fc48d1b43269810cd589ceaf5/typing_extensions-4.11.0-py3-none-any.whl Collecting trio~=0.17 (from selenium) Downloading https://files.pythonhosted.org/packages/17/c9/f86f89f14d52f9f2f652ce24cb2f60141a51d087db1563f3fba94ba07346/trio-0.25.0-py3-none-any.whl (467kB) Collecting urllib3[socks]<3,>=1.26 (from selenium) Downloading https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl (121kB) Collecting certifi>=2021.10.8 (from selenium) Downloading https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl (163kB) Collecting trio-websocket~=0.9 (from selenium) Downloading https://files.pythonhosted.org/packages/48/be/a9ae5f50cad5b6f85bd2574c2c923730098530096e170c1ce7452394d7aa/trio_websocket-0.11.1-py3-none-any.whl Collecting sortedcontainers (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl Collecting outcome (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl Collecting idna (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl (66kB) Collecting sniffio>=1.3.0 (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl Collecting exceptiongroup; python_version < "3.11" (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/01/90/79fe92dd413a9cab314ef5c591b5aa9b9ba787ae4cadab75055b0ae00b33/exceptiongroup-1.2.1-py3-none-any.whl Collecting attrs>=23.2.0 (from trio~=0.17->selenium) Downloading https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl (60kB) Collecting pysocks!=1.5.7,<2.0,>=1.5.6; extra == "socks" (from urllib3[socks]<3,>=1.26->selenium) Downloading https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl Collecting wsproto>=0.14 (from trio-websocket~=0.9->selenium) Downloading https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl Collecting h11<1,>=0.9.0 (from wsproto>=0.14->trio-websocket~=0.9->selenium) Downloading https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl (58kB) Installing collected packages: typing-extensions, sortedcontainers, attrs, outcome, idna, sniffio, exceptiongroup, trio, pysocks, urllib3, certifi, h11, wsproto, trio-websocket, selenium Successfully installed attrs-23.2.0 certifi-2024.2.2 exceptiongroup-1.2.1 h11-0.14.0 idna-3.7 outcome-1.3.0.post0 pysocks-1.7.1 selenium-4.20.0 sniffio-1.3.1 sortedcontainers-2.4.0 trio-0.25.0 trio-websocket-0.11.1 typing-extensions-4.11.0 urllib3-2.2.1 wsproto-1.2.0 |
Selenium 4.20.0がインストールされました。
上述のSelenium Pythonプロジェクトページに載っているサンプルを参考に、ターミナルからChromeブラウザを操作してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user@WS-0300u:~$ python3 Python 3.8.0 (default, Dec 9 2021, 17:53:27) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> >>> from selenium import webdriver >>> from selenium.webdriver.common.by import By >>> >>> driver = webdriver.Chrome() >>> driver.get('https://twilog.togetter.com/ryan_j23') >>> element = driver.find_element(By.XPATH, "/html/body/div/div/div[1]/div[2]/div[2]/aside/div[2]/div[1]/div/div/form/input") >>> element.click() >>> drive.quit() |
この時、デスクトップ上にChromeブラウザのウィンドウが現れ、Pythonから手動更新ボタンを押すことができました。
次に、ブラウザウィンドウを開くこと無く操作可能な、ヘッドレスモードを試してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By import time ## Twitter Accounts usernames = ['ryan_j23'] ## Driver Options options = webdriver.ChromeOptions() options.add_argument('--headless') ## Driver Loading driver = webdriver.Chrome(options=options) ## Web Manipulation for u in usernames: driver.get('https://twilog.togetter.com/'+u) time.sleep(3) element = driver.find_element(By.ID, "side-update").find_element(By.XPATH, ".//form/input[@type='submit']") element.click() time.sleep(1) driver.quit() |
エラー以外は何も戻り値はないので、プロンプトが返ってくれば成功です。
1 2 |
user@WS-0300u:~$ ./twilog_updClicker.py user@WS-0300u:~$ |
Ubuntu 22.04デスクトップのFirefoxで試行
続いてシステム構成が少し異なる、Ubuntu 22.04デスクトップのFirefoxで試してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user@VPCSA26GGu:~$ uname -a Linux VPCSA26GGu 5.15.0-105-generic #115-Ubuntu SMP Mon Apr 15 09:52:04 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux user@VPCSA26GGu:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.4 LTS Release: 22.04 Codename: jammy user@VPCSA26GGu:~$ firefox --version Mozilla Firefox 125.0.2 user@VPCSA26GGu:~$ python --version Python 3.10.12 |
同様に pip でSeleniumパッケージのインストールを済ませたら、Firefoxのヘッドレスモードを試してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By import time ## Twitter Accounts usernames = ['ryan_j23'] ## Driver Options options = webdriver.FirefoxOptions() options.add_argument("--headless") ## Driver Loading driver = webdriver.Firefox(options=options) ## Web Manipulation for u in usernames: driver.get('https://twilog.togetter.com/'+u) time.sleep(3) element = driver.find_element(By.XPATH, "/html/body/div/div/div[1]/div[2]/div[2]/aside/div[2]/div[1]/div/div/form/input") element.click() time.sleep(1) driver.quit() |
ここまでの試行は拍子抜けするぐらい順調でしたが、問題はここから。
Ubuntu 20.04 AArch64で苦戦
ここまではいずれもテスト環境のデスクトップ機でしたが、最終的には終日安定稼働しているOracle Cloud上の仮想マシン上での、ヘッドレス運用を考えています。Firefoxをインストールし、 pip からSeleniumパッケージ導入を済ませました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ubuntu@ubnxc:~$ uname -a Linux ubnxc 5.15.0-1058-oracle #64~20.04.1-Ubuntu SMP Tue Apr 16 06:55:46 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux ubuntu@ubnxc:~$ lsb_release No LSB modules are available. ubuntu@ubnxc:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal ubuntu@ubnxc:~$ firefox --version Mozilla Firefox 125.0.2 ubuntu@ubnxc:~$ python --version Python 3.8.10 |
ターミナル上でPythonスクリプトを一行ずつ実行してみると、ドライバをロードするところで、AArch64アーキテクチャはサポート外とのエラーが。ちなみにこのインスタンスは、Oracle Crould VM.Standard.A1.Flexシェイプを使用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
ubuntu@ubnxc:~$ python3 Python 3.8.10 (default, Nov 22 2023, 10:22:35) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from selenium import webdriver >>> from selenium.webdriver.common.by import By >>> from selenium.webdriver.firefox.options import Options as FirefoxOptions >>> >>> options = FirefoxOptions() >>> options.add_argument("--headless") >>> driver = webdriver.Firefox() Traceback (most recent call last): File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/driver_finder.py", line 67, in _binary_paths output = SeleniumManager().binary_paths(self._to_args()) File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/selenium_manager.py", line 45, in binary_paths args = [str(self._get_binary())] + args File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/selenium_manager.py", line 83, in _get_binary raise WebDriverException(f"Unsupported platform/architecture combination: {sys.platform}/{arch}") selenium.common.exceptions.WebDriverException: Message: Unsupported platform/architecture combination: linux/aarch64 |
そこで、各ブラウザ向けwebdriverのインストール・更新に便利なwebdriver-managerパッケージを導入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
ubuntu@ubnxc:~$ pip install -U webdriver-manager Defaulting to user installation because normal site-packages is not writeable Collecting webdriver-manager Downloading webdriver_manager-4.0.1-py2.py3-none-any.whl.metadata (12 kB) Requirement already satisfied: requests in ./.local/lib/python3.8/site-packages (from webdriver-manager) (2.31.0) Collecting python-dotenv (from webdriver-manager) Downloading python_dotenv-1.0.1-py3-none-any.whl.metadata (23 kB) Collecting packaging (from webdriver-manager) Downloading packaging-24.0-py3-none-any.whl.metadata (3.2 kB) Requirement already satisfied: charset-normalizer<4,>=2 in ./.local/lib/python3.8/site-packages (from requests->webdriver-manager) (3.3.2) Requirement already satisfied: idna<4,>=2.5 in /usr/lib/python3/dist-packages (from requests->webdriver-manager) (2.8) Requirement already satisfied: urllib3<3,>=1.21.1 in ./.local/lib/python3.8/site-packages (from requests->webdriver-manager) (2.2.1) Requirement already satisfied: certifi>=2017.4.17 in ./.local/lib/python3.8/site-packages (from requests->webdriver-manager) (2024.2.2) Downloading webdriver_manager-4.0.1-py2.py3-none-any.whl (27 kB) Downloading packaging-24.0-py3-none-any.whl (53 kB) Downloading python_dotenv-1.0.1-py3-none-any.whl (19 kB) Installing collected packages: python-dotenv, packaging, webdriver-manager Successfully installed packaging-24.0 python-dotenv-1.0.1 webdriver-manager-4.0.1 |
上述のプロジェクトページに記載されているサンプルスクリプトを参考に、一行ずつ進めてみると、ドライバのロードでアーキテクチャを間違えてしまい、linux64版のドライバがインストールされてしまったことによるエラーに。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ubuntu@ubnxc:~$ python3 Python 3.8.10 (default, Nov 22 2023, 10:22:35) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from selenium import webdriver >>> from selenium.webdriver.firefox.service import Service as FirefoxService >>> from webdriver_manager.firefox import GeckoDriverManager >>> driver = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install())) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/firefox/webdriver.py", line 65, in __init__ self.service.start() File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py", line 98, in start self._start_process(self._path) File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py", line 208, in _start_process self.process = subprocess.Popen( File "/usr/lib/python3.8/subprocess.py", line 858, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "/usr/lib/python3.8/subprocess.py", line 1704, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename) OSError: [Errno 8] Exec format error: '/home/ubuntu/.wdm/drivers/geckodriver/linux64/v0.34.0/geckodriver' |
そこで、geckodriverは自分でダウンロードすることが可能なので、webdriver-managerのフォルダ構成に習い、linux-aarch64版を手作業で配置してみることに。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ubuntu@ubnxc:~$ tree .wdm .wdm ├── drivers │ └── geckodriver │ ├── linux-aarch64 │ │ └── v0.34.0 │ │ ├── geckodriver │ │ └── geckodriver-v0.34.0-linux-aarch64.tar.gz │ └── linux64 │ └── v0.34.0 │ ├── geckodriver │ ├── geckodriver-v0.34.0-linux64.tar.gz │ └── geckodriver.linux64 └── drivers.json ubuntu@ubnxc:~$ ll .wdm/drivers/geckodriver/linux-aarch64/v0.34.0/ -rwxr-xr-x 1 ubuntu ubuntu 10154760 Jan 3 05:41 geckodriver* -rw-rw-r-- 1 ubuntu ubuntu 3269989 Jan 3 17:42 geckodriver-v0.34.0-linux-aarch64.tar.gz ubuntu@ubnxc:~$ ll .wdm/drivers/geckodriver/linux64/v0.34.0/ -rwxr-xr-x 1 ubuntu ubuntu 10413536 Jan 3 05:38 geckodriver* -rw-rw-r-- 1 ubuntu ubuntu 3249164 May 1 23:56 geckodriver-v0.34.0-linux64.tar.gz |
更にwebdriver-managerの os_type 指定を試してみるも、思惑通りに振り向いてはくれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
ubuntu@ubnxc:~$ python3 Python 3.8.10 (default, Nov 22 2023, 10:22:35) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from selenium import webdriver >>> from selenium.webdriver import FirefoxOptions >>> from selenium.webdriver.firefox.service import Service >>> from selenium.webdriver.common.by import By >>> from webdriver_manager.firefox import GeckoDriverManager >>> from webdriver_manager.core.os_manager import OperationSystemManager >>> >>> options = webdriver.FirefoxOptions() >>> options.add_argument('--headless') >>> >>> service = Service(GeckoDriverManager(os_system_manager=OperationSystemManager(os_type="linux-aarch64")).install()) >>> driver = webdriver.Firefox(service=service, options=options) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/firefox/webdriver.py", line 65, in __init__ self.service.start() File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py", line 98, in start self._start_process(self._path) File "/home/ubuntu/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py", line 208, in _start_process self.process = subprocess.Popen( File "/usr/lib/python3.8/subprocess.py", line 858, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "/usr/lib/python3.8/subprocess.py", line 1704, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename) OSError: [Errno 8] Exec format error: '/home/ubuntu/.wdm/drivers/geckodriver/linux64/v0.34.0/geckodriver' |
万策尽きたところでwebdriver-managerによるお膳立てを諦め、Seleniumをドライバパス指定で動かすことでようやく成功。最終的に、以下のスクリプトに落ち着きました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver import FirefoxOptions from selenium.webdriver.firefox.service import Service from selenium.webdriver.common.by import By import time ## Twitter Accounts usernames = ['ryan_j23'] ## Driver Options options = webdriver.FirefoxOptions() options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument('--disable-dev-shm-usage') ## Driver Loading driver_path = '/home/ubuntu/.wdm/drivers/geckodriver/linux-aarch64/v0.34.0/geckodriver' service = Service(executable_path=driver_path) driver = webdriver.Firefox(service=service, options=options) ## Web Manipulation for u in usernames: driver.get('https://twilog.togetter.com/'+u) time.sleep(3) element = driver.find_element(By.ID, 'side-update').find_element(By.XPATH, './/form/input[@type="submit"]') element.click() time.sleep(1) driver.quit() |
ようやく機能するようになったPythonスクリプトファイルをcronへ登録し、ヘッドレスモードで毎日1回、手動更新ボタンを押してもらいます。
1 |
27 18 * * * ubuntu /home/ubuntu/twilog_updClickerFFaarch64.py |
最後に、今後Firefoxが更新されないように、 apt-mark でホールドしておきました。
1 2 3 4 |
ubuntu@ubnxc:~$ sudo apt-mark hold firefox firefox set on hold. ubuntu@ubnxc:~$ apt-mark showhold firefox |
本記事をまとめている間に月が変わり、Twilogの有料プランがサービスイン。
Python Seleniumで手動更新を自動化できてしまいましたが、お世話になっているサービスへの支援を込めて、有料プランを申し込もうと思います。