2
1

【SUMUMA】Pythonで家計簿データを取得

Posted at

SUMUMAという家計簿サービスがあり愛用しています。csvの入出力機能付き・API対応で、なんと無料で使えます。自分は普段スマホで記録→月末にpythonでAPIを用いてデータ取得・まとめという形で利用しているので、今回はその方法を紹介したいと思います。

はじめに

こちらのサイトに飛んで、Authorizeボタンをクリックします。SUMUMAのメールアドレスとパスワードを入力してAuthorizeしたら、試しにexpendituresのGETを開いて、Try it out→pageに1を入力→Executeとしてみます。すると、

curl -X 'GET' \
  'https://sumuma.com/api/expenditures/?page=1' \
  -H 'accept: application/json' \
  -H 'authorization: [ここの文字列を記録しておく]' \
  -H 'X-CSRFToken: [ここの文字列を記録しておく]'

という出力とRequest URL、1page目の家計簿データが画面上に出力されます。入出力のイメージはざっとこんな感じです。

pythonでAPIを叩く

自分は以下のようなclassを作って使っています。initの中身は書き換えてください。

import requests
import pandas as pd

class sumumaAPI:
    
    def get_requests(self, url, page):
        res = requests.get(f'{url}/?page={page}', headers=self.headers)
        return res.json()['results']
    
    def __init__(self):
        # 初期設定
        self.accept = 'application/json'
        self.authorization = '先程のauthorization:の文字列'
        self.token = '先程のX-CSRFToken:の文字列'
        self.headers = {'accept':self.accept, 'authorization':self.authorization, 'X-CSRFToken':self.token}
        # url
        self.url = 'https://sumuma.com/api'
        self.url_expenditures = f'{self.url}/expenditures'
        self.url_incomes = f'{self.url}/incomes'
        self.url_categories = f'{self.url}/categories'
        # 分類
        df = []
        for dic in self.get_requests(f'{self.url_categories}', 1):
            df.append((dic['label'], dic['id'], dic['name']))
        df = pd.DataFrame(df, columns=['label', 'id', 'name'])
        self.categories = df.set_index(['label', 'id'], drop=True)['name']
        
    # page指定で収入データを取得する
    def get_incomes(self, page):
        df = []
        for dic in self.get_requests(self.url_incomes, page):
            df.append((dic['event_date'], self.categories['income'][dic['category']], dic['amount'], dic['memo']))
        df = pd.DataFrame(df, columns=['date', 'category', 'amount', 'memo'])
        df['date'] = pd.to_datetime(df['date'])
        return df
    
    # page指定で支出データを取得する
    def get_expenditures(self, page):
        df = []
        for dic in self.get_requests(self.url_expenditures, page):
            df.append((dic['event_date'], self.categories['expenditure'][dic['category']], dic['amount'], dic['memo']))
        df = pd.DataFrame(df, columns=['date', 'category', 'amount', 'memo'])
        df['date'] = pd.to_datetime(df['date'])
        return df
    
    # 日付指定で収入データを取得する
    def get_incomes_by_date(self, date_min, date_max):
        page_num = 1
        dfs = []
        while True:
            df = self.get_incomes(page_num)
            dfs.append(df)
            if df['date'].min() < date_min:
                break
            else:
                page_num += 1
        df = pd.concat(dfs, axis=1)
        df = df[(df['date']>=date_min)&(df['date']<date_max)].reset_index(drop=True)
        return df
    
    # 日付指定で支出データを取得する
    def get_expenditures_by_date(self, date_min, date_max):
        page_num = 1
        dfs = []
        while True:
            df = self.get_expenditures(page_num)
            dfs.append(df)
            if df['date'].min() < date_min:
                break
            else:
                page_num += 1
        df = pd.concat(dfs, axis=1)
        df = df[(df['date']>=date_min)&(df['date']<date_max)].reset_index(drop=True)
        return df

収入と支出のデータをpage指定・日付指定でpandasのDataFrame形式で取り出せるようにしています。他にも色々できますが、普段自分が使うのはこの2つ。日付については以下のように、指定1≦日付<指定2で取り出せるようにしています。このほうが1か月単位の取り出しがしやすいので。使用例は以下です。

# 2024年1月の支出データを取り出す例
date_min = pd.to_datetime('2024-01-01')
date_max = pd.to_datetime('2024-02-01')
sa = sumumaAPI()  
df = sa.get_expenditures_by_date(date_min, date_max)

終わりに

自分は特にcsv入出力とAPI機能に惹かれてSUMUMAを使っています、とても便利なサービスですので皆さんも是非!余談ですが先日SUMUMAで少し不具合があり、問い合わせを送ったところとても早く対応していただけました。この記事は前々から書こうと思っていた内容なのですが、放置していたのが申し訳なくなるほどに…。

2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1