MENU

Python-seleniumではてなブログ自動投稿プログラムを作ってみる⑥~結合編その3~

こんにちは、のちたままです。

長らく続いたはてなブログ自動投稿プログラム製作ですが、今回で主な説明は終了です。

長々とお疲れ様でした。

それでは最後も張り切っていきましょう。

コード全体

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
import os
import sys


xpath=[
    '//*[@id="login-name"]',
    '//*[@id="container"]/div[1]/form[1]/div/div[2]/div/input',
    '//*[@id="option"]/input[2]',
    '//*[@id="admin-menu"]/ul/li[3]/ul[1]/li[2]/a',
    '//*[@id="title"]',
    '//*[@id="body"]'
]

xpath_app=[#公開
    '//*[@id="submit-button"]', 
]
xpath_save=[#下書き保存
    '//*[@id="editor-main"]/div/div[3]/div[2]/div[2]/span[1]/div/a', 
    '//*[@id="editor-main"]/div/div[3]/div[2]/div[2]/div/ul/li[2]/a',
    '//*[@id="editor-main"]/div/div[3]/div[2]/div[2]/span[1]/button'
]
xpath_reserve=[#予約投稿
    '//*[@id="editor-support-container"]/div/div[1]/div[2]/div[3]',
    '//*[@id="datetime"]/div[1]/input',
    '//*[@id="datetime"]/div[1]/div/span/input',
    '//*[@id="datetime"]/div[2]/p[2]/label/input',
    '//*[@id="editor-main"]/div/div[3]/div[2]/div[2]/span[2]/input'
]
def read_file(filename):
    with open(filename) as f:
        s=f.readline()
        d=f.readline()
        d=f.read()
    return s,d

def SEND_KEYS(drivers, xpath, inputtext):
    text=WebDriverWait(drivers, 30).until(
        expected_conditions.visibility_of_element_located((By.XPATH, xpath))
    )
    text.send_keys(inputtext)
    
def CLICK(drivers, xpath):
    button=WebDriverWait(drivers, 30).until(
        expected_conditions.visibility_of_element_located((By.XPATH, xpath))
    )
    button.click()
    
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(__file__)
    return os.path.join(base_path, relative_path)


def main(userid,userpw,day,time,option,filename):
    driver = webdriver.Chrome(resource_path('./driver/chromedriver.exe'))
    weburl='https://blog.hatena.ne.jp/'
    driver.get(weburl)
    
    SEND_KEYS(driver, xpath[0], userid)
    SEND_KEYS(driver, xpath[1], userpw)
    CLICK(driver, xpath[2])
    
    CLICK(driver, xpath[3])
    
    title,arch=read_file(filename)
    SEND_KEYS(driver, xpath[4], title)
    SEND_KEYS(driver, xpath[5], arch)
    
    if option==1:#公開
        CLICK(driver, xpath_app)
    elif option==2:#予約投稿
        CLICK(driver, xpath_reserve[0])
        SEND_KEYS(driver, xpath_reserve[1], day)
        CLICK(driver, xpath_reserve[2])
        for i in range(5):
            SEND_KEYS(driver, xpath_reserve[2], Keys.BACK_SPACE)
        SEND_KEYS(driver, xpath_reserve[2],times)
        CLICK(driver, xpath_reserve[3])
        CLICK(driver, xpath_reserve[4])
    else:#下書き保存
        for i in range(3):
            CLICK(driver, xpath_save[i])

メインとなるプログラムです。

今まではスクリプトと読んでいましたが、もう関数化してしまったのであまりスクリプトに見えないですね。

seleniumスクリプトって複雑なことをしているようで実は以下の3つで実現できます。

・要素の取得
・文字列を入力
・ボタンをクリック

なので「文字列の入力」と「ボタンをクリック」を関数化し、それぞれの関数に「要素を取得」処理を入れれば見やすくなるのです。

これが理解できればhatenaprogram.pyはほぼ100%理解できます。

それでは細かい説明に移ります。

細かい説明

xpathのリストについて

コード上部にxpathのリストが書いてあります。

seleniumの自動処理はもともとスクリプトなので、要素を取得する順番も決まっています。

ですので、xpathをリスト化して上から順番に使用すればスクリプトと同じ用に動かすことができます。

要素の取得についてはxpath以外、例えばidやclass_nameでも可能ですが、関数化するにあたってはすべてxpathで取得するようにしたほうがコードが見やすくなります。

実行時間に関しては考えていません。

実際に使ってみるとわかりますが、人間が体感できないような差になると思うので、ここでは気にしないことにします。

read_file関数について

read_file関数は指定されたファイルを読み取り、タイトルと中身を返す関数です。

def read_file(filename):
    with open(filename) as f:
        s=f.readline()
        d=f.readline()
        d=f.read()
    return s,d
引数:ファイル名
返り値:タイトル(文字列)、中身(文字列)

readline()は一行ずつ読み取る処理、read()は全体を読み取る処理です。

ですので、ここで想定されているファイルは、

・1行目にタイトル
・3行目以降に内容

という形式になっています。

ここの処理部を変えることでどんな内容のファイルに対応するか変えることができます。

自分の好み似合わせて作り変えてください。

SEND_KEYS関数について

def SEND_KEYS(drivers, xpath, inputtext):
    text=WebDriverWait(drivers, 30).until(
        expected_conditions.visibility_of_element_located((By.XPATH, xpath))
    )
    text.send_keys(inputtext)
引数:ドライバ、xpath、入力する文字列
返り値:なし

この関数はxpathで指定した空欄に文字列を入力する関数です。

text=から始まる3行は要素が取得できるまで待つ処理になります。

この処理がない場合、画面表示が間に合わず途中でエラーになるときがあります。

なるべく入れるようにしましょう。

今回は最大30秒待つようになっています。

CLICK関数について

def CLICK(drivers, xpath):
    button=WebDriverWait(drivers, 30).until(
        expected_conditions.visibility_of_element_located((By.XPATH, xpath))
    )
    button.click()
引数:ドライバ、xpath
返り値:なし

こちらは取得した要素のボタンをクリックする関数です。

先程のSEND_KEYS関数と主な処理は同じです。

処理としては最後の1行が変わっているだけです。

resource_path関数について

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(__file__)
    return os.path.join(base_path, relative_path)

こちらはwebdriverを読み取るときに使う処理です。

この処理はこのプログラムでつかうというよりは、Pyinstallerを使って.exeなどの実行ファイルを作るときに必要な処理になります。

詳しくは以下の記事を御覧ください。

www.zacoding.com

main関数について

def main(userid,userpw,day,time,option,filename):
    driver = webdriver.Chrome(resource_path('./driver/chromedriver.exe'))
    weburl='https://blog.hatena.ne.jp/'
    driver.get(weburl)
    
    SEND_KEYS(driver, xpath[0], userid)
    SEND_KEYS(driver, xpath[1], userpw)
    CLICK(driver, xpath[2])
    
    CLICK(driver, xpath[3])
    
    title,arch=read_file(filename)
    SEND_KEYS(driver, xpath[4], title)
    SEND_KEYS(driver, xpath[5], arch)
    
    if option==1:#公開
        CLICK(driver, xpath_app)
    elif option==2:#予約投稿
        CLICK(driver, xpath_reserve[0])
        SEND_KEYS(driver, xpath_reserve[1], day)
        CLICK(driver, xpath_reserve[2])
        for i in range(5):
            SEND_KEYS(driver, xpath_reserve[2], Keys.BACK_SPACE)
        SEND_KEYS(driver, xpath_reserve[2],times)
        CLICK(driver, xpath_reserve[3])
        CLICK(driver, xpath_reserve[4])
    else:#下書き保存
        for i in range(3):
            CLICK(driver, xpath_save[i])
引数:ユーザID、ユーザパスワード、年月日、時刻、公開設定、ファイル名
返り値:なし

main関数にはGUIから入力された値を引数として処理が行われています。

公開設定をif文で分けている以外はスクリプトと同じ流れになっているので、理解はしやすいと思います。

最後に

これでjsonファイル、GUIプログラム、メインプログラムが完成しました。

はてなブログ自動投稿プログラムは完成です。

お疲れさまでした。

何かあればコメントをお願いします。

それでは。