PR
私が使っているPCガジェット類
作業環境で実際に使っている・気になっている周辺機器などをまとめました
※ 一部のリンクは広告(アフィリエイト)を含みます
作業環境で実際に使っている・気になっている周辺機器などをまとめました
※ 一部のリンクは広告(アフィリエイト)を含みます
私は本業でも副業でもお世話になてるSeleniumさん
普通に使ってると歯痒いところ何気に多く、ラッパーを作ったり微調整してあげてるんですが実際に遭遇した事象と対処法をメモしていくのでSelenium好きの方はちょくちょく覗きに来てもらうと情報共有できると思います
Seleniumはスクレイピングで使うことも多くてスピードを求める人も多いですが根源的にはWEBアプリのテスト用であり、スクレイピングするにしてもWEBサイトに迷惑をかけないことが必須なので”スピード <<< 正確な動作”を前提にプログラムを作成します
このページでは使っていないものもありますが、私がSeleniumを使う時の基本セットてきなやつです
import dataclasses
import logging
import logging.config
import logging.handlers
import os
import datetime
from time import sleep
import bs4
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common import utils
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
# ---今回はoptionsとserviceは考慮しない
driver = webdriver.Chrome()ここから実際に遭遇した事象と個人的対処方法を書き残していきます
自分で使うときはラッパークラスを作ってるので厳密には違うコードになってますが考え方は同じなんで参考にどうぞ
何を言ってるのかわからねーと思うがおれも何をされたのかわからなかった…頭がどうにかなりそうだった…
例えば
なんてよくある処理でもサイトによっては稀に謎エラーが発生します
何度デバックでステップインしても再現せずに苦しみましたが次の処理をすることでその後再現していないので対応完了ってことにしました
driver.get(url=url)
try:
# JavaScriptを使ってページ全体が読み込まれるまで待機
WebDriverWait(driver, 30).until(lambda d: d.execute_script("return document.readyState") == "complete") # ---①
sleep(0.1) # ---②
except Exception:
logger.error('Page Transition Error!!')
driver.find_element(By.CSS_SELECTOR, '#email').send_keys(id)
driver.find_element(By.CSS_SELECTOR, '#password').send_keys(pw)
driver.find_element(By.CSS_SELECTOR, 'button.login_bunt').click()
try:
# JavaScriptを使ってページ全体が読み込まれるまで待機
WebDriverWait(driver, 30).until(lambda d: d.execute_script("return document.readyState") == "complete") # ---①
sleep(0.1) # ---②
except Exception:
logger.error('Page Transition Error!!')
'''
~~~ ログイン後の処理 ~~~
'''やることは2つ
jsの処理が終わってないのに次の処理をしようとしてエラーになってる気がするので適度に待機処理入れることで解消したんじゃないかと推察してます
どのサイトでも発生するってわけでもないんですが、画面上に入力する要素が見えてないと文字入力ができない時がありまして・・
他にも文字入力処理は気を遣わないと思い通りの処理にならないことが多々あるのでちゃんとエラーハンドリングしようとするならこれくらい必要
css_selector = '#target_id'
# ---入力する要素が見える状態か確認する
try:
_ = WebDriverWait(driver, 3).until(EC.visibility_of(driver.find_element(By.CSS_SELECTOR, css_selector)))
except NoSuchElementException:
print('No Such Element')
except TimeoutException:
print('Timeout Error')
except Exception:
print('Unknown Error', exc_info=True)
# ---WebDriverWait以外でもチェックする
element = driver.find_element(By.CSS_SELECTOR, css_selector)
# ---タグがinputかtextareaじゃないか確認する
if element.tag_name != 'input' and element.tag_name != 'textarea':
print('This element tagName is not input and textarea')
return False
# ---タグが入力可能か確認する
if element.is_enabled() is False:
print('This element is disable')
return False
# ---readonly属性が付いてないか確認する
if not element.get_attribute('readonly') is None:
print('This element is readonly')
return False
# ---念のため画面をelementへ動かしておく
ActionChains(driver).move_to_element(element).perform()
# ---send_keysだと追記になってしまうので一旦クリアしておく
element.clear()
# ---念のため待機処理を入れる
sleep(0.1)
# ---ようやく実際に文字入力する
element.send_keys('hoge')
# ---入力後に発火するjsの可能性があるのでタブ移動する
element.send_keys(Keys.TAB)
# ---念のため待機処理を入れる
sleep(0.1)まず”driver.switch_to.frame“より”expected_conditions“の”frame_to_be_available_and_switch_to_it“の方がエラーハンドリングしてくれるのでオススメ
中身を見ればやってることは結局”driver.switch_to.frame“だったりする
def frame_to_be_available_and_switch_to_it(locator: Union[Tuple[str, str], str]) -> Callable[[WebDriver], bool]:
"""An expectation for checking whether the given frame is available to
switch to.
If the frame is available it switches the given driver to the
specified frame.
"""
def _predicate(driver: WebDriver):
try:
if isinstance(locator, Iterable) and not isinstance(locator, str):
driver.switch_to.frame(driver.find_element(*locator))
else:
driver.switch_to.frame(locator)
return True
except NoSuchFrameException:
return False
return _predicate本題ですが”失敗する”じゃなくて”成功しない”と表現したのは”frame_to_be_available_and_switch_to_it“ではTrueで返ってくるのになぜか遷移できていないことに遭遇したから
発生確率も地味に高いのでちょっと面倒だけどこれくらいやらないと安心できないです
Frameの遷移が必要なWEBサイトは少ないのでこれくらいは我慢ってことですかね
css_selector = '#target_id'
try:
# ---初期Frameに戻す
driver.switch_to.default_content()
# ---5回までリトライするようにする
for _ in range(5):
# ---5秒でFrameの遷移が完了するかチェック
if WebDriverWait(driver, 5).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, css_selector))) is False:
sleep(1)
else:
# 選択したフレームが見えなくなっていることを確認して終わりにする
if len(driver.find_elements(By.CSS_SELECTOR, css_selector)) == 0:
return True
else:
sleep(1)
continue
else:
raise NoSuchFrameException(msg=f'"{css_selector}" に遷移できませんでした')
except Exception:
# ---エラーになったら画面ショットを残しておく
driver.save_screenshot(R'errorshot_{}.png'.format(datetime.datetime.now().strftime("%Y%m%d%H%M%S")))(Frameと言えばこんな感じのソースで動いているサイトもまだまだ現役のようです)

何か問題に遭遇して解決した時は追記していくので気が向いた時にでも見に来てください!
PythonとExcelを中心に仕事に役立つ業務ツールや自動化、スクレイピングツールの作成を受注していて、クラウドワークスでは気が付けば100件以上のお仕事を受注してきました!
会社員をやりながらの副業なので時間の捻出は相応ですが、クライアントの方々と近い立場でこちらからも提案しながら活動していますのでお悩みあれば是非ご相談ください
VBAとPythonを中心にユーザー側でできるITを自己学習しているので備忘録半分、学習履歴を残して同じ道を辿る人の参考になればとブログを始めました
副業でスクレイピングツール作成を中心にできることを色々やっていますのでご相談いただけるとありがたいです!
クラウドワークスのページへ
ココナラのページへ
コメント