Python selenium活用メモ

01_効率化

chromを動かすために使います。

私のためのメモたちです。

  1. ■Webdriverの準備
      1. ■上記でうまくいかないとき
  2. ■Webdriverの準備(スマホ画面)
  3. ■要素を指定する
      1. ■IDで要素を指定する
      2. ■classで要素を指定する
      3. ■nameで要素を指定する
      4. ■CSSセレクタで要素を指定する
      5. ■Xpathで要素を指定する
        1. ■textで要素を指定する
  4. ■ブラウザを更新する
  5. ■IDを使ってelement(text)を取得する
  6. ■IDを使ってelementを取得する List編
  7. ■textをListでとる場合
  8. ■urlをListでとる場合
  9. ■要素liの長さをとりたい場合
  10. ■ランダムな数字を用意したい場合
  11. ■XPathをとるときのポイント
        1. ・style=”visibility (input要素が見つからないとき)
      1. ■ブラウザ上のテキストをxpathで取得するとき
      2. ■xpathでなぜか情報が取れない領域があるとき
        1. ・iframe
  12. ■ボタンなどの要素をクリックする
  13. ■画像をクリックする ※中央以外
  14. ■xpathを使ってループ
  15. ■自作関数を作る
  16. ■テキストボックスに任意テキストを入力する
  17. ■処理待ち時間を設けるとき
      1. ①最も単純な待ち時間指定 time.sleep
      2. ②すべての要素に対しての待ち時間を設定 implicitly_wait
      3. ③特定の要素が読み込まれるまで待つ presence_of_element_located
  18. ■if文を使って特定の要素があるかどうか探す
  19. ■for loopを使って条件に合う項目(items)を見つける
  20. ■別画面が表示されたときに別の画面に切り替える方法
  21. ■クラスにwebdriverを引数として渡す

■Webdriverの準備

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

driver.get("目的のURL")
# ここに処理を記載
driver.close() # やることが終わったら閉じます。

一般的な方法として、chromeのwebdriverを公式サイトから落としてきて、C:\developmentのような開発用フォルダにdriverを入れて置き、そちらを参照する方法がありますが

chromeブラウザは結構な頻度で更新されるのでVerが変わる度にdriverを落としてくるなんて作業はしなくてよいので、これは定型文です。

■上記でうまくいかないとき

ドライバを自動更新してくれるのは良いのですが、稀にドライバとChromeのVerが一致しないときがあります。

そうなるとエラーが出て動かなくなるので任意の場所’C:\development\chromedriverに最新Verを置いておきます。

driver = webdriver.Chrome(service=Service('C:\development\chromedriver'))

//或いは以下のような書き方もあります。これはVer指定
driver = webdriver.Chrome(service=Service(ChromeDriverManager("114.0.5735.90").install()))

上記のように絶対パスでこのdriver使えと言ってれば動いてくれます。

ある程度時が経過し、Verが戻ってくれば上記のコードに修正すればよいでしょう。

Ver指定の方は現在のchromeのVerを指定してあげればOKです。

ブラウザ右上の縦三つの・→ヘルプ→Google Chromeについて で表示されるVerをコピペで入力すれば動きます。

大体この3つのうちどれかくらいは動きます。

■chromeの公式サイトからドライバを持ってくる方法

まず、エラーが出てる内容を確認するとVer116のchormeを使用していることがわかります。

■余談 現在使っているChromeのVerを確認するには

ブラウザ右上の・・・→ヘルプ→Google Chromeについて で確認できます。

以下のサイトに飛ぶと

Downloads  |  ChromeDriver  |  Chrome for Developers

2023/8/21現在の以下の画像見るとVer114は見えてますがVer116はないですよね。ChromeDriverManager().install())で引っ張ってこれるのは114なんで、webdriver.Chrome(service=Service(‘C:\development\chromedriver’))のようにして自分で116を持ってきてドライバが格納されているファイルパス(C:\development\chromedriver)を指定しないと行けないわけです。

ということで”the Chrome for Testing availability dashboard”に移動します。

116があったので左のStableをクリック。

使用されているOSを確認し、私の環境の場合Windowsの64bitなので上記ハイライト部のURLをクロームブラウザのアドレスバー(URL入力したり、検索窓にできるやつ)に入力すればZipとしてドライバがDLできるので所定の場所にドライバを格納しておきます。

以上ですね。

■Webdriverの準備(スマホ画面)

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

def smp_mood():
    URL = "https://web.screen.rakuten.co.jp/app"
    mobile_device_name = { "deviceName": "iPhone 12 Pro" } # エミュレートするデバイスを選択
    options = webdriver.ChromeOptions()
    options.add_experimental_option("mobileEmulation", mobile_device_name)
    driver = webdriver.Chrome(options=options, service=Service(ChromeDriverManager().install()))
    driver.get(URL)

smp_mood()

■要素を指定する

■IDで要素を指定する

from selenium.webdriver.common.by import By

driver.find_element(By.CSS_SELECTOR, 'element_id')

■classで要素を指定する

driver.find_element(By.CLASS_NAME, "element_class_name")

■nameで要素を指定する

driver.find_element(By.NAME, "element_name")

■CSSセレクタで要素を指定する

from selenium.webdriver.common.by import By

driver.find_element(By.CSS_SELECTOR, "css_selector")
# 以下のように指定
driver.find_element(By.CSS_SELECTOR, '#element_id') #idで指定
driver.find_element(By.CSS_SELECTOR, '.element_class_name') #classで指定
driver.find_element(By.CSS_SELECTOR, "tag_name") #tag名で指定
driver.find_element(By.CSS_SELECTOR, "[attribute_name='attribute_value']") #attributeで指定
driver.find_element(By.CSS_SELECTOR, "[attribute_name1='attribute_value1'][attribute_name2='attribute_value2']") #複数attributeで指定
driver.find_element(By.CSS_SELECTOR, "[attribute_name^='attribute_value']") #attributeが指定の値で始まる
driver.find_element(By.CSS_SELECTOR, "[attribute_name$='attribute_value']") #attributeが指定の値で終わる
driver.find_element(By.CSS_SELECTOR, "[attribute_name*='attribute_value']") #attributeが指定の値を含む
driver.find_element(By.CSS_SELECTOR, "parent_element child_element") #child_elementで指定
driver.find_element(By.CSS_SELECTOR, "preceding_element + following_element") #隣接する兄弟要素で指定
driver.find_element(By.CSS_SELECTOR, "preceding_element ~ following_element") #一般的な兄弟要素で指定

■Xpathで要素を指定する

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, "//xpath_expression")
■textで要素を指定する
driver.find_element(By.XPATH, '//div[text()="テキスト"]')

注意点

私の場合、特定の要素をクリックすることが多いのですが、指定する要素はdivやbuttonを指定することを心がけます。

textで指定する場合、div内のaタグ内にtextがある場合があります。そういったときはdiv側を指定します。

■ブラウザを更新する

driver.refresh()

■IDを使ってelement(text)を取得する

①IDのテキストを取得

coinというidを持つtext(数値)を取得する例

coin_element = driver.find_element("id", "coin").text
    if "," in coin_element: # 数値なので,が含まれる場合がある
        coin_element = coin_element.replace(",", "")
 # ","→""に置き換える
    coin_count = int(coin_element) # string(文字列)データである場合があるのでint(整数値)に変換
    print(coin_count)

②altのテキストを取得

画像などに設定されているテキストをとる例です

img_element = driver.find_element("xpath", '//[@id="idname"]/~~~/img')
print(img_element.get_attribute('alt'))

画像エレメントをxpath指定で格納し、内部のalt要素を出力します。

■IDを使ってelementを取得する List編

from selenium.webdriver.common.by import By # "By.CSS_SELECTOR" などのByで指定する場合必要
~
items = driver.find_elements(By.CSS_SELECTOR, "#idname div") # idを使ってdivを特定
item_ids = [item.get_attribute("id") for item in items] # 上記のdivに含まれるid要素(text)をリスト化する

実用例:idを使ってdivを特定し、内部の<b>要素に書かれているtextの数値だけをListにする

from selenium.webdriver.common.by import By

~
all_nums = driver.find_elements(By.CSS_SELECTOR, "#idname b") # cssの要素から指定する方法 #~はid名 .~はclass名 なので"idname"という要素(主にdiv)に含まれる<b>のエレメントをすべて取得

# print(all_nums) # 仮に表示するとしたら "[<selenium.webdriver.remote.webelement.WebElement (session="~~~", element="~~~")>, <selenium.webdriver.remote.webelement.WebElement (session="~~~", element="~~~")>, ・・・

item_nums = [] # 上記はそのまま使えないので、まず空のリストを準備

# <b>に含まれるテキストの内、整数のみ取得する。
for num in all_nums:
    element_text = num.text # text情報だけを取得する
    # print(element_text) # 仮にここで表示されるテキストはこのようなものとする "name - num"
    if element_text != "": # 空欄が含まれる場合除外する
        buf = int(element_text.split("-")[1].strip().replace(",", "")) # "num"となる -で分離(split)するのでname※[0]とnum※[1]に分かれるので[1]を指定して、数が1,000のように,を含んでいる場合があるので","→""に置き換える(replace)
        item_nums.append(buf) # 準備していたリストに格納する
    print(item_nums)

応用例:上記で得たListをdictionaryに変換する

dictionary = {}
     for n in range(len(item_nums)): # 実用例で得たListの数だけループ
          dictionary[item_nums[n]] = item_ids[n] # 数字とtextの辞書ができる
     print(dictionary) # e.g {12: 'text1', 24: 'text2', ・・・・}

■textをListでとる場合

上記は属性のtextを取得してますが、実際にwebページに書かれていることを取得したい場合

items = driver.find_elements(By.CSS_SELECTOR, ".classname") # classを用いて取得したいエレメントを取得

item_texts = [] # 格納用のListを準備
for text in range(len(items)): 
    try: # try exceptは念のため入れているだけです。
        item_texts.append(driver.find_element("xpath", f'/html/body/~~~/div[{text + 1}]/~~~').text) # fullxpathで任意の箇所を指定
    except:
        pass
print(item_texts)

■urlをListでとる場合

elements = driver.find_elements(By.XPATH, "//a[@href]")
for element in elements:
    url = element.get_attribute("href")

■要素liの長さをとりたい場合

ulやolは箇条書きで何か書かれているときに出てきますが、その中身要素がいくつあるのか知りたい場合があります。

results_list = driver.find_elements("xpath", '//*[@id="idname"]/~~~/ul/li')
print(len(results_list))

ポイントはxpathの後ろの”li”ですね、”ul”によって”li”が束ねられていますので、ulのxpathをとってきても、上記の例だと

//*[@id=”idname”]/~~~/ul までしか表示されません、とはいえ検証画面で直下にliが連なっていることはわかっているので

//*[@id=”idname”]/~~~/ul/li としてしまえば、あとはlenで長さを表示して終わりです。

■ランダムな数字を用意したい場合

import random

variable_name = random.randint(1, 10)

■XPathをとるときのポイント

①結論を言えば、htmlの勉強を少しすればどこの要素のpathを指定すればいいかわかる。

htmlの知識がない状態でseleniumを使いだすと、要素指定に便利なxpathを使いこなせないことがあります。

div span a p li input button などの要素です。

上記の意味を調べるだけでも助けになるはずです。

例えば、テキストボックスに何か文字を入力したい場合(何かのサービスにログインするとき)

from selenium.webdriver.common.keys import Keys

email = driver.find_element(By.XPATH, '//*[@id="~~~~"]~~~~/input')
password = driver.find_element(By.XPATH, '//*[@id="~~~~"]~~~~/input')

email.send_keys(EMAIL)
password.send_keys(PASSWORD)

上記のようにinput(テキストボックス)に対して書き込みを行う必要がある。←この感覚がつかめる。

②クリックしたい要素にidやclassが書かれていない場合

通常のxpathをmemoなどにコピぺしてみたら”//*[@id=”~~~~”]~~~~/”のような感じでidがないように見えて表示されたりします。

・style=”visibility (input要素が見つからないとき)

send_keysってファイルパスを送ることでファイル転送ができたりするので便利なんですけど、要素が見つからないと言われるサイトがちらほらあるんですよね。

ファイルを送る場合はinput要素を見つけて、type=”file”のものを探すのが手っ取り早いわけです。

そのXPATH指定しているのに見つからない場合、そのinput要素のプロパティ?をもう少し調べます。

style=”visibility: hidden;とか書いてある奴が含まれていると、この属性を上書きしちゃいます。

drop = driver.find_element(By.XPATH,"/html/body/input")
driver.execute_script("style = 'visible';", drop)
driver.find_element(By.XPATH,"/html/body/input").send_keys(FilePath)

うん、何やってるんでしょうねJavaScript Executoとかいうやつを使ってるらしいです。

こんな感じで貼り付けできるときがあります。

■ブラウザ上のテキストをxpathで取得するとき

text情報を取ろうとして、xpathを取得したとき、以下のようなxpathになることがあります。

/html/body/div[x]/div[x]/..../p[x]/text()

これを以下のようにしてもうまく取れません。

driver.find_element(By.XPATH, "/html/body/div[x]/div[x]/..../p[x]/text()")

正しくは以下のように書きます。

driver.find_element(By.XPATH, "/html/body/div[x]/div[x]/..../p[x]").text

基本的にstr形式でとられることが多いと思いますので、数値情報ならint()で囲ったり、してください。

■xpathでなぜか情報が取れない領域があるとき

・iframe

iframe内にある情報はいつも通りにxpathを取りに行っても要素が無いと言われます。

iframe内の要素を指定する場合は、Seleniumのswitch_to.frameメソッドを使用します。下記は、iframe内の要素を指定する例です。

htmlを検証し、iframeが無いか探します。

上記例の場合、classが指定されているので、classでiframeを指定します。

iframe = driver.find_element(By.CSS_SELECTOR, ".classname")#classで指定
iframe = driver.find_element(By.CSS_SELECTOR, "iframe[title='titlename']")#titleで指定

driver.switch_to.frame(iframe)

# ~以降でxpath指定などで要素を取得できるようになる~

# iframeから戻る
driver.switch_to.default_content()

以下参考

What does #document mean?
This is the HTML file I have. I am trying to use Selenium-Webdriver API along with ChromeDriver to send_keys to an input...

■ボタンなどの要素をクリックする

あたりまえだけど、クリック対象は1つなのでelement

#以下のような方法で要素を指定
driver.find_element(By.CSS_SELECTOR, "#id_name").click()
driver.find_element(By.CSS_SELECTOR, ".class_name").click()
driver.find_element(By.XPATH, 'XPathをコピペ').click()
driver.find_element(By.CSS_SELECTOR, "[alt='alt_text']").click()

driver.switch_to.window(driver.window_handles[1])# 制御対象のタブを指定
~何か処理~

# クリックした後に別タブが開かれ、元のタブに戻りたい場合以下を入れる
driver.close() # 別タブを閉じる
driver.switch_to.window(driver.window_handles[0]) # 制御対象のタブを指定

classで指定する場合、listで返ってくるので同じclass名が同一画面に複数あった場合

[0]などで何番目の要素かを指定する。

■画像をクリックする ※中央以外

画像をクリックすることもできますが、普通に真ん中をクリックするだけなら上記のやり方で行けます。クリックする場所を少しずらしたいなどの需要がある場合は以下の方法を使います。

from selenium.webdriver.common.action_chains import ActionChains

element = driver.find_element(By.XPATH, 'XPathをコピペ')
actions = ActionChains(driver)
actions.move_to_element(element).move_by_offset(0, 10).click().perform()#10ポイント下をクリック

■xpathを使ってループ

buf = 1
for item in items: # ループしたいものをリスト形式で先にitemsとして格納しています
    driver.find_element(By.XPATH, 'XPathをコピペ').click() # fstringを使って文字列に変数を埋め込みます。
    buf = buf + 1

リスト(li)などの要素に何か処理したい場合、検証→完全なxpathをコピーからfullxpathを得ると、どこの変数が変化しているかわかるので、その位置を変数と置き換えます。

■自作関数を作る

def function_name(argument1,argument2):
    # 処理内容
    # return 戻り値が必要なら設ける

function_name(aaa,bbb)

同じようなコードが多くなってきて、使いまわしする場合などで使用

■テキストボックスに任意テキストを入力する

from selenium.webdriver.common.keys import Keys

PASSWORD = "ここにパスワードを入力"

password = driver.find_element(By.CSS_SELECTOR, '#idname')
password.send_keys(PASSWORD)
password.send_keys(Keys.ENTER)

上記の例はIDをもとに確認する方法

■処理待ち時間を設けるとき

①最も単純な待ち時間指定 time.sleep

この方法は理解しやすいが、運用時にメンテナンスが必要になったりすると物凄く扱いにくい。seleniumを使うということはwebページの読み込み時間を待つことが多いので、非推奨のやり方と言える。

  • メリット:
  • 簡単に実装
  • コードの書き方が簡単
  • デメリット:
  • 待ち時間が長すぎるとプログラムの実行が遅くなる
  • 逆に短すぎると、処理に必要な要素が読み込まれていない状態で実行される
import time

time.sleep(1)

②すべての要素に対しての待ち時間を設定 implicitly_wait

待ち時間の指定は、ドライバを定義した直後のみでよく、画面全体の読み込み時間を待つ必要があれば、このコードで対応できます。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.implicitly_wait(10)
driver.get(URL)

簡潔で理解しやすく①の方法よりも優れています。

ただし、この方法はすべての要素が読み込まれるまで待機するので、待ち時間が必要以上に長くなったりすることがあります。

③特定の要素が読み込まれるまで待つ presence_of_element_located

特定の要素が読み込まれたらすぐに処理を実行したい場合に使用します。

紹介した中では最もエラーが発生しにくいです。読み込み時間は環境によって異なりますので、エレメントが見つからないエラーが出る場合は待ち時間を長く設定します。

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located((By.ID, "element_id")))

詳しく紹介しているページは以下

■if文を使って特定の要素があるかどうか探す

try:
    # 以下の何れかの形で指定
    if driver.find_elements(By.CSS_SELECTOR, '.classname'): #class指定
    if driver.find_element(By.XPATH, 'XPathをコピペ'): #xpath指定
        ~任意処理内容~
except:
    pass

■for loopを使って条件に合う項目(items)を見つける

upgrades = {}
for cost, id in dictionary.items():
    if cookie_count > cost:
        affordable_upgrades[cost] = id
        print(affordable_upgrades[cost])

■別画面が表示されたときに別の画面に切り替える方法

ログインなどで画面が切り替わってしまった時などに使用する

base_window = driver.window_handles[0] #初めから表示されている画面は基本的にhandles[0]になっている
pop_window = driver.window_handles[1] #次に表示されている画面は基本的にhandles[1]になっている
driver.switch_to.window(pop_window) #画面を切り替える
print(driver.title) #handlesの並びがあっているか確認するためにtitleを確認するといい

上記は意図せずクリック操作で別タブが開いたときに使用できますが、

どこかのサービスログイン、サービス内の別ページを参照したい場合、

要するにログインの後画面遷移が多すぎてクリック処理とか面倒なことがやりにくいことがあれば

urlを指定した別のタブを開くこともできます。

driver.execute_script("window.open()") #新しくタブを開きます
driver.switch_to.window(driver.window_handles[1]) #※1
driver.get(url) #urlを指定します

#~~~任意処理~~~
driver.close()
driver.switch_to.window(driver.window_handles[0]) #元のタブに戻ります。

※1タブを開くとき[ ]内の番号は使用されていないタブの番号を使いましょう。

開いたタブは0から順に番号が振られるので上記の例ではログイン画面[0]で処理が終わった後に

目的の画面[1]に遷移して処理する感じです。

目的の画面が複数ある場合urlをList化しておいて、forループで呼び出してくればok

■クラスにwebdriverを引数として渡す

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

class Bot:
    def __init__(self, web_driver):
        self.driver = web_driver

bot = Bot(driver)

コメント

タイトルとURLをコピーしました