はじめに
beautifulsoup4でスクレイピングを行います。
インストールコマンド
参考リンク
Python Webスクレイピング テクニック集「取得できない値は無い」JavaScript対応@追記あり6/12 - Qiita
beautifulsoup4でスクレイピングを行います。
Python Webスクレイピング テクニック集「取得できない値は無い」JavaScript対応@追記あり6/12 - Qiita
WPF開発技術を復習します。(かなり忘れているため・・・)
以下が体系的にまとめられていていそうです
MVVMな設計のTips~サービスを作ってVMの依存性を排除~
jupyter Notebookのコードをexe化する方法を記録します。
次に、Anaconda Promptから、conda install pyinstaller
を実行して、pyinstallerパッケージをインストールします。
pyinstaller 実行したいPythonファイル --clean -F
2つあるオプションは以下の目的で付けています。
--clean
: 前回ファイル削除
-F
: 実行時に必要なファイルが1つのexeファイルに集約される
[例]
pyinstaller Google_Search_Using_XPath.py --clean -F
作成されたexeファイルは、exe
ファイルはpyinstaller
を実行したカレントディレクトリに生成されるdist
フォルダ内に出力されます。
なお、chromedriver.exe
ファイルを使用する場合は、作成したexeファイルから参照可能なパスである必要があります。同じ階層に置くとよいでしょう。
比較的簡単にexe化を実行することができました。
なお、pyinstaller
によって作成されたexeファイルを実行するとコンソールが表示されます。これを消去するのはPythonでは難しそうです。C#なら可能のようです。
python - HeadlessモードでChromeDriverを起動したときにコンソールが表示されないようにする - スタック・オーバーフロー
本記事は、Python + Selenium + ChromeでGoogleの検索を自動化する手法について記載したものです。
今回は、XPathを使用して、検索を行います。
XPathはXML文章中の要素、属性値などを指定するための言語です。 XPathではXML文章をツリーとして捉えることで、要素や属性の位置を指定することができます。 HTMLもXMLの一種とみなすことができるため、XPathを使ってHTML文章中の要素を指定することができます。
(※記事記載時点(2019/10/17)では本記事のコードが動作することを確認していますが、Googleのデザイン等が変われば動作しなくなる可能性があります。ご了承ください)
環境は以下のとおりです。パッケージの管理はAnacondaで実施しています。pythonコードの実行はjupyter Noteboook
を使用しております。
XPathを用いた検索方法は、以下の記事で分かりやすく説明してあります。
本記事のサンプルコードでは、Full XPath(
絶対XPathパス)を用いてWebページの要素を取得しています。
XPathを用いれば、以下のように、要素にID指定やclass名指定がなくても、テストしたいページのタグ構成から要素を掴むことが可能です。
element = driver.find_element_by_xpath("/html/body/form[1]")
XPathを用いて、要素を操作する処理は何度も繰り返すと思いますので、関数化するとよいと思います。
# XPathを用いて、ボタンをクリックする def click_button_using_XPath(element_XPath : str): element = WebDriverWait(driver, MAX_WAIT_TIME_SEC).until( EC.presence_of_element_located((By.XPATH, element_XPath)) ) element.click() return element
Googleの検索を自動化したコードです。検索文字列を入力してEnterキーを押した後、ニュースボタンを押します。
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys MAX_WAIT_TIME_SEC = 100 # ChromeDriverのパスを引数に指定しChromeを起動 CHROME_DRIVER_PATH = "chromedriver.exe" options = Options() driver = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH, options=options) driver.maximize_window() # 画面表示を最大化する # 指定したURLに遷移する web_site = 'https://www.google.com/' driver.get(web_site ) # XPathを用いて、ボタンをクリックする def click_button_using_XPath(element_XPath : str): element = WebDriverWait(driver, MAX_WAIT_TIME_SEC).until( EC.presence_of_element_located((By.XPATH, element_XPath)) ) element.click() return element # XPathを用いて、テキストボックスに文字列を入力する def input_textbox_using_XPath(element_XPath : str, send_code : str): element = WebDriverWait(driver, MAX_WAIT_TIME_SEC).until( EC.presence_of_element_located((By.XPATH, element_XPath)) ) # 検索テキストボックスをクリアする for item in range(0,100) : element.send_keys(Keys.BACK_SPACE) element.send_keys(send_code) return element # テキストボックスに検索ワードを入力して、直後にEnterキーを押す input_textbox_using_XPath("/html/body/div/div[4]/form/div[2]/div[1]/div[1]/div/div[2]/input", "test").send_keys(Keys.RETURN) # ニュースボタンを押す click_button_using_XPath("/html/body/div[7]/div[3]/div[5]/div/div/div[1]/div/div/div[1]/div/div[2]/a") driver.close() # ウィンドウを閉じることが出来ます。閉じることが出来るのは引数にブラウザのバイナリを指定したインスタンスウインドウのみ driver.quit() # 「quit」を実行することで全てのウィンドウを閉じることが出来ます。
本記事は、Python + Selenium + ChromeでGoogleの検索を自動化する手法について記載したものです。
ChromeのWeb操作は、ChromeDriverを用います。
ChromeDriverを導入する方法として、以下の2つの方法がありますが、本記事は前者の方法を使用します。
pip install chromedriver-binary
)でインストールする以下のサイトから入手できます。Google Chrome Versionと合わせる必要があるので、ダウンロードするバージョンについては注意してください。
https://chromedriver.chromium.org/
また、ChromeDriverは非headlessモードで動作させます。
(※記事記載時点(2019/10/15)では本記事のコードが動作することを確認していますが、Googleのデザイン等が変われば動作しなくなる可能性があります。ご了承ください)
環境は以下のとおりです。パッケージの管理はAnacondaで実施しています。pythonコードの実行はjupyter Noteboook
を使用しました。
以下のコードでGoogleのWebサイトを開くことができます。
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.keys import Keys from time import sleep options = Options() driver = webdriver.Chrome(chrome_options=options, executable_path="F:\\tool\\ChromeDriver_77_0_3865_40\\chromedriver.exe") web_site = 'https://www.google.com/' driver.get(web_site )
from selenium.webdriver.common.keys import Keys
は、文字を削除、入力、エンターキーの実行を行うためのキー操作を可能にするための宣言です。
from time import sleep
はスリープさせるための宣言になります。
特に、webdriver.Chromeのパス指定(executable_path)は注意してください。Windowsの場合は、フォルダー階層はバックスラッシュ2回にする必要があります。
Googleの検索を自動化しようとした場合、以下のような課題があります。
上記の課題を解決するためには、WebDriverWait
を使って任意のHTMLの要素が特定の状態になるまで待つことで解決可能です。
presence_of_element_located
を実行することで、指定した要素がDOM上に現れるまで待機することが出来ます。
An expectation to locate an element and check if the selection state specified is in that state. locator is a tuple of (by, path) is_selected is a boolean
[例]
WebDriverWait(driver, MAX_WAIT_TIME_SEC).until(EC.presence_of_element_located((By.CLASS_NAME, INPUT_BOX_CLASS_NAME)))
EC.presence_of_element_located((By.CLASS_NAME, INPUT_BOX_CLASS_NAME))
で2重の括弧になっていますが、バグではありません。
内側の括弧がタプル(tuple of (by, path))を示します。byは要素の種類、pathは要素の位置を示します。外側の括弧が関数presence_of_element_locatedの括弧です。
Webページの要素を操作する場合は、Webページの要素にアクセス可能か必ず確認してから操作したほうがよいでしょう。
以下のサンプルコードでは、以下の操作を自動化しています。
test
を入力するfrom selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys # ChromeDriverのパスを引数に指定しChromeを起動 CHROME_DRIVER_PATH = "F:\\tool\\ChromeDriver_77_0_3865_40\\chromedriver.exe" options = Options() driver = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH, options=options) # 指定したURLに遷移する web_site = 'https://www.google.com/' driver.get(web_site ) # 指定された要素(検索テキストボックス)がDOM上に現れるまで待機する (最大でMAX_WAIT_TIME_SEC秒待つ) MAX_WAIT_TIME_SEC = 100 INPUT_BOX_CLASS_NAME = "gLFyf" element = WebDriverWait(driver, MAX_WAIT_TIME_SEC).until(EC.presence_of_element_located((By.CLASS_NAME, INPUT_BOX_CLASS_NAME))) # 検索テキストボックスをクリアする for item in range(0,100) : element.send_keys(Keys.BACK_SPACE) # 検索テキストボックスに文字列を入力する code = "test" element.send_keys(code) # Enterボタンを押す element.send_keys(Keys.RETURN)
Seleniumリファレンス
Seleniumリファレンス
Python + Selenium で Chrome の自動操作を一通り
PythonのseleniumでChrome自動操作する時に使うビジーウェイトは大抵これで解決!
Webページの要素の指定をさらに簡単にしたいので、「XPath」で指定できるようにしたいと思います。また、GoogleのUIを使用しないHeadless Chromeでの自動化ができるようにしたいと思います。
Selenium + Chrome + Python = ラクしてカンタンに「UIテストの自動化」を!
【Python】SeleniumでHeadless Chromeを使おう
def names(name_and_grouplist, group_name): _names = [] for name_and_group in name_and_group_list: if name_and_group[0] == group_name: name = name_and_group[1] if name not in _names: _names.append(name) return _names name_and_group_list = [ ["Aグループ", "Tom"], ["Aグループ", "Jerry"], ["Bグループ", "Mike"], ["Bグループ", "Ichiro"], ["Bグループ", "Mike"], ] result_list = names(name_and_group_list, "Bグループ") print(result_list)
['Mike', 'Ichiro']
def get_name_and_group(name_and_group_list): # 辞書宣言 _names_by_group = {} print(type(_names_by_group)) for name_and_group in name_and_group_list: group = name_and_group[0] name = name_and_group[1] print(type(group)) print(type(name)) if group not in _names_by_group: _names_by_group[group] = [] if name not in _names_by_group: _names_by_group[group].append(name) return _names_by_group name_and_group_list = [ ["A", "Tom"], ["A", "Jerry"], ["B", "Mike"], ["B", "Ichiro"], ["B", "Mike"], ] print(type(name_and_group_list)) result_list = get_name_and_group(name_and_group_list) print(result_list["B"])
['Mike', 'Ichiro', 'Mike']
from collections import defaultdict def get_names_by_group(someList): _names_by_group = defaultdict(list) for item in someList: group = item[0] name = item[1] if name not in _names_by_group[group]: _names_by_group[group].append(name) return _names_by_group name_and_group_list = [ ["A", "Tom"], ["A", "Jerry"], ["B", "Mike"], ["B", "Ichiro"], ["B", "Mike"], ] result_list = get_names_by_group(name_and_group_list) print(result_list["B"])
['Mike', 'Ichiro']
defaultdictは標準ライブラリのcollectionsモジュールに含まれるデータ型です。 辞書に存在しないキーにアクセスしたとき、デフォルト値が設定されたキーを自動で作ってくれるのがメリットです。 defaultdict の引数には、「初期化時に実行する関数」を記述します。
from collections import defaultdict def get_names_by_group(someList): _names_by_group = defaultdict(set) for group, name in someList: _names_by_group[group].add(name) return _names_by_group name_and_group_list = [ ["A", "Tom"], ["A", "Jerry"], ["B", "Mike"], ["B", "Ichiro"], ["B", "Mike"], ] result_list = get_names_by_group(name_and_group_list) print(result_list["B"])
{'Mike', 'Ichiro'}
defaultdict()の使い方について
Python defaultdict の使い方
defaultdict()についてわかりやすくまとめて見た
ポリモーフィズムを利用すべく作った基底クラスのデストラクタはvirtualが必要であるためです。 基底クラスのデストラクタにvirtualを付けない場合、派生クラスのデストラクタが呼ばれずメモリリークが起きる
[Guilty Code]
class Test
{
Test(){}
}
[Correct Code]
class Test { Test(){} virtual ~Test() = default; }
std::cinとstd::coutは副作用があるため呼び出す場所は限定すること (main関数内を強く推奨)
使用していない#include文は削除しましょう
#include <cassert> #include <string> #include "Main.h" bool isIPAddr(const std::string& inputConsoleLine); class Test { public: Test() { ExecuteTest(); } private: static void ExecuteTest() { // ---- 正常パターン単体テスト ---- // // 正規表現ロジック上の下限値・上限値確認 assert(isIPAddr("0.0.0.0")); assert(isIPAddr("255.255.255.255")); assert(isIPAddr("9.9.9.9")); assert(isIPAddr("10.99.10.99")); assert(isIPAddr("100.199.100.199")); assert(isIPAddr("200.249.200.249")); assert(isIPAddr("250.255.250.255")); // ---- 異常パターン単体テスト ---- // // 前置きの0は失敗判定させる assert(!isIPAddr("00.00.00.00")); assert(!isIPAddr("000.000.000.000")); assert(!isIPAddr("0000.0000.0000.0000")); // 異常値を与える assert(!isIPAddr(".")); assert(!isIPAddr(",")); assert(!isIPAddr("2147483647")); // コロンを挿入する位置を変更する assert(!isIPAddr("12.3..4")); assert(!isIPAddr("..1.2.3.4")); assert(!isIPAddr("1.2.3.4..")); // 誤った桁数を与える assert(!isIPAddr("1")); assert(!isIPAddr("1.2")); assert(!isIPAddr("1.2.3")); assert(!isIPAddr("1.2.3.4.5")); // 範囲外の値を与える assert(!isIPAddr("-1.-1.-1.-1")); assert(!isIPAddr("256.256.256.256")); assert(!isIPAddr("2147483647.2147483647.2147483647.2147483647")); // __int32の+側最大値 } }; const static Test Test;
型エイリアスは以前に定義された型を参照する名前です (typedef と同様です)
以下のように型を別名で宣言します。 using TestData = std::array<int, 10>;
【修正前】 [Main.cpp]
#include <iostream> #include "Test.hpp" int main(void) { std::array<int, 10> testData{1,2,3,4,5,6,7,8,9,10}; Test test{testData}; for (const auto& i : test.GetMultiplyData(5)) { std::cout << i << std::endl; } }
[Test.hpp]
#include <array> #include <vector> #include <algorithm> // std::transform class Test { private: std::array<int,10> m_inputData; public: Test(std::array<int,10> inputData) : m_inputData{inputData} {} // 倍数したデータをvectorで取得する std::vector<int> GetMultiplyData(int times) { std::vector<int> resultData; std::transform(std::begin(m_inputData), std::end(m_inputData), std::back_inserter(resultData), [times](int data){return data * times;}); return resultData; } };
【修正後】 [Main.cpp]
#include <iostream> #include "Test.hpp" int main(void) { Test::TestData testData{1,2,3,4,5,6,7,8,9,10}; Test test{testData}; for (const auto& i : test.GetMultiplyData(5)) { std::cout << i << std::endl; } }
[Test.hpp]
#include <array> #include <vector> #include <algorithm> // std::transform class Test { // クラス側でクラスで使用する型を決定することで、ソフトウェアの堅牢性を向上させる public: using TestData = std::array<int, 10>; private: TestData m_inputData; public: Test(TestData inputData) : m_inputData{inputData} {} // 倍数したデータをvectorで取得する std::vector<int> GetMultiplyData(int times) { std::vector<int> resultData; std::transform(std::begin(m_inputData), std::end(m_inputData), std::back_inserter(resultData), [times](int data){return data * times;}); return resultData; } };