中年プログラマーの息抜き

ブログをはじめました。気の向くままにプログラム関連ネタをメモしていきます。

Pythonでスクレイピング(Selenium,Beautifulsoup,otp)

やりたいこと

pythonスクリプトを実行し、以下の順で自動処理するマクロを作る。

  1. ブラウザ(Chrome)をシークレットモードで開く
  2. amazonショッピングサイトに自分のアカウントでログイン認証
  3. あらかじめ選んでおいた商品をカートに入れる

実行環境

Pythonを使いますが、いくつかライブラリを追加しました。
・chromedriver-binary
selenium
・beautifulsoup4
・oathtool

早速、環境を作りましょう。
といっても、簡単に進めたいので、PIPを使います。

Pythonのバージョン

python --version
Python 3.9.7

PIPのバージョン

pip --version
pip 22.2.2

・・・うーん、では、最新化しておきましょうか。

python -m pip install --upgrade pip
pip --version
pip 23.2

Chromeのバージョン

ブラウザを開いてバージョンを調べますね。・・115.0.5790.99を確認

ライブラリ「chromedriver-binary」を追加

pip install chromedriver-binary==115.*

ERROR: Could not find a version that satisfies the requirement chromedriver-
・・・
112.0.5615.49.0, 113.0.5672.24.0, 113.0.5672.63.0, 114.0.5735.16.0, 114.0.5735.90.0)
ERROR: No matching distribution found for chromedriver-binary==115.*

エラーが出たので、一番近い「114.0.5735.90.0」を追加しましょう。

pip install chromedriver-binary==114.0.5735.90.0
pip list | grep chromedriver-binary
chromedriver-binary 114.0.5735.90.0

ライブラリ「selenium」を追加

pip install selenium
pip list | grep selenium
selenium           4.10.0

ライブラリ「beautifulsoup4」を追加

pip install beautifulsoup4
pip list | grep beautifulsoup4
beautifulsoup4     4.12.2

ライブラリ「oathtool」を追加

pip install oathtool
pip list | grep oathtool
oathtool            2.3.1

pypi.org
ちょっと最終更新が古いですが、Amazonの2段階認証はパスできた。ネット調べるとミドル的なoath-toolkitの情報が多く見つかりますね。(今回のネタでは使いません)

Python+Seleniumで2段階認証(6桁のパスコード)を突破する全手順 | たぬハック

ライブラリ「そのほかすべて」を最新で更新しておこう

pip-reviewを導入します。

pip install pip-review
pip list | grep pip-review
pip-review          1.3.0

 

パスを通します。(設定していなければ追加します)

set path=C:\Users\・・・\AppData\Roaming\Python\Python39\Scripts;%path%

 

まとめて最新で更新してみます。

pip-review --auto

 

私の環境だと依存がらみでエラーになりました。

RROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
fabric3 1.14.post1 requires paramiko<3.0,>=2.0, but you have paramiko 3.2.0 which is incompatible.
html5print 0.1.2 requires ply==3.4, but you have ply 3.11 which is incompatible.

1つずつ調整します。

まずは「ply」と「fabric3」

pip uninstall ply
pip install ply==3.4
pip install -U fabric3

 

続けて「paramiko」と「html5print」

pip uninstall paramiko
pip install paramiko==2.12.0
pip install -U html5print

 

2段階認証アプリ(oathtool)を登録していきます。

アカウント > ログインとセキュリティ > 2段階認証 > 管理ボタンからバックアップ手段を追加(新しい電話または認証アプリを追加)とリンクをたどります。

バーコードをスキャンできませんか? をクリックするとシークレットが表示されるのでこれを控えておき、OTPで利用します。

python -m oathtool "!!! your secret !!!"

999999

↑をワンタイムパスワードに入力して「ワンタイムパスワードを確認して次に進む」と2段階認証アプリ(oathtool)がアカウントに紐づきます。

2段階認証アプリ(oathtool)をソースに組み込む。

こんな感じで組み込めます。

totp = oathtool.generate_otp("!!! your secret !!!")

全体のソース(Python)はこんな感じです。

import time import oathtool from selenium import webdriver def request(url): global driver try: driver.get(url) time.sleep(3) except: pass def click(xpath): global driver try: el1 = driver.find_element(webdriver.common.by.By.XPATH, xpath) el1.click() time.sleep(3) except: pass def input(xpath, text): global driver try: el1 = driver.find_element(webdriver.common.by.By.XPATH, xpath) el1.send_keys(text) except: pass options = webdriver.ChromeOptions() options.add_argument('--incognito') options.add_argument('--headless') driver = webdriver.Chrome(options=options) request('https://www.amazon.co.jp/') click('//*[@id="nav-signin-tooltip"]/a') input('//*[@id="ap_email"]', '!!! your email !!!') click('//*[@id="continue"]') input('//*[@id="ap_password"]', '!!! your password !!!') click('//*[@id="signInSubmit"]') totp = oathtool.generate_otp("!!! your totp secret !!!") input('//*[@id="auth-mfa-otpcode"]', totp) click('//*[@id="auth-signin-button"]') request('https://www.amazon.co.jp/dp/・・・/') click('//*[@id="add-to-cart-button"]') driver.quit()

【C#】キーフック-左CapsLockを無効にして左Ctrlキーに偽装してみた

早速経緯から、

つい最近までHHKBキーボードを使っていましたが、パワポやワードなど資料作成時間が増えたことから矢印キー(↑→↓←)の利用頻度が増えます。

長時間作業すると 右手首が痛いんじゃ 状態

※HHKBでは、矢印キーは「Fn」割り当て

 

そんな理由から、手元でほこりをかぶっていたRealForce108UBKキーボードを使うようにしたのですが、これCapsLockと左Controlキーの割り当て変更が出来ません。

 

HHKBキーボードの左Ctrlキーに慣れきっているのでタイピング中の手の位置も中央付近が習慣付けされていて、こうなると左下にある左Ctrlキーを使うときの違和感が半端ないんですよね。さらに、間違えてCapsLockしてしまうことでイライラ

そして、長時間作業すると 左手首が痛いんじゃ 状態

 

キーボード買い換えも考えますが、そんな余裕もないしそもそもこんな理由で買い替えるなんていやだしなぁといった感じで我慢して何日か作業したんですが、どうにも慣れない機材を使うと作業時間に比例してイライラ

※少し使うと慣れてくるかと思いましたが、全然でした。

 

ということで、プログラム書いてCapsLockキー入力を無効にして使ってます。

※単に無効化するだけでは面白くなかったので「CtrlLockキー」にしてしまいました。

 

以下C#ソースコード

public static class KeyboardHook { [StructLayout(LayoutKind.Sequential)] private struct KBDLLHOOKSTRUCT { public uint vkCode; public uint scanCode; public uint flags; public uint time; public UIntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public UIntPtr dwExtraInfo; public int dummy1; public int dummy2; }; [StructLayout(LayoutKind.Sequential)] private struct INPUT { public int type; public KEYBDINPUT ki; }; [DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll")] private static extern uint SendInput(int nInputs, INPUT pInputs, int cbsize); private const int WH_KEYBOARD_LL = 0x000D; private static readonly IntPtr TRUE = new IntPtr(1); private static readonly IntPtr WM_KEYDOWN = new IntPtr(0x0100); private static readonly IntPtr WM_KEYUP = new IntPtr(0x0101); private static readonly IntPtr WM_SYSKEYDOWN = new IntPtr(0x0104); private static readonly IntPtr WM_SYSKEYUP = new IntPtr(0x0105); private static readonly INPUT CTRL_D = { new INPUT { type = 1, ki = new KEYBDINPUT() { wVk = 162, wScan = 0, dwFlags = 0, time = 0, dwExtraInfo = UIntPtr.Zero } } }; private static readonly INPUT[] CTRL_U = { new INPUT { type = 1, ki = new KEYBDINPUT() { wVk = 162, wScan = 0, dwFlags = 2, time = 0, dwExtraInfo = UIntPtr.Zero } } }; private static readonly int SZ_INPUT = Marshal.SizeOf(typeof(INPUT)); private delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static readonly KeyboardProc proc = Proc; private static IntPtr sHookId = IntPtr.Zero; private static bool sHoldCtrl = false; public static void Start() { if (sHookId != IntPtr.Zero) return; using (var p = Process.GetCurrentProcess()) using (var m = p.MainModule) sHookId = SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(m.ModuleName), 0); } public static void Stop() { UnhookWindowsHookEx(sHookId); sHookId = IntPtr.Zero; if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); } private static IntPtr Proc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode < 0) return CallNextHookEx(sHookId, nCode, wParam, lParam); if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); var vkCode = kb.vkCode; if (vkCode == 20) { SendInput(1, CTRL_D, SZ_INPUT); sHoldCtrl = true; return TRUE; } else if (vkCode == 240 || vkCode == 242) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); return TRUE; } else if (vkCode == 162) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); } } else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) { var kb = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); var vkCode = kb.vkCode; if (vkCode == 20 || vkCode == 240 || vkCode == 242) { if (sHoldCtrl) SendInput(1, CTRL_U, SZ_INPUT); return TRUE; } else if (vkCode == 162) { sHoldCtrl = false; } } return CallNextHookEx(sHookId, nCode, wParam, lParam); } }

KeyboardHookクラスリファレンス

CapsLockキー入力を無効化します。

CapsLockキー入力で状態を切り替えます。

 → Ctrlキーロック状態をエミュレート

 → Ctrlキーロック状態をリリース

  • KeyboardHook.Start() 
    グローバルフック(キーフック)開始

  • KeyboardHook.Stop()
    グローバルフック(キーフック)停止

Wordpress の記事を短く表示したい。語尾に三点リーダー(…)を付ける方法

f:id:tm-b:20161229140514p:plain

はじめに

ブログなどでタイトルや一覧画面のレイアウトを考えるとき、本文が長すぎたり画像が表示されてしまったりと、想定している枠内に文字が収まりきらなくなる場合があります。

PHPワードプレス)の投稿一覧画面と投稿内容画面との関係を考えていて投稿一覧画面にも簡単な説明文を表示したいことはありませんか? この場合、例えばカスタムフィールドなどを利用して、一覧表示文と投稿本文とをそれぞれで保存しておくのが簡単です。といっても面白くないので、今回は、投稿本文の中から100文字の一覧表示文を自動作成して、100文字を超えたところで語尾に三点リーダー(…)を付ける方法を紹介します。こうしておくと一覧表示レイアウトから簡単な説明が読み取れますし『あ、まだ続きがあるんだ…。』ということも伝わりますね。

まずは完成イメージ

今手伝っているフェスの準備でWordpressを使ってみました。(期間限定の紹介です)※レイアウトはデザイナーさんがきれいに仕上げてくれます。

onestead.xsrv.jp

目指すこと(実現したいこと)

PHPワードプレス)の投稿本文に設定した内容を一覧画面へ反映する

「テキストを含むタグ以外(img)などは除去する」

「文字数は上限(100文字)を設ける」

「文字数上限を超える場合、語尾に三点リーダーを付与する」

「XSERVER」環境(Wordpress)で動作する

PHPワードプレス)で語尾に三点リーダ

function.phpなどへ作成しておき表示用のPHPファイルから実行するだけです。

<?php echo(strimwidth_post_contents($post->post_content)); ?>

まとめ

ちょっとしたことですがヒューマンエラーを減らすことにもつながりますし、自動処理させるという視点は大切なのかなと感じます。REST化してJS経由から実行すればShopifyなどで活用するなど利用シーンはあるのかな。

DOMDocumentって何者?

ここで詳しく説明されてます。とても参考になります、ありがとうございます。

PHPでDOMを使ってHTMLをロード...

能古島フェスの紹介

W@F 福岡ワーケーションフェス2022

日本でもっともチルな島、能古島