gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

【Python】【Selenium】 Webサイトから自動でファイルDLする その1

2019/03 超追記

概要

「Webサイトにアクセス→ログイン→ボタンを押してファイルをDL」

みたいな処理を自動化したいと思いました。

とりあえず基本を説明し、どんどん応用編に進んでいきたいと思います。

環境構築

PythonSeleniumを使用します。 Pythonは環境構築が楽で、パッケージがいろいろあるのでいいですよね。

OSはWin10です。 ブラウザはクロームを使用します。

Pythonの環境

初めて環境構築する場合は、 以下のリンクからインストーラをDLして実行すると、 Python、エディタ(Spyder)など全部入ります。

初めて入れるならPython 3.*版にしましょう。 2.*版は2020年にサポートが終了します。 * https://www.anaconda.com/download/

ちなみに私はPython2のAnacondaが入っている状態でさらにPython3版をインストールしたため、 パスがめちゃくちゃになりました。

解決方法としては、環境変数を適当に消してから再インストールすると正常に動作するようになりました。

Seleniumのインストール

コマンドプロンプト

pip install selenium

以上。

次、ここからwebdriverをDLして、適当なディレクトリに置いておきます。 * https://sites.google.com/a/chromium.org/chromedriver/home

パスは後で使用するので覚えておきましょう。

ちなみに、これをDLしてない or スクリプト上でパス指定が間違っている場合、以下のエラーが出ます。

WebDriverException: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home

基本その1 DOM要素の取得

DOM要素(HTMLタグで囲まれた要素)を取得して、クリックや入力を行います。

※headタグ省略。実際のソースを見たい方は上記のURL参照

<body>
    <h1>Selenium使用例 初級</h1>

    <h2 id="result">html要素選択結果がここにセットされます</h2>
    <h2>ランダム文字列→ <span id="random_text"></span></h2>

    <div>
        <ui>
            <li><input id ="aaa" type="button" value="ID指定" onclick="show_result('ID指定');"/></li>
            <li><input class="bbb" type="button" value="CLASS指定" onclick="show_result('CLASS指定');"/></li>
            <li><input name="ccc" type="button" value="NAME指定" onclick="show_result('NAME指定');"/></li>
            <li><input type="button" value="XPATH指定" onclick="show_result('XPATH指定');"/></li>
            <li><input type="button" value="内部テキスト指定" onclick="show_result('内部テキスト指定');"/></li>
            <li><a href="#" onclick="show_result('リンク名称指定');">リンク名称指定をする</a></li>
        </ui>
    </div>

    <form>
    <p>性別は?</p>
    <input type="radio" name="gender" value="male">男性
    <input type="radio" name="gender" value="female">女性

    <p>年齢は?</p>
    <select name="old">
        <option value="item1">0才-10才</option>
        <option value="item2">11才-50才</option>
        <option value="item3">51才-</option>
    </select>

    <p>名前は?</p>
    <input type="text" name="yourname">

    <p>このサイトはテストサイトということは理解していますよね?</p>
    <input type="checkbox" name="understand" value="yes">理解してます

    </form>
</body>
$(document).ready( function(){
    show_random_text();
    show_random_texts();
});

function show_result(result_text){
    elem = $('#result'); 
    elem.text(result_text);
}

function show_random_text(){
    
    random_text = get_random_text();
    
    elem = $('#random_text'); 
    elem.text(random_text);
}

function show_random_texts(){
    
    var a = $('#random_texts').find('li')
    
    a.each(function(index, elem){
        $(elem).text(get_random_text());
    })
}

function get_random_text(){
    return Math.random().toString(36).slice(-8)
}
    
function show_alert(){
    
    var date = new Date();
    var hour = date.getHours();
    var min = date.getMinutes();
    var sec = date.getSeconds();

    window.alert("現在は" + hour + "時" + min + "分" + sec + "秒です");
    
    show_result("アラートを表示しました");
}

各DOM要素をいろいろな方法で指定、操作します。

# -*- coding: utf-8 -*-
"""
Created on Sat Mar  9 16:28:14 2019

@author: gologius
"""

from selenium import webdriver
import time

CHROME_DRIVER_PATH = "chromedriver_win32\chromedriver.exe"
URL = "https://gologius.github.io/test/selenium_lecture_1.html"

driver = webdriver.Chrome(CHROME_DRIVER_PATH)
driver.implicitly_wait(10) #sec
WAIT_TIME = 1

def process_1():
    """
    シンプルな要素選択
    """
    driver.get(URL)

    #要素を指定→クリック
    elm = driver.find_element_by_id("aaa")
    elm.click()
    time.sleep(WAIT_TIME)

    elm = driver.find_element_by_class_name("bbb")    
    elm.click()
    time.sleep(WAIT_TIME)

    elm = driver.find_element_by_name("ccc")
    elm.click()
    time.sleep(WAIT_TIME)

    elm = driver.find_element_by_xpath("//div/ui/li/input[@value='XPATH指定']")
    elm.click()
    time.sleep(WAIT_TIME)

    elm = driver.find_element_by_xpath("//input[contains(@onclick, '内部テキスト')]")
    elm.click()    
    time.sleep(WAIT_TIME)

    elm = driver.find_element_by_link_text("リンク名称指定をする")
    elm.click()    
    time.sleep(WAIT_TIME)

    #要素の値を取得
    elm = driver.find_element_by_id("random_text")    
    print(elm.text)
    elm = driver.find_element_by_id("aaa")
    print(elm.get_attribute("onclick"))

    #ラジオボタンを指定→クリック
    elm = driver.find_element_by_xpath("//input[@value='female']")
    elm.click()    
    time.sleep(WAIT_TIME)

    #プルダウンを指定→クリック
    elm = driver.find_element_by_xpath("//select/option[contains(@value, 'item2')]")
    elm.click()
    time.sleep(WAIT_TIME)

    #テキストボックスを指定→値入力
    elm = driver.find_element_by_name("yourname")
    elm.send_keys("吾輩は猫である")
    time.sleep(WAIT_TIME)

    #チェックボックスを指定→クリック
    elm = driver.find_element_by_xpath("//input[@value='yes']")
    elm.click()    
    time.sleep(WAIT_TIME)

    return

process_1()    

実行すると下記のように、自動で要素をクリック、入力していきます。 f:id:gologius:20190311234815g:plain

詳細説明

driver.implicitly_wait(WAIT_SEC) はかなり重要です。 要素がなくても'WAIT_SEC'秒だけ待ち続けます。 これを指定することで、読み込みが完了しておらず、要素がないのでエラーになる、といった事態を避けられます。

また、driver.find_element_by_xpath()xpathを指定して要素を取得できます。 xpathの説明はこの記事が詳しいです。 上の例だと、

  • //input[@id='page'] → ページ全体(//)の中で<input id="page"></input>を取得
  • //a[@onclick='download(); → ページ全体(//)の中で<a onclick="download"></a>を取得

となります。 要素取得後はsend_keys()で値セット、click()で要素をクリックしています。 ちなみにsend_keys()は複数同時入力(Ctrl+Cなど)に対応しているので、複数形(keys)になってるみたいです。今回は使ってませんが。

まとめ

個人的にはdriver.implicitly_wait()が便利。 いちいち読み込み完了を検知するスクリプトを書かなくともよいのは便利。

その2に続く

次回は、ウインドウ、アラート、ファイルダウンロードを主に説明します