seleniumとは
Pythonにはいくつかのスクレイピングライブラリがありますが、seleniumはその中でも、「ブラウザを操作する」タイプのライブラリです。
操作できるブラウザの種類も多く、ChromeやPhantomJS、FireFox、Edgeなどを自動操作することが出来ます。
操作するブラウザとして、以前は特別色々インストールせずともサクッと使いやすい PhantomJS が良く選ばれていたようですが、開発を終了したことや、そもそもクロームのシェアが高い事、Chromeが完全にヘッドレスに対応したこともあり、今ではChromeとの組み合わせで使うのが結構主流です。
- ブラウザをマクロ的に自動操作できるライブラリ。
- 本来はWebページのテスト用ツール。
- データの取得・収集を目的とした、スクレイピングの用途にも使われる。
- さまざまな種類のブラウザを操作可能。
seleniumでできる事
Pythonのスクレイピングライブラリとして非常にメジャーなhttpインターフェイスのrequestsと比較した場合、ブラウザ自動操作系のseleniumの最大の特徴としてJavaScriptの動作が挙げられます。
requestsなどのhttpリスエストのみを行うライブラリはJavaScriptによるサイトの内容の変化や、情報の展開を考慮することが出来ません。
JavaScriptによる動的な変化に対応したBot操作を行いたい場合はseleniumを選ぶことになるでしょう。
- JavaScriptによる動作を考慮したBotが作れる。
- ログインの必要なページにも簡単にアクセスできる。
対して、デメリットとしてはJavaScriptなどによる実際のレンダリングエンジンが走るため、動作が遅い、且つ、クリックなどのメソッドは不安定になりがちです。
あと、超個人的な感想ですが、DOM操作系のメソッド記述がやたらダラダラと長ったらしいのも不満点だったりします。
純粋にスピードや安定感重視のBotを組みたいだけであれば、敢えてseleniumを選ぶメリットはありません。
- ブラウザの描画処理があるので処理が遅い。
- 動作は安定性に欠ける。
- DOM操作系のメソッドの記述がやたらと長い。
seleniumのインストール
pip install selenium
pipで一発インストールできます。
Chrome Driver のインストール
seleniumのインストールだけではブラウザを操作することが出来ません。
ブラウザとseleniumの間に中継するドライバが必要なのでそれをインストールする必要があります。
FireFoxなど元々seleniumにドライバが組み込まれている一部ブラウザであれば、この操作は必要ありません。
今回はChromeを操作するためのドライバーをインストールしていきます。当たり前ですが、ドライバー単体では動作しませんのでChromeの本体インストールは必須です。
Chrome Driverのインストールに関しては環境によるところが大きいので、今回は一般的なWindows環境を前提とします。
Chrome Driverのダウンロード
注意点として、インストール済みのChromeのバージョンとChromeDriverのバージョンを合わせてダウンロードする必要があります。
Chrome Driverの設置
windowsのChrome Driver自体は一つの実行形式のexeファイルです。
任意のディレクトリに置いて、そのディレクトリにPathを通す必要があります。
seleniumでChromeを操作する
from selenium import webdriver from selenium.webdriver.chrome.options import Options
インポートして準備完了。
seleniumでChromeを立ち上げる
driver = webdriver.Chrome()
Chromeをヘッドレスで立ち上げる
#optionsインスタンスを生成 options = Options() #オプションを設定 options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-gpu') #ドライバインスタンスを生成(Chromeの立ち上げ) driver = webdriver.Chrome(options=options)
VBAでいうところの IE.Visible = False のようなバックグラウンド状態でクロームを操作できます。
‘–no-sandbox’ と ‘–disable-gpu’ は何なのかよく分かりませんが、これを指定しないと僕の環境ではエラーになります。環境によっては ‘–headless’ だけで動作する場合もあるみたいです。
任意のURLにアクセス
driver.get('https://www.yahoo.co.jp')
現在のURLを取得
driver.current_url
現在のページタイトルを取得
driver.title
ブラウザで表示中の全ソースを取得
driver.page_source
非常に個人的な感想ですが、単純にHTMLをパースするだけの使い方の場合は、selenuimはメソッドが長ったらしくてあまり好みではありません。
表示中のページソースを全て取得できるので、そのまま引き渡すことで、BeautifulSoupを使ったパースが可能になります。
from bs4 import BeautifulSoup as bs4 soup = bs4(driver.page_source, 'lxml')
ブラウザの更新
driver.refresh()
タブ(ウインドウ)を閉じる
driver.close()
現在アクティブのタブのみを閉じます。
全てのタブ(ウインドウ)を閉じる
driver.quit()
特定の要素を取得
driver.find_element_by_id('id_name') #idに該当する要素 driver.find_element_by_name('name') #nameに該当する最初の要素 driver.find_element_by_class_name('class_name') #classに該当する最初の要素 driver.find_element_by_link_text('link_text') #link textに該当する最初の要素 driver.find_element_by_tag_name('tag_name') #tagに該当する最初の要素 driver.find_element_by_css_selector('css') #cssセレクタに一致する最初の要素
指定した要素がpage_source内に存在しない場合は例外が発生する。
特定の要素を全て取得
driver.find_elements_by_id('id_name') #idに該当する全ての要素リスト driver.find_elements_by_name('name') #nameに該当する全ての要素リスト driver.find_elements_by_class_name('class_name') #classに該当する全ての要素リスト driver.find_elements_by_link_text('link_text') #link textに該当する最初の要素 driver.find_elements_by_tag_name('tag_name') #tagに該当する全ての要素リスト driver.find_elements_by_css_selector('css') #cssセレクタに一致する全ての要素リスト driver.find_elements_by_xpath(".//a") #Xpathでも指定できます。
elementをelementsに変更するとリストで要素が返却されてきます。
idはページ内に1つしかないことが構文上の約束事ですが、seleniumではelementsとしても通ります。もちろんリストで返却されます。
要素が存在しない場合は例外ではなく、空のリストが返却されます。
要素が存在するかどうか確認する方法
[find_elements ~ ]では、該当する要素が存在しない場合は例外ではなく「空のリスト」が返却されてきますので、この性質を利用して、要素の存在を確認することが出来ます。
elm = driver.find_elements_by_id('id_name') #存在しない場合空のリストがelmに格納される。 if elm: print('要素は存在する') else: print('要素は存在しない')
特定の要素をクリック
ブラウザを操作することが出来るので、要素をクリックすることもできます。
driver.find_element_by_id('id_name').click()
特定の要素までスクロール
クリックメソッドですが、ウインドウ内に要素をとらえていない場合、上手く実行できないときがあります。
そういった場合、その要素の位置までスクロールする必要があります。
事前に ActionChains の import が必要です。
from selenium.webdriver.common.action_chains import ActionChains element = driver.find_element_by_id("id_name") actions = ActionChains(driver) actions.move_to_element(element) actions.perform()
特定の要素にテキストを入力
勿論、テキストを入力することも可能。ログインの操作などがrequestsと比べ楽に実装できます。ぶっちゃけ、これがselenium最大のメリット(使いどころ)だと個人的には思っています。
driver.find_element_by_id('id_name').send_keys('入力内容')
ちなみにエクセルのVBAによるIE操作と違って、NumLockされて変な感じになったり、OSで他の操作してたらsend_keysが失敗したりはしないので、安心して使えます。
テキスト以外のキーを入力
単純なテキスト入力ではなくキーボードのENTERやTABなどのキーを入力することもできます。事前に Keys の import が必要です。
from selenium.webdriver.common.keys import Keys driver.find_element_by_id('id_name').send_keys('入力内容',Keys.TAB,Keys.ENTER)
send_keysに複数の引数を渡すと左から順に全てのkeyを送信します。
要素がウインドウ内に表示されているか
driver.find_element_by_id('id_name').is_displayed()
要素がアクティブかどうか
ボタンがグレーアウトして押せない場合などを判定できる。
driver.find_element_by_id('id_name').is_enabled()
要素が選択されているか
driver.find_element_by_id('id_name').is_selected()
ウインドウサイズの最大化
driver.maximize_window()
覚えておくと便利な小技
ChromeDriverのゴミインスタンスを一括kill
エクセルのVBAを使ったスクレイピングを書いている時なんかもアリがちなんですが、デバッグやらなんやらでコードを書いているうちに殺しきれなかったChrome Driverのインスタンスのゴミが大量にバックグラウンドに残っている時があります。
夢中になってコード書いてると、一面Chromedriver.exeで埋め尽くされていることなんかもあります。
seleniumの場合はほとんどありませんが、VBAの場合なんかだとメモリ圧迫されまくってIEが立ち上がらなくなってから気づくんですよね(笑)
Pythonならsubprocess モジュール使って直接殺していってもいいんですが、僕はbatファイルをあらかじめ作ってそれで一気にkillするようにしています。
@echo off set APP_NAME=chromedriver.exe for /f %%i in ('tasklist /fi "IMAGENAME eq %APP_NAME%" 2^>^&1') do ( if /i %%i==%APP_NAME% ( taskkill /im %APP_NAME% /f goto ENDLOOP; ) ) :ENDLOOP
一括でChromeDriverをkillできます。
selenium × Chrome によるログイン処理
requestsなどのHTTPの送受信のみを行うインターフェイスと違いブラウザの自動操作系のインターフェイスの利点の一つにログイン処理を楽に行えるという点があります。
そもそもJavaScriptによってBot判定がなされているサイトもあり、ブラウザ利用以外でのログインが難しい場合も多いです。
selenium を使ってChromeを操作すれば、それらの認証を通過して、ログインユーザーにしか扱えないページにBotアクセスすることが可能になります。
selenium × ChromeでYahoo!にログインする
ヤフーのログインフォームを入力するサンプルです。
from selenium import webdriver import time driver = webdriver.Chrome() #立ち上げ driver.get('https://login.yahoo.co.jp/config/login') #ログインページへ login_id = 'ログインid' login_pass = 'ログインpass' driver.find_element_by_css_selector('#username').send_keys(login_id) #id入力 driver.find_element_by_css_selector('#btnNext').click() #submitボタンをクリック time.sleep(1) driver.find_element_by_css_selector('#passwd').send_keys(login_pass) #passを入力 driver.find_element_by_css_selector('#btnSubmit').click() #submitボタンをクリック time.sleep(1)
これでヤフーにログインした状態のブラウザの状態を作り出すことが出来ます。
一行ずつ追っていけば、普通のマクロ的な動作しかしていないので、割と簡単ですね。
ログイン状態を維持したまま操作をrequestsで行う
seleniumによるログイン処理は、普通にブラウザからアクセスするのと同じ操作をトレースすることで可能になりますので、非常に実装が楽になります。
ただ、情報を収集したり、変化を監視するといった用途のBotを組んでいく場合、seleniumは動作の速度や安定性に欠ける部分があります。
ブラウザの描画が必要な分、多少仕方ない部分ではありますが、ログイン処理後の動作にJavaScriptのエンジンが必要でない場合、その先をrequestsによって行うことが出来ます。
それぞれのライブラリに得意な部分のみを担当させるイメージです。
- ログイン処理 → selenium × Chrome
- クロール処理→ requests × BeautifulSoup
HTTPと言うプロトコルは、一度のリクエストで完結し、前後の処理の相関を保持することが出来ません。簡単にいうと、一回ぽっきり。すぐに忘れます。つまり、HTTPはログインしたことなど、覚えていません。
では、何故ページをめくってWebサイトの中を回遊してもログイン状態が維持されているのか?というと、ログインセッションと言うのは基本的にCookieを使って管理されているからです。
Cookieと言うのはクライアント側(ブラウザ)に書き込まれる、メモ書きのようなもので、色々な用途に利用されますが、ログインの判定はその代表例です。
つまり、特定サイトにログインした状態のCookieがあり、尚且つ、そのCookieが有効期限内であれば、別のインターフェイスからそのサイトにアクセスしてもログイン状態を維持することが出来ます。
import requests session = requests.session() #sessionインスタンスを作成 #Chrome Driverが持っているCookieを全てsessionに引き渡す for cookie in driver.get_cookies(): session.cookies.set(cookie["name"], cookie["value"]) session.get('login後操作したいURL')
上記処理で、seleniumでログインした状態のChromeブラウザのクッキーをrequestsのsessionインスタンスに引き渡すことが可能です。
その後はログインユーザー用のページをsession.get()で取得可能になります。
サイトの構造によっては使えないパターンもありますが、多くのサイトで使えるはずです。
seleniumでユーザープロファイルを指定してChromeを立ち上げる
ここまで、seleniumを用いてログインの必要なページを操作する処理を紹介してきました。
が、普段からChromeをメインのブラウザとして使っているのであれば、面倒なログインの処理なんかはすっ飛ばして、ログインした状態を作り出すことが出来る方法があります。
見出しにもある通り、Chromeに備わっている、ユーザープロファイルと言う機能をオプションとして指定することによって、普段使っている状態のChromeブラウザをそのまま操作することが可能になります。
以下でどういうことなのか詳しく説明していきます。
seleniumでChromeを立ち上げた時
まずはユーザープロファイルの指定をせずに、seleniumからChromeを立ち上げた場合の挙動を見ていきます。
from selenium import webdriver driver = webdriver.Chrome()
ブックマークやユーザー名、拡張機能など、全てまっさらな状態で立ち上がります。
「Chrome は自動テスト ソフトウェアによって制御されています。」という、アラートも確認できます。
seleniumでユーザープロファイルを指定してChromeを立ち上げた時
次に、普段使っているChromeと全く同じ状態でseleniumから起動させる方法です。
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() PROFILE_PATH = r'C:UsersUserNameAppDataLocalGoogleChromeUser Data' driver = webdriver.Chrome(options=options)
seleniumを立ち上げたときの自動操作のメッセージが出ながらも、拡張機能やブックマークなどが表示されているのが確認できると思います。
ちなみにユーザープロファイルのパスは環境によって変わります。
- Chromeを起動する。
- URLバーに chrome://version と入力して開く。
- [プロフィール パス] の欄にパスが記述されているので確認する。
これでいつも使っている状態のChromeで自動操作を行うことが可能になります。
これで厄介な「reCAPTCHA」も突破しやすくなります。
突破しやすくなるというよりは、そもそもログインした状態を維持して立ち上げられます。(サイトやログイン方法によりますが。)
コメント