年々こうも暑さが厳しくなると、気になるのは冷房の使用に伴う「電気料金の増加」です。

東京電力の場合、電気料金はこれまで「紙の検針票」で確認していましたが、2021年3月分で終了となりました。4月分からは「Web検針票」で確認する必要があります。

Web検針票は、ページにアクセスして、契約情報(名義・お客様番号等)を入力すると電気料金を確認できます。今回はその作業をSelenium+Pythonで自動化します。確認した電気料金はExcelに保存するところまでプログラミングします。

東京電力に会員登録すると2回目からはログインすれば契約情報を入力せず確認できますが、今回は会員登録しないでプログラミングで省力化します。

本記事の目次

Seleniumについて

Seleniumにはいくつかのツールがありますが、今回は「WebDriver」を用います。Chromeなどのブラウザごとに配布されているWebDriverを公式サイトからダウンロードして利用します。WebDriverをPythonで操作して、ブラウザを自動操作します。Seleniumの使い方の基本は、以下の書籍などが参考になります。

Seleniumによるブラウザ操作の概要は、以下の記事でもチェックできます。

Pythonでブラウザを自動操作する方法

本記事のコードはWindows10に対応していますが、MacでもWebDriverをMac用に置き換えることで実行可能です。

まずは手動でWeb検針票から確認してみよう

プログラミングで自動化をする前に、まずは手動で操作してみます

Web検針票とは、「紙」ではなく「Web」で電気料金を確認する仕組みです。実際には、以下のWebページを操作して確認します。

電気料金・検針情報認証画面|東京電力エナジーパートナー株式会社(TEPCOEP)

電気料金を確認するまでの流れは、以下のように、1)Webページに契約情報を入力して「照会」ボタンをクリック、2)表示される「検針情報」のページで電気料金を確認します。

tepco flow

ここで、入力する契約情報は以下の5項目です。

  1. 事業所コード(xxx:3桁)
  2. お客様番号(xxxxx-xxxxx-x-xx:13桁)
  3. 住所
  4. 契約名義
  5. 電話番号

事業所コードとお客様番号の確認方法は、以下のページで説明されています。

利用方法のご案内|東京電力エナジーパートナー株式会社(TEPCOEP)

「照会」ボタンがクリックできない場合

未入力の欄があったり、入力方法が間違えていると「照会」ボタンはクリックできません。例えば、「契約名義」は「カタカナ」で入力しますが、「漢字」になっているとクリックできません。すべての項目を正しい方法で入力すると以下の状態に変わります。

buttons

プログラミングの準備

今回のプログラム(check_tepco_web_meter.py)は、以下のように適当な場所に作成した「tepcoフォルダー」に「WebDriver(chromedriver.exe)」と「エクセルファイル(電気料金.xslx)」と一緒に配置します。ここでWebDriverはdriverというサブフォルダーを作成してその中に入れます。

プログラムのフォルダー構成
  ├ tepco/
      ├ driver/
      │    └ chromedriver.exe
      │
      ├ 電気料金.xslxcheck_tepco_web_meter.py

プログラムはこのtepcoフォルダーをカレントにして実行することとします。

1.ライブラリ

今回のプログラムでは、「selenium 」と「openpyxl 」の2つのライブラリを使います。以下のようなコマンドでpip installを実行してインストールしておきます。

# Windowsの場合
py -m pip install selenium
py -m pip install openpyxl
# pythonのPATHが通っている場合
python -m pip install selenium
python -m pip install openpyxl
# Mac等
pip3 install selenium
pip3 install openpyxl

2.WebDriverのダウンロード

自動操作するブラウザに対応したWebDriverを以下のリンクのサイトからダウンロードして、今回のプログラムファイルと同じ階層にある「driverフォルダー」に配置します。

ブラウザ WebDriverのダウンロードページ
Chrome https://sites.google.com/a/chromium.org/chromedriver/downloads
Edge https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Firefox https://github.com/mozilla/geckodriver/releases
Safari https://webkit.org/blog/6900/webdriver-support-in-safari-10/

今回はChromeブラウザのWindows用のWebDriver(chromedriver.exe)を使用します。ここでは、Chromeのバージョンと一致するWebDriverを使うように気をつけてください。

3.エクセルファイル

結果のWebページから読み取った電気料金を書き込むためのエクセルファイル「電気料金.xlsx」を準備しておきます。以下のように、1行目に項目だけを入力したファイルを作成し、今回のプログラムファイルと同じ階層に保存しておきます。

電気料金.xlsx

電気料金.xlsx

「Web検針票」自動確認プログラムの作成

本プログラムは、以下のように各工程を関数に分けて作成します。今回の処理は、それぞれの関数を用いて「main()関数」の中に書き込みます。

  • 契約情報の入力:input_user_info()関数
  • 電気料金(検針情報)の読み取り:scraping_meter_info()関数
  • 電気料金のエクセルファイルへの書き込み:to_excel()関数

このプログラムファイルを実行すると、最後のif __name__ == '__main__':のブロックにあるmain()が呼び出され処理が実行されます。

今回のプログラムのコードは以下のようになります。

check_tepco_web_meter.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.common.exceptions import TimeoutException
import openpyxl
import time

# Web検針票ページのURL
TEPCO_URL = "https://www.kenshin.tepco.co.jp/Certification"

# 契約情報
OFFICE_CODE = "xxx"  # 事業所コード
ADDRESS_1 = "東京都"  # 都道府県
ADDRESS_2 = "千代田区" # 市区町村
ADDRESS_3 = "永田町"  # 町名
ADDRESS_4 = "1丁目"  # 字丁目
ADDRESS_5 = "xx-xx"  # 番地 / 号(ハイフン使用)
USER_NAME = "サンプル カブ"  # 契約名義(全角カタカナ)
PHONE_NUM = "xx-xxxx-xxxx"  # 電話番号(ハイフン使用)
# 契約種別:お客様番号(ハイフン使用)
USER_CODES = {  
    "低圧電力": "xxxxx-xxxxx-x-xx",
    "従量電灯B": "xxxxx-xxxxx-x-xx"
}


def main():
    driver_path = "driver/chromedriver.exe"
    driver = webdriver.Chrome(executable_path=driver_path)
    driver.implicitly_wait(10)

    for key, code in USER_CODES.items():
        driver.get(TEPCO_URL)
        contents = input_user_info(driver, code)
        if contents is None:
            continue
        info_list = scraping_meter_info(contents)
        to_excel(key, info_list)
        # 3秒間、結果画面を表示
        time.sleep(3)

    driver.quit()


def input_user_info(driver, user_code: str):
    """契約情報入力"""
    wait = WebDriverWait(driver, 5)

    # 事業所コード
    driver.find_element(By.ID, "officeCode").send_keys(OFFICE_CODE)

    # お客様番号
    for i, code in enumerate(user_code.split('-')):
        tag_id = f"visitNumber{i + 1}"
        driver.find_element(By.ID, tag_id).send_keys(code)

    # 都道府県
    driver.find_element(By.ID, "selectPrefecture").send_keys(ADDRESS_1)

    # 市区町村
    select = Select(wait.until(ec.element_to_be_clickable((By.ID, "selectCity"))))
    select.select_by_visible_text(ADDRESS_2)

    # 町名
    select = Select(wait.until(ec.element_to_be_clickable((By.ID, "selectAddress1"))))
    select.select_by_visible_text(ADDRESS_3)

    # 字丁目
    select = Select(wait.until(ec.element_to_be_clickable((By.ID, "selectAddress2"))))
    select.select_by_visible_text(ADDRESS_4)

    # 番地/号
    driver.find_element(By.ID, "address_banchi").send_keys(ADDRESS_5.split('-')[0])
    driver.find_element(By.ID, "address_gou").send_keys(ADDRESS_5.split('-')[1])

    # 「会社・店舗」ボタンをクリック(「一般家庭」の場合はこの行を削除)
    driver.find_element(By.CSS_SELECTOR, "label.btn[for='corporation']").click()

    # 契約名義(「一般家庭」の場合はこの行を書き換える)
    wait.until(ec.visibility_of_element_located((By.ID, "companyNm"))).send_keys(USER_NAME)

    # 電話番号
    for i, num in enumerate(PHONE_NUM.split('-')):
        tag_id = f"phoneNumber{i + 1}"
        driver.find_element(By.ID, tag_id).send_keys(num)

    # 「照会」ボタンをクリック
    wait.until(ec.element_to_be_clickable((By.ID, "inquiry"))).click()

    # >> 「検針情報」のページに遷移する

    # コンテンツのdiv要素
    contents = None
    retry_times = 3
    for i in range(retry_times):
        try:
            # ページが遷移するのを待機してからコンテンツのdiv要素を取得
            contents = wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "div.info-content")))
        except TimeoutException:
            if i + 1 < retry_times:
                # ページが遷移しない場合は、再度「照会」ボタンをクリックする
                print("照会ボタンのリトライ :", i + 1)
                wait.until(ec.element_to_be_clickable((By.ID, "inquiry"))).click()
        else:
            break
            
    return contents


def scraping_meter_info(contents):
    """検針情報ページのコンテンツdivから情報を読み取る"""
    # コンテンツdiv要素の中のセクションdiv要素
    sections = contents.find_elements(By.CSS_SELECTOR, "div.info-section")

    # 請求金額
    price = sections[1].find_element(By.CSS_SELECTOR, "p.info-price").text
    # 使用量
    volume = sections[2].find_element(By.CSS_SELECTOR, "p.info-price").text
    # 振替予定日
    pay_date = sections[1].find_elements(By.TAG_NAME, "td")[0].text
    # 使用期間
    use_date = sections[2].find_elements(By.TAG_NAME, "td")[0].text

    return [use_date, price, volume, pay_date]


def to_excel(use_type, info_list):
    """エクセルに電気料金を書き込む"""
    file_name = "電気料金.xlsx"
    wb = openpyxl.load_workbook(file_name)
    ws = wb["Sheet1"]
    new_row = [use_type] + info_list
    # 末尾に追加
    ws.append(new_row)
    wb.save(file_name)


if __name__ == '__main__':
    main()

コードの上部にある契約情報の部分は、自分の内容に書き換えてください。手動でうまく実行できたときの内容をそのまま用います。「全角」と「半角」も違わないように気を付けてください。

上記のコードは、会社や店舗で大型エアコンなどを使用し「低圧電力」と「従量電灯」の2つの契約がある場合に対応していますので、以下の点に注意して適宜書き換えるようにしてください。

※ 契約種別が1つの場合は、USER_CODES は {"従量電灯B": "xxxxx-xxxxx-x-xx"} のように1つの要素にしてください。

※ 「一般家庭」の場合は、「会社・店舗」ボタンをクリックするコードは削除し、契約名義を入力するコードを以下のように書き換えてください。' 'は全角スペースなので気をつけてください。

# 契約名義(USER_NAMEには"セイ メイ"を代入)
driver.find_element(By.ID, "first_name").send_keys(USER_NAME.split(' ')[0])
driver.find_element(By.ID, "last_name").send_keys(USER_NAME.split(' ')[1])

プログラミングのポイント

入力に伴い順次更新されるページ

今回のWebページは、ボタンやドロップダウンリストといったHTML要素の状態が入力に伴い順次更新されるようになっています。

例えば、「都道府県」のドロップダウンリストで「東京都」を選択すると「市区町村」には東京都の市区町村のリストがサーバーから非同期通信により更新されます。さらに、「市区町村」で「新宿区」を選択すると「町名」には新宿区の町名のリストが更新されます。

つまり、入力に応じて次のHTML要素が更新されるまで待ち処理を行う必要があります。

待ち処理はSeleniumのWebDriverWait()で設定

Seleniumでは、HTML要素が見つからない場合の待ち時間をimplicitly_wait()で設定できますが、さらにHTML要素ごとに詳細な待ち処理を設定するにはWebDriverWait()を用います。

WebDriverWait()で待ち時間を設定したら、until()によりHTML要素がどのような状態になるまで待機するのかを指定します。この状態は以下のようなexpected_conditionsを用います。expected_conditionsは、selenium.webdriver.supportからインポートしておきます。

expected_conditions 説明
element_to_be_clickable 要素がクリック可能な状態になるまで待機する
visibility_of_element_located 要素が存在し、表示される状態になるまで待機する
presence_of_element_located 要素が現れるまで待機する

until()は待機が完了するとそのHTML要素を返します。ドロップダウンリストの場合は、その要素をSelect()でSelectオブジェクトにしてからselect_by_visible_text()で項目を選択します。一方、その要素をクリックする場合は、until()の後にclick()を実行するだけでできます。

「照会」ボタンのリトライ

「照会」ボタンがクリックされないと電気料金が表示される次のページに遷移しません。「照会」ボタンは入力内容の検証(Validation)が完了するまでクリックできる状態にならないようになっています。ここで、待ち処理のタイムアウトが発生してしまうケースが確認されたので、今回はこのボタンのクリック3回までリトライする処理を加えています。

プログラムの実行

IDLEなどのIDEで、先述のフォルダー構成で配置したプログラムファイルを開いて実行します。コマンドプロンプトなどから実行する場合は、プログラムファイルがあるフォルダーをカレントにしてから実行してください。

プログラムの実行結果

プログラムを実行するとChromeブラウザが自動的に起動します。契約情報の入力、照会ボタンのクリックが行われ、電気料金が表示されているページが切り替わります。プログラムが終了するとChromeブラウザが閉じられます。

ページに表示された電気料金は自動的に読み取られ、以下のように「電気料金.xlsx」に書き込まれます。プログラムを実行するたびに、末尾に電気料金が追加されるので、月末ごとに実行すると電気料金を継続して記録しておくことができます。

電気料金.xlsx(プログラム実行後)

result xlsx

最後に

今回のWeb検針票ページは、ユーザーが正しく入力できるように様々なサポートが施されています。そのため、非同期通信により都度更新や検証が行われています。このようなページの操作を自動化するには、今回のようにSeleniumを利用するのが便利です。