◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/ビジネス
⟐ ビジネス/2026-10-23中級

ストアごとに形の違う売上 CSV は、4列に寄せてから足します。月末の為替と、元通貨の合計を残しておく理由

App Store と Google Play の売上 CSV を、日付・アプリ・金額・通貨の4列に寄せて月別に集計する Python スクリプトです。読めない行を捨てない作り方と、為替を月末の固定値にする理由、元通貨の合計で突き合わせる手順をまとめました。

売上集計App Store93Google Play41Python個人開発223収益化70

✦ プレミアム記事

月初になると、ストアの管理画面から売上の CSV を落として、表計算ソフトに貼る作業が待っています。アプリが一本なら、それで足ります。ところが本数が増えると、ストアごとに列の名前も日付の書き方も違うことが、じわじわと効いてきます。

ある月、貼り付けた合計と、ストアの画面に出ている合計が合いませんでした。差は小さく、どこで消えたのか見当がつきません。結局、返金の行を「マイナスだから」と手で除いていたことが原因でした。合っているかどうかを確かめる手段を持たないまま足し算をすると、間違いは数字の見た目だけでは分かりません。

その反省から組んだ集計の手順を、ここに書き残します。ストアごとの CSV を共通の4列に寄せ、月末の為替で円に直し、元の通貨のままの合計を並べて突き合わせるところまでです。ストアの CSV の列名は変わることがあるので、スクリプトの中身は「あなたの手元の CSV を開いて書き換える」前提にしています。

共通の4列に寄せる、という割り切り

集計に必要なのは、突き詰めると四つだけです。いつ(月)、どのアプリか、いくらか、何の通貨か。ストアの CSV にはほかにも多くの列がありますが、月別・アプリ別の収益を知るだけなら、この四つで足ります。

寄せ方は単純です。ストアごとに「うちの CSV では、日付はこの列、アプリ名はこの列」という対応表を持ちます。新しいストアを足すときは、対応表に1行足すだけです。

共通の列寄せるときの注意
月日付の先頭7文字だけ使います。2026/09/05 と 2026-09-05 の両方を 2026-09 にそろえます
アプリストアによって表示名が違うことがあります。違うなら対応表で名前を寄せます
金額桁区切りのカンマを外し、浮動小数点でなく Decimal で持ちます。返金はマイナスのまま残します
通貨大文字にそろえます。小文字で入っている CSV がありました

金額を Decimal で持つのは、円に直したあとの端数が、月ごとにずれていくのを避けるためです。数十行なら誤差は目に見えませんが、あとで「合わない」と気づいたときに原因が追えなくなります。

スクリプト全体

次のスクリプトを merge_sales.py という名前で置きます。これは、列名の対応表と、為替の固定値を渡すだけで動くように作ってあります。MAPPING の列名は例です。 手元の CSV の1行目を見て、そのまま書き換えてください。

"""store ごとに形式の違う売上 CSV を共通 4 列に寄せ、アプリ別・月別に集計する。"""
import csv, sys, json
from collections import defaultdict
from decimal import Decimal, InvalidOperation
 
# store ごとの列名の対応は、手元の CSV を開いて自分で書き換える(ここは例)
MAPPING = {
    "storeA": {"date": "Date", "app": "App", "amount": "Net", "currency": "Cur"},
    "storeB": {"date": "Order Charged Date", "app": "Product", "amount": "Payout", "currency": "Currency"},
}
 
def load(path, store):
    m = MAPPING[store]
    rows, bad = [], []
    with open(path, newline="", encoding="utf-8-sig") as f:
        for n, r in enumerate(csv.DictReader(f), start=2):
            try:
                rows.append({
                    "month": r[m["date"]].strip()[:7].replace("/", "-"),
                    "app": r[m["app"]].strip(),
                    "amount": Decimal(r[m["amount"]].replace(",", "")),
                    "currency": r[m["currency"]].strip().upper(),
                    "store": store,
                })
            except (KeyError, InvalidOperation) as e:
                bad.append((path, n, repr(e)))   # 黙って捨てず、行番号ごと残す
    return rows, bad
 
def main(rates_path, *pairs):
    rates = json.load(open(rates_path))          # {"2026-09": {"USD": "150.0", "JPY": "1"}}
    totals, raw_sum, bad_all = defaultdict(Decimal), defaultdict(Decimal), []
    for p in pairs:
        store, path = p.split("=", 1)
        rows, bad = load(path, store)
        bad_all += bad
        for r in rows:
            rate = Decimal(rates[r["month"]][r["currency"]])   # 無ければ KeyError で止める
            totals[(r["month"], r["app"])] += r["amount"] * rate
            raw_sum[(store, r["currency"])] += r["amount"]
    for (month, app), v in sorted(totals.items()):
        print(f"{month}  {app:<12} {v:>12,.0f} JPY")
    print("-- 元通貨のままの合計(ストア別・突き合わせ用)")
    for (store, cur), v in sorted(raw_sum.items()):
        print(f"{store}  {cur}  {v}")
    print(f"-- 読めなかった行: {len(bad_all)}")
    for b in bad_all: print("  ", *b)
 
if __name__ == "__main__":
    main(*sys.argv[1:])

為替の固定値は、月ごとに1本の JSON にまとめます。取引日ごとに変えず、月末の1回だけ決める理由は、このあと書きます。

{"2026-09": {"USD": "150", "JPY": "1"}}

走らせるときは、為替の JSON と、「ストア名=CSV のパス」の組を渡します。

python3 merge_sales.py rates.json storeA=a.csv storeB=b.csv
✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦ストアごとに列名も日付の書式も違う売上 CSV を、共通の4列に寄せて、アプリ別・月別の円建て表を自分で作れるようになります
✦読めなかった行や為替の抜けを黙って捨てるスクリプトを避け、集計が合っているかどうかを元通貨の合計で毎月確かめられるようになります
✦月初に表計算ソフトの前で数字を写す時間をなくし、スクリプトを1回走らせるだけで前月分を締められるようになります
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⟐ ビジネス2026-10-19
アプリに課金を入れる前に決める、特商法表記の住所と電話番号の扱い方
有料プランを入れると、個人でも特定商取引法の表記ページが必要になる場面があります。住所と電話番号を載せる・請求時に開示する・別住所を使う、の3択を、消費者庁ガイドの表記事項に沿って比べ、決める順番を整理します。
⟐ ビジネス2026-05-15
運用中の壁紙アプリをRork Maxで作り直した記録:工程別の実作業時間と再リリース後の指標
運用中の壁紙アプリをRork Maxでゼロから再実装し、工程ごとに実作業時間を計測しました。生成コードが取り違えた仕様、既存サブスクリプションの引き継ぎ手順、再リリース後30日のクラッシュ率と転換率まで数字で記録しています。
⟐ ビジネス2026-05-13
Rork アプリのユーザーレビューを AI に読ませたら、次に作るべき機能が3分で見えてきた
App Store・Google Play のレビューを Claude API で自動収集・感情分析・機能優先度スコアリングするシステムを Rork アプリに構築する全工程。バッチ分析が静かに欠落する問題の切り分けまで、動作確認済みコードとともに解説します。