S0-ma's Blog

s0-maのブログです。

でんき家計簿 (くらしTEPCO) をスクレイピングしてみた (30分毎電力データ)

Abstract

PythonからくらしTEPCO経由ででんき家計簿にログインして、 自宅の30分電力使用量データを遡れるだけ取得してみた。

Introduction

前回からの続きです。

くらしTEPCOのアカウントを使ってでんき家計簿にログインし、30分電力値データを遡れるだけ取得してみた。

Setup and Results

前準備

  • 前回と同じ

どこにアクセスすればデータが取れるのか?

前回の時点で、月毎電力データのページまではたどり着けた。ここから更に進めてみる。

時間別ページに飛ぶためのリンクリンクはこんな感じ。

<a href="javascript:void(0);" onclick="
    createHiddenTag(fnjdoc.forms['syo_electricUsageAmountActionForm'], new Array('xxxxxxxx','yyyyyyyyyyyyy'), new Array('key.officeCd', 'key.visitNum'), true);
    return submitForm(fnjdoc.forms['syo_electricUsageAmountActionForm'], '/dk/syo/electricUsageAmount/goElectricUsage30minGraph', null);">

月別使用量ページの表示とほぼ同じだが、 追加でofficeVisitCd = xxxxxxxx/yyyyyyyyyyyyy と、ヘッダのRefererとして遷移元のurlを指定しないといけないみたい。

htmlからの数値読み出しは、でんき家計簿側のコードが変更されないことを祈って、形式固定の力技で抜き出してみた。 (本当はjsのパーサと書けばよいのだろうけど...)

データが抜き出せたら、前日データの表示用のリンクから同様のことを繰り返す。

コード

# 30分値表示ページから、数値データを抜き出す
def getData(html_text):
    def getCDATA(text):
        ret = ""
        isInCDATAArea = False
        for l in text.split("\n"):
            l = l.strip()
            if (l == "// <![CDATA["):
                isInCDATAArea = True
            elif (l.strip() == "// ]]>"):
                isInCDATAArea = False
        
            if(isInCDATAArea):
                ret += l + "\n"
        
        return ret
    
    d = {}
    function = ""
    for l in getCDATA(html_text).split("\n"):
        
        if(l.startswith("function")):
            function = l.split("function")[1].split("()")[0].strip()
            if(function.startswith("vbar_usage_grp")):
                d[function] = {}
            
        if(function.startswith("vbar")):
            if(l.startswith("var items = ")):
                d[function]["items"] = json.loads(l.split("=")[-1].strip(";"))
    
    html = BeautifulSoup(html_text, "html.parser")
    date_str = html.find(id="motion_area1").find_all("td")[1].contents[0]
    
    date = datetime.datetime.strptime(date_str.split("\u3000")[0], '%a %b %d %H:%M:%S JST %Y')
    
    return (date, d["vbar_usage_grp"]["items"][0][1:-1])

# 前日移動に必要な情報
def getParamsToMovePrev(html_text):
    html = BeautifulSoup(html_text, "html.parser")
    link_js = html.find(id="motion_area1").find("a")["onclick"]
    #print(link_js)

    key_d = link_js.split("'")[3]
    key_date = link_js.split("'")[5]
    post_to = link_js.split("'")[13]

    return(key_d, key_date, post_to)

# 前日の30分データ表示ページ を取得
def getPrev(key_d, key_date, post_to):
    param = {
        'key.d': key_d,
        'key.date': key_date
    }

    header = {
        'Referer':data_page.url
    }

    data_page_30min = session.post(
            'https://www.kakeibo.tepco.co.jp'+post_to, data=param, headers=header)
    data_page_30min.encoding = data_page_30min.apparent_encoding
    
    return data_page_30min.text

#取得可能な30分値データを、全て取得
current_page = data_page_30min.text
with open("./usage30min.csv", "w") as f:

    while True:
        d, date, post_to = getParamsToMovePrev(current_page)
        current_page = getPrev(d, date, post_to)
        data = getData(current_page)
        if(len(data[1]) != 48):
            break
    
        f.write(data[0].strftime('%Y-%m-%d'))
        for usage in data[1]:
            f.write(" ," + str(usage))
        f.write("\n")
        f.flush()

実行結果

くらし家計簿から、30分毎電力値を取得し、csvファイルに書き出した。

Reference

github.com

www.kurashi.tepco.co.jp www.kakeibo.tepco.co.jp

でんき家計簿 (くらしTEPCO) をスクレイピングしてみた (月毎電力データ)

Abstract

PythonからくらしTEPCO経由ででんき家計簿にログインして、 自宅の月毎電力使用量データを取得してみた。

Introduction

自宅の電力計がスマートメーター化され、東京電力くらしTEPCOというサイトから自宅の30分電力使用量が取得できるらしい。 ただ私の場合、契約が電力自由化前のものであるため、調べてみるとでんき家計簿という別のサイトを使わないと電力値の取得ができないことが判明。

幸いくらしTEPCO経由ででんき家計簿にログインする方法が用意されていた。 くらしTEPCOのアカウントを使ってでんき家計簿ログインし、手始めに月毎電力値データ表示ページを取得してみた。

Setup and Results

前準備

  • 前回と同じ

どこにアクセスすればデータが取れるのか?

くらしTEPCOのログイン後マイページにはでんき家計簿へのリンクがある。

f:id:s0-ma:20190211150914j:plain
くらしTEPCO でんき家計簿リンク

この部分のHTMLはこんな感じ。

f:id:s0-ma:20190211151350j:plain

くらしTEPCOにログインした状態で https://www.kakeibo.tepco.co.jp/pf/ja/pco/mypage/redirect-sso.page?sitekbn=kakeibo に行くと、でんき家計簿の会員ホームに飛ばされるということみたい。

未ログイン状態だと普通にログインフォームが表示されるので、どうやらでんき家計簿くらしTEPCOと同じ要領でやれば良さそう。

でんき家計簿ログイン後に、電力使用値を表示するページに移動するための「使用量と料金をグラフで見る」リンクはこんな感じ。

<a href="javascript:void(0);" onclick="
    createHiddenTag(fnjdoc.forms['com_menuActionForm'], new Array('xxxxxxxx','yyyyyyyyyyyyy','amount'), new Array('key.officeCd', 'key.visitNum', 'key.display'), true);
    return submitForm(fnjdoc.forms['com_menuActionForm'], '/dk/com/menu/goElectricUsageAmount', null);
">

key.officeCd = xxxxxxxxxとkey.visitNum=yyyyyyyyyyyyyを/dk/com/menu/goElectricUsageAmountにpostすればよいみたい。 これで月別使用量のページが得られる。

データ自体はhtml内部の// <![CDATA[と書かれた領域に埋め込まれていた。 この領域をひとまず切り出して、後は力技で該当部分を切り出してみる。

コード

# くらしtepcoからでんき家計簿にログイン
param = {
        'ACCOUNTUID': username,
        'PASSWORD': password,
        'HIDEURL': '/ls/pf/ja/pco/mypage/redirect-sso.page?sitekbn=kakeibo',
        'LOGIN': 'EUAS_LOGIN',
        }

header = {
        'Referer': 'https://www.kakeibo.tepco.co.jp/dk/com/menu/'
        }

login = session.post(
        'https://www.kurashi.tepco.co.jp/kpf-login', data=param, headers=header)
login.encoding = login.apparent_encoding

# 月ごとデータへの移動リンクで発火するjs関数から、必要な情報を抜き出す
html = BeautifulSoup(login.text, "html.parser")
link_js = html.find(id="frame3").find(class_="box01 box firstBox").find("a")["onclick"]
#print(link_js)
key_officeCd = link_js.split("'")[3]
key_visitNum = link_js.split("'")[5]
key_display = link_js.split("'")[7]
post_to = link_js.split("'")[17]

# 月ごとデータの表示ページ に移動
param = {
        'key.officeCd': key_officeCd,
        'key.visitNum': key_visitNum,
        'key.display': key_display
}

header = {
        'Referer':login.url
        }

data_page = session.post(
        'https://www.kakeibo.tepco.co.jp'+post_to, data=param, headers=header)
data_page.encoding = data_page.apparent_encoding

# CDATA領域を切り出し
def getCDATA(text):
    ret = ""
    isInCDATAArea = False
    for l in text.split("\n"):
        l = l.strip()
        if (l == "// <![CDATA["):
            isInCDATAArea = True
        elif (l.strip() == "// ]]>"):
            isInCDATAArea = False
        
        if(isInCDATAArea):
            ret += l + "\n"
        
    return ret

# 月毎データを抜き出す
d = {}
function = ""
for l in getCDATA(data_page.text).split("\n"):
    
    if(l.startswith("function")):
        function = l.split("function")[1].split("()")[0].strip()
        if(function.startswith("vbar")):
            d[function] = {}
        
    if(function.startswith("vbar")):
        if(l.startswith("var items = ")):
            d[function]["items"] = l.split("=")[-1]

        if(l.startswith("x:")):
            d[function]["x"] = json.loads(l.split(":")[-1])
        
        if(l.startswith(",y:")):
            d[function]["y"] = json.loads(l.split(":")[-1])

# 表示
for f in d:
    print(f)
    for k in d[f]:
        print("\t", k, "\t", d[f][k])

実行結果

くらし家計簿から、月毎電力値の表示ページのhtmlを取得し、書かれているデータを抜き出すことができた。

Reference

github.com

www.kurashi.tepco.co.jp www.kakeibo.tepco.co.jp

PythonでくらしTEPCOをスクレイピングしてみた

Abstract

PythonからくらしTEPCO|東京電力のご家庭向け無料Webサービスにログインして、ページ内のユーザー情報を取得してみた。

Introduction

最近、自宅の電力計がスマートメーター化された。で調べてみると、使用電力の30分値が計測されていてデータも取得できるらしい。 www.tepco.co.jp どうせならデータをすべて手元に置いて、分析(の真似事)をしてみたい。 まずはPython経由でくらしTEPCOにログインし、ログイン後に表示されるページのデータを取得する方法を調べてみた。

Setup and Results

前準備

  • htmlを解析するため、Beautifulsoupというライブラリをインストール。
pip install beautifulsoup4
  • くらしTepcoのアカウントを作成しておく。

  • ログイン情報は設定ファイルから読み込む方式とした。 (本当は環境変数にしたほうが良いのでしょうが。) .passwordという名前で、くらしTEPCOにログインするためのIDとPasswordを書いた以下の設定ファイルを用意しておく。

[settings]
username = my_username
password = my_password

どこにアクセスすればログインできるのか?

https://www.kurashi.tepco.co.jp/のログインフォームを見てみるとこんな感じ。

f:id:s0-ma:20190211004345j:plain
くらしTEPCO ログインフォーム

要は、https://www.kurashi.tepco.co.jp/kpf-login に対して、

  • ACCOUNTUID:
  • PASSPWORD:
  • HIDE_URL: /pf/ja/pc/mypage/home/index.page?
  • LOGIN': EUAS_LOGIN

をPOSTしてやれば良いみたい。

コード

import configparser
import requests
from bs4 import BeautifulSoup

#IDとパスワードをファイルから読み込み
config = configparser.ConfigParser()
config.read('.password', 'UTF-8') 
username = config.get("settings", 'username')
password = config.get("settings", 'password')

# くらしtepcoにログイン
session = requests.Session()
param = {
        'ACCOUNTUID': username,
        'PASSWORD': password,
        'HIDEURL': '/pf/ja/pc/mypage/home/index.page?',
        'LOGIN': 'EUAS_LOGIN',
        }

login = session.post(
        'https://www.kurashi.tepco.co.jp/kpf-login', data=param)
login.encoding = login.apparent_encoding

#ログインページのhtmlから、フッタに書いてあるユーザー情報を取り出してみる
html = BeautifulSoup(login.text, "html.parser")
for element in html.find("footer").find_all("input"):
    print(element["name"], element["value"])

実行結果

契約者名とか契約者住所が取得できた。

Conclusion

これでログインが必要なWebサイトであっても、ログイン後の情報がPython経由で取得することができた。 実際のブラウザ上でどのようなアクセスをしているかを一つ一つ確認して、それをプログラム上で再現していく方法が一番早かった。 次は、使用電力量を取得するところまで試してみたい。

Reference

github.com

www.kurashi.tepco.co.jp

www.crummy.com

Python+jupyter+plotlyでインタラクティブなグラフを書いてみた

Abstract

jupyter notebook上でPlotlyというグラフライブラリを使い、3次元データをインタラクティブに可視化してみました。

Introduction

Pythonでグラフライブラリと言うと、真っ先に出てくるのがmatplotlib。 ただやはり、グラフを描画することに特化しているのか、プロットしたものをズームするとかマウスオーバーで値を表示とかがサクッとはできないみたい。 そんな中で、Plotlyという良さげなライブラリを見つけたので試してみた。

Setup and Results

Plotlyのインストール。

pip install plotly

プロットする内容

プロットする関数自体は、以前の記事と全く同じ。 あと折角なので、自作したfloat拡張range関数も使ってみた。

コード

import math
from plotly.offline import iplot, init_notebook_mode
import plotly.graph_objs as go

init_notebook_mode(connected=True) 

x = [_x for _x in drange(0, math.pi, math.pi/100)]
y = [_y for _y in drange(0, math.pi, math.pi/100)]
z = []

for _y in y:
    z.append([])
    for _x in x:
        z[-1].append(math.sin(3*_x) * math.sin(2*_y))
        
data = [
    go.Heatmap(
        x=x,
        y=y,
        z=z,
        colorscale='jet',
    )
]

iplot(data)

実行結果

カーソル位置のデータ値を表示したり、ズームやオートスケールがブラウザ上のUI操作で可能。 f:id:s0-ma:20190209225542g:plain:w560

Conclusion

jupyter上で使うなら、matplotlibよりこちらのほうが好みかも。デモもいろいろあるようなので、そのうち試してみようと思います。

Reference

github.com

plot.ly

Pythonのrange関数をfloat型に拡張してみた

Abstract

Pythonのrange関数はInt型しか扱えない。0.1 ステップ刻みのようなfloat型でも同じことをやりたかったので、作ってみた。

Introduction

Pythonのrange関数は、整数値にしか使えない。 float型でやろうとするとnumpyのarrange関数を使うという手があるが、そのためだけに "import numpy"するのも気が引ける。

検索すると、以下のような関数を自分で定義するというのが割と知られているらしい。

def frange(start, end, step):
    for i in range(int((end-start)/step)):
        yield start + i*step

が、これを実際につかってみると、丸め誤差の影響が出るのであまり美しくない。

for f in frange(1.3, 2.1, 0.1):
    print(f)
    
#1.3
#1.4000000000000001
#1.5
#1.6
#1.7000000000000002
#1.8
#1.9000000000000001
#2.0

やりたいこと

float型に拡張したrange関数で、かつ、丸め誤差が乗らないようなものを作りたい。

コードと実行結果

from decimal import Decimal
def drange(start, end, step):
    start = Decimal(str(start))
    end = Decimal(str(end))
    step = Decimal(str(step))

    for i in range(int((end-start)/step)):
        yield float(start + i*step)
for f in drange(1.3, 2.1, 0.1):
    print(f)
#1.3
#1.4
#1.5
#1.6
#1.7
#1.8
#1.9
#2.0

Conclusion

今回は、一度Decimal型に内部で変換して扱うことで丸め誤差を抑えた。 Pythonの標準ライブラリの一部なので、numpyのようにインストールせずとも使えるのがメリット。

Reference

github.com

docs.python.jp

Python+jupyter+matplitlibでヒートマップを書いてみた

Abstract

matplotlibのヒートマップを使い、jupyter notebook上で3次元データを可視化してみました。

Setup and Results

プロットする内容

x, yが100x100の範囲について、z=sin(3πx/100) + sin(2πx/100)という関数をプロットしてみる。

コード

%matplotlib inline
import math
import matplotlib.pyplot as plt

x = []
y = []
z = []

for i in range(0,100):
    x.append([])
    y.append([])
    z.append([])
    for j in range(0,100):
        x[i].append(i)
        y[i].append(j)
        z[i].append(math.sin(3*math.pi *i/100) * math.sin(2*math.pi *j/100))
        
plt.pcolormesh(x,y,z, cmap="jet")
plt.show()

実行結果

f:id:s0-ma:20190209184241p:plain
matplotlibでheatmapをプロット

Conclusion

x, y共に2次元配列で渡すのがコツ。オプションで最大最小値やカラーマップも変更できるみたい。

Reference

github.com matplotlib.pyplot.pcolormesh — Matplotlib 3.0.2 documentation

jupyter notebookでPythonプログラミングしてみた

Abstract

jupyter notebookというPythonの実行環境で、hello worldをやってみました。

Setup and Results

jupyterのインストール

ターミナルを開いて以下のコマンドでjupyterをインストール。

pip install jupyer

インストールができたら、適当に作業ディレクトリを作成して以下を実行。

jupyter notebook

ブラウザが開くので、あとは新しくPython3のnotebookを作成するとPythonでコードが書けるようになる。

コード

print("hello world!")

実行結果

実行したいコードが書かれているセルにカーソルをあわせて、Ctrl+Enterで実行。 ブラウザ上で"hello world!" と表示された。

f:id:s0-ma:20190209180033j:plain
hello world on jupyter notebook

Conclusion

jupyterすごい。コマンドラインで実行するよりも簡単だし、何よりgithubがノートブック形式に対応しているので、やったことがgithub上でそのまま見られる。

Reference

github.com