※この記事は9割Geminiに作ってもらいました※
PythonとSeleniumでデータベース登録を自動化!使いづらいサイトもこれで攻略
大量のデータをWebサイト上のデータベースに登録する作業は、手動で行うと非常に手間がかかりますよね。特に、使いづらいサイトの場合はなおさらです。ここでは、PythonとSeleniumを使って、この厄介な作業を自動化した経験についてご紹介します。
注意点: Webサイトの自動操作は、サイトの利用規約に反する場合があります。実施する際は、必ずサイトのポリシーを確認し、慎重に行ってください。
当初の試み(そして失敗)
当初は、PythonのrequestsモジュールでHTMLを取得し、テキスト処理で解析する方法を試しました。HTML解析ライブラリであるBeautifulSoupは苦手だったので、自力でテキストを解析することに挑戦しました。
クロスドメインログインの維持には成功し、サイトへのアクセスも可能になりましたが、大きな壁にぶつかりました。それは、JavaScriptの複雑な挙動をPythonで再現することです。動的にコンテンツが生成されたり、非同期通信が行われたりするサイトでは、単にHTMLを取得するだけでは限界があり、結局このアプローチは挫折に終わりました。
改善案:SeleniumとWebDriverの導入
そこで目をつけたのが、PythonのSeleniumです。SeleniumはWebブラウザを自動操作するためのツールで、実際のブラウザ(FirefoxとGeckodriverを使用しました)を動かすため、JavaScriptで生成されるコンテンツも問題なく扱えます。
特に、「find_element(By.XPATH, value=xpath)」での要素検索は非常に便利でした!XPathを使えば、ワイルドカードやand検索を駆使して、目的の要素を柔軟に、かつ正確に特定できます。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options # Firefoxのオプションをインポート
def get_element_safely(driver: WebDriver, by_method, element_id: str):
# get_element_safely 関数は別途定義記述
if __name__ == "__main__":
# Firefoxドライバーのインスタンスを作成
# geckodriverのパスを指定する必要がある場合:
# driver = webdriver.Firefox(executable_path='/path/to/geckodriver')
# 例として、FirefoxのWebDriverをHeadlessモードで起動します
options = Options() # FirefoxOptions を使用
options.add_argument('--headless')
driver = webdriver.Firefox(options=options) # Firefox WebDriver を使用
driver.get("https://www.google.com") # 例としてGoogleを開きます
# IDで要素を取得する例
search_box = get_element_safely(driver, By.NAME, "q")
if search_box:
search_box.send_keys("Selenium Python")
print("検索ボックスにテキストを入力しました。")
else:
print("検索ボックスが見つかりませんでした。")
# 存在しないIDで要素を取得する例
non_existent_element = get_element_safely(driver, By.ID, "nonExistentId")
if non_existent_element:
print("存在しないはずの要素が見つかりました。")
else:
print("存在しない要素は見つかりませんでした。(期待通り)")
driver.quit()
自動化をスムーズにするための工夫
より効率的で堅牢な自動化を目指し、いくつかの工夫を凝らしました。
-
要素の安全な取得関数: Seleniumで要素を取得する際、要素のロードには時間がかかる場合があります。そのため、指定した要素が見つかるまで待機し、それでも見つからない場合はエラーを捕捉して
Noneを返す関数を作成しました。これにより、スクリプトが途中で予期せぬエラーで停止するのを防ぎます。get_element_safely.pyfrom selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException from selenium.webdriver.remote.webdriver import WebDriver def get_element_safely(driver: WebDriver, by_method, element_id: str): try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((by_method, element_id)) ) return element except TimeoutException: print(f"エラー: 要素 '{element_id}' が指定された時間内に見つかりませんでした。") return None
-
入力フォームの自動判定と入力: 取得した要素が
<input>タグなのか<select>タグなのかによって、データの入力方法が異なります。そこで、取得したオブジェクトの種類を自動で判定し、それぞれに適した方法で値を入力する関数を作成しました。これにより、コードの記述量を減らし、汎用性を高めることができました。
ハマった点と解決策
自動化を進める中で、いくつかの課題に直面しました。
NoSuchElementException: Message: No such element with visible text: 'Your Text'
最もハマったのは、要素を検索する際のelement_id情報が少なすぎたために、意図しない要素を取得してしまい、操作しようとしてエラーが発生したことです。特に、似たような名前や構造を持つ要素が複数存在する場合にこの問題が起こりがちでした。
この問題の解決策は、「横着せずにBy.XPATHで複数の情報を指定すること」です。例えば、単にIDを指定するだけでなく、親要素のIDやクラス、テキスト内容なども含めてXPathを記述することで、目的の要素を確実に特定できるようになります。
XPathの記述例:
-
IDを指定して要素を取得:
"//input[@id='username']"
(idがusernameのinput要素) -
クラス名とタグ名を組み合わせて要素を取得:
"//div[@class='form-group']/input"
(classがform-groupのdiv要素の子孫にあるinput要素) -
要素のテキスト内容で要素を取得:
"//button[text()='送信']"
(テキストが「送信」のbutton要素) -
部分一致で要素を取得(
contains関数を使用):
"//a[contains(@href, 'product')]"
(href属性に「product」を含むa要素) -
複数の条件を組み合わせて要素を取得(
andを使用):
"//input[@name='password' and @type='password']"
(nameがpasswordで、かつtypeがpasswordのinput要素)
まとめ
PythonとSeleniumを使えば、JavaScriptが多用されているような複雑なWebサイトであっても、データ登録作業を効率的に自動化できます。XPathをうまく活用し、エラーハンドリングを丁寧に行うことで、より堅牢で実用的な自動化スクリプトを構築できるでしょう。
注意点: Webサイトの自動操作は、サイトの利用規約に反する場合があります。実施する際は、必ずサイトのポリシーを確認し、慎重に行ってください。