👁

Pythonで自動化ツールを作ろう

この章では、Pythonを使った売上データ集計の自動化ツール構築をハンズオン形式で学習します。これにより、CSVの読み込みからクラスによるデータ管理・JSON出力まで、自動化ツールを自分で作れるようになります。

1. 本章の概要

1.1 本章の目的

Pythonを学ぶ大きなメリットの1つが、日々の定型業務を自動化して時間とヒューマンエラーを削減できることです。手作業では、データの転記ミスや計算間違い、ファイルの保存先を間違えるといったヒューマンエラーが起こりがちですが、プログラムで自動化すれば同じ処理を何度実行しても常に同じ結果が得られます。

本章では、これまでのセクションで学んだPythonの要素(データ構造・制御構文・関数・クラス)を組み合わせて、CSVで管理された売上データをカテゴリ別に集計する自動化ツールを1つ完成させます。

1.2 ハンズオンの流れ

売上データが記録されたCSVファイルを読み込み、商品カテゴリごとに販売個数と売上合計を集計し、JSON形式のレポートとして出力するツールを作成します。実行時のコマンドライン引数で目標金額を指定すると、達成状況を判定する機能も加えます。

sequenceDiagram
    participant CSV as CSVファイル
    participant Sale as Saleオブジェクト
    participant Agg as 集計処理
    participant CS as CategorySummary
    participant JSON as JSONファイル

    CSV->>Sale: データを読み込み
    Sale->>Agg: カテゴリ別にグループ化
    Agg->>CS: 販売個数・売上合計を算出
    CS->>JSON: レポート辞書に変換して出力

1.3 事前準備

必要なツール

この章では、以下のツールを使用します。まだインストールしていない場合は、リンク先の手順に沿って準備をお願いします。

ツール名 関連箇所 理由
Visual Studio Code Visual Studio Codeのインストール 自動化ツールのコードを記述するエディタとして使用する
Python Pythonのインストール 自動化スクリプトの実行環境として使用する

2. プロジェクトの準備

それでは、早速ハンズオンを行っていきます。まずはプロジェクトの準備から行います。

2.1 フォルダの作成

まず、任意の場所にsales-reportフォルダを作成します。

sales-report/  ← このフォルダを作成

作成したフォルダをVisual Studio Codeで開きます。Visual Studio Codeのメニューから「ファイル」→「フォルダーを開く」を選択し、作成したフォルダを開いてください。

2.2 売上データの作成

集計対象となる売上データのCSVファイルを作成します。Visual Studio Codeのエクスプローラーで右クリックし、「新しいファイル」を選択してsales.csvという名前でファイルを作成してください。

sales-report/
└── sales.csv  ← このファイルを作成

作成したファイルに以下の内容を記述して保存します。これは、3日間の売上データを模したサンプルデータです。

date,product,category,quantity,unit_price
2025-01-06,ノート,文房具,10,200
2025-01-06,ボールペン,文房具,5,150
2025-01-06,コーヒー,飲料,8,300
2025-01-07,ノート,文房具,3,200
2025-01-07,お茶,飲料,12,150
2025-01-07,コーヒー,飲料,5,300
2025-01-08,消しゴム,文房具,6,100
2025-01-08,ボールペン,文房具,15,150
2025-01-08,お茶,飲料,10,150

各列の意味は以下のとおりです。

列名 説明
date 売上日
product 商品名
category 商品カテゴリ
quantity 数量
unit_price 単価(円)
📝 CSVとは
CSV(Comma-Separated Values)は、データをカンマ(,)で区切って記録するテキスト形式のファイルです。Excelなどの表計算ソフトでも開くことができ、データの受け渡しに広く使われています。1行目はヘッダ行(列名)、2行目以降がデータ行です。

2.3 メインスクリプトの作成

売上データを集計するスクリプトを作成します。Visual Studio Codeのエクスプローラーで右クリックし、「新しいファイル」を選択してmain.pyという名前で作成してください。

sales-report/
├── sales.csv
└── main.py  ← このファイルを作成

3. CSVファイルの読み込み

自動化の起点となる売上データを、CSVから Python 上に読み込むところから始めます。まずはCSVを扱うための標準ライブラリと、そのなかで使う csv.DictReader() の役割を押さえます。

3.1 csvモジュールとは

Pythonの標準ライブラリには、CSVファイルの読み書きを行うためのcsvモジュールが用意されています。前の章 Python関数とクラス で学んだopen()split()でもCSVを読み込むことはできますが、csvモジュールを使うことで、より安全で簡潔にCSVファイルを扱えます。

📝 csvモジュールについて
csvモジュールはPythonの標準ライブラリに含まれており、追加のインストールは不要です。CSVファイルの読み込みにはcsv.reader()csv.DictReader()の2つの方法がありますが、本章では列名でデータにアクセスできるcsv.DictReader()を使用します。

3.2 csv.DictReaderによる読み込み

csv.DictReader()は、CSVファイルの各行を辞書として読み込むクラスです。ヘッダ行の列名がキー、各データ行の値が値になります。基本的な構文は以下のとおりです。

import csv

with open("ファイル名", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["列名"])

csv.DictReader()にファイルオブジェクトを渡すと、ヘッダ行を自動的に読み取り、各データ行を辞書型のオブジェクトとして返します。row["列名"]のように列名をキーにして値にアクセスできます。

実際に試してみましょう。main.pyに以下の内容を記述して保存します。

import csv

with open("sales.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row)

Visual Studio Codeのメニューから「ターミナル」→「新しいターミナル」を選択してターミナルを開きます。main.pyを実行して動作を確認しましょう。

💡 ポイント
Pythonの実行コマンドは環境によって異なります。Windowsではpython main.py、macOSではpython3 main.pyを使用します。以降のハンズオンでは「main.pyを実行して」と記載しますので、お使いの環境に合わせてコマンドを読み替えてください。

以下のように各行が辞書として表示されれば成功です。

{'date': '2025-01-06', 'product': 'ノート', 'category': '文房具', 'quantity': '10', 'unit_price': '200'}
{'date': '2025-01-06', 'product': 'ボールペン', 'category': '文房具', 'quantity': '5', 'unit_price': '150'}
{'date': '2025-01-06', 'product': 'コーヒー', 'category': '飲料', 'quantity': '8', 'unit_price': '300'}
{'date': '2025-01-07', 'product': 'ノート', 'category': '文房具', 'quantity': '3', 'unit_price': '200'}
{'date': '2025-01-07', 'product': 'お茶', 'category': '飲料', 'quantity': '12', 'unit_price': '150'}
{'date': '2025-01-07', 'product': 'コーヒー', 'category': '飲料', 'quantity': '5', 'unit_price': '300'}
{'date': '2025-01-08', 'product': '消しゴム', 'category': '文房具', 'quantity': '6', 'unit_price': '100'}
{'date': '2025-01-08', 'product': 'ボールペン', 'category': '文房具', 'quantity': '15', 'unit_price': '150'}
{'date': '2025-01-08', 'product': 'お茶', 'category': '飲料', 'quantity': '10', 'unit_price': '150'}

CSVファイルの1行目(date,product,category,quantity,unit_price)がキーとして使われ、2行目以降の各データ行が辞書に変換されていることが確認できます。

💡 ポイント
CSVから読み込んだ値は、数値であってもすべて文字列型になります。上の出力で'quantity': '10'のように数値が引用符で囲まれていることからもわかります。計算に使う場合はint()float()で型変換が必要です。

4. 売上データクラスの定義

CSVから読み込んだデータは辞書として扱えますが、ここでは前の章 Python関数とクラス で学んだクラスを使って売上データを管理します。クラスを使うことで、データの構造が明確になり、sale.dateのようにわかりやすい形でデータにアクセスできるようになります。

4.1 Saleクラスの作成

売上データを管理するSaleクラスを定義します。このクラスはmain.pyから読み込んで使うため、別ファイルとして作成します。Visual Studio Codeのエクスプローラーで右クリックし、「新しいファイル」を選択してmodels.pyという名前で作成してください。

sales-report/
├── sales.csv
├── models.py  ← このファイルを作成
└── main.py

models.pyに以下の内容を記述して保存します。

class Sale:
    def __init__(self, date, product, category, quantity, unit_price):
        self.date = date
        self.product = product
        self.category = category
        self.quantity = quantity
        self.unit_price = unit_price
        self.amount = quantity * unit_price

Saleクラスの__init__メソッドで、売上日、商品名、カテゴリ、数量、単価を属性として保持し、売上金額(amount)をquantity * unit_priceで自動的に計算しています。

4.2 main.pyの更新

次に、main.pyからSaleクラスを読み込んで使います。main.pyを以下の内容に書き換えて保存します。

import csv
from models import Sale

# CSVファイルの読み込み
sales = []
with open("sales.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        sale = Sale(
            date=row["date"],
            product=row["product"],
            category=row["category"],
            quantity=int(row["quantity"]),
            unit_price=int(row["unit_price"])
        )
        sales.append(sale)

# 読み込み結果の確認
for sale in sales:
    print(f"{sale.date} {sale.product}: {sale.quantity}個 × {sale.unit_price}円 = {sale.amount}円")

追加したコードを解説します。

from models import Sale

この部分で、models.pyに定義したSaleクラスを読み込んでいます。これは前の章 Pythonライブラリ で学んだモジュールのインポートと同じ仕組みです。models.pyというファイル名から.pyを除いたmodelsがモジュール名になります。

for row in reader:

forループで、CSVの各行を1行ずつ処理します。rowには{"date": "2025-01-06", "product": "ノート", "category": "文房具", "quantity": "10", "unit_price": "200"}のような辞書が入ります。

    sale = Sale(
        date=row["date"],
        product=row["product"],
        category=row["category"],
        quantity=int(row["quantity"]),
        unit_price=int(row["unit_price"])
    )

辞書から値を取り出し、Saleクラスのインスタンスを作成しています。row["date"]のように、ヘッダ名をキーにして値を取り出せます。CSVから読み込んだ値はすべて文字列のため、数量と単価はint()で整数に変換してから渡しています。

    sales.append(sale)

作成したSaleインスタンスを、リストsalesに追加しています。このループが完了すると、CSVの全行分のSaleインスタンスがsalesリストに格納されます。

for sale in sales:
    print(f"{sale.date} {sale.product}: {sale.quantity}個 × {sale.unit_price}円 = {sale.amount}円")

最後に、salesリストに格納された各Saleインスタンスの属性をprintで出力し、正しく読み込めたかを確認しています。sale.amountSaleクラスの__init__で自動計算された売上金額です。

main.pyを実行して動作を確認しましょう。以下のように各売上データが表示されます。

2025-01-06 ノート: 10個 × 200円 = 2000円
2025-01-06 ボールペン: 5個 × 150円 = 750円
2025-01-06 コーヒー: 8個 × 300円 = 2400円
2025-01-07 ノート: 3個 × 200円 = 600円
2025-01-07 お茶: 12個 × 150円 = 1800円
2025-01-07 コーヒー: 5個 × 300円 = 1500円
2025-01-08 消しゴム: 6個 × 100円 = 600円
2025-01-08 ボールペン: 15個 × 150円 = 2250円
2025-01-08 お茶: 10個 × 150円 = 1500円

sale.datesale.productsale.amountのように、属性名でデータにアクセスできています。

5. カテゴリ別の集計

ここからは、売上データをカテゴリ別に集計していきます。集計結果を保持するためのCategorySummaryクラスを新たに作成し、Saleのデータを集計します。

5.1 CategorySummaryクラスの作成

カテゴリごとの販売個数と売上合計を保持するCategorySummaryクラスを定義します。このクラスには、Saleのデータを受け取って集計するadd()メソッドを持たせます。

クラスにデータを加算するメソッドの基本的な構文は以下のとおりです。

class 集計クラス:
    def __init__(self, グループ名):
        self.グループ名 = グループ名
        self.合計 = 0

    def add(self, データ):
        self.合計 += データの値

__init__で合計の初期値を0に設定し、add()メソッドが呼ばれるたびにデータの値を加算していきます。

models.pyCategorySummaryクラスを追加します。Saleクラスの下に以下のコードを追加してください。

class Sale:
    def __init__(self, date, product, category, quantity, unit_price):
        self.date = date
        self.product = product
        self.category = category
        self.quantity = quantity
        self.unit_price = unit_price
        self.amount = quantity * unit_price


# ====== ↓ ここから追加 ======
class CategorySummary:
    def __init__(self, category):
        self.category = category
        self.total_quantity = 0
        self.total_amount = 0

    def add(self, sale):
        self.total_quantity += sale.quantity
        self.total_amount += sale.amount
# ====== ↑ ここまで追加 ======

CategorySummaryクラスの各部分を説明します。

  • __init__メソッドで、カテゴリ名と、販売個数の合計(total_quantity)・売上金額の合計(total_amount)の初期値0を設定しています
  • add()メソッドは、Saleインスタンスを受け取り、そのquantity(数量)とamount(金額)をそれぞれ加算します

5.2 main.pyの更新

次に、main.pyを更新してCategorySummaryを使ったカテゴリ別集計を行います。カテゴリ別に集計するには、辞書を使ってカテゴリ名をキー、CategorySummaryインスタンスを値として管理します。基本的な構文は以下のとおりです。

グループ = {}
for データ in データのリスト:
    キー = データのグループ名
    if キー not in グループ:
        グループ[キー] = 集計クラス(キー)
    グループ[キー].add(データ)

辞書にまだそのキーが存在しない場合は新しいインスタンスを作成し、add()メソッドでデータを加算していきます。

main.pyにカテゴリ別集計の処理を追加します。インポート文にCategorySummaryを追加し、CSV読み込みの後に集計と表示のコードを追加してください。

import csv
from models import Sale, CategorySummary  # ← CategorySummaryを追加

# CSVファイルの読み込み
sales = []
with open("sales.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        sale = Sale(
            date=row["date"],
            product=row["product"],
            category=row["category"],
            quantity=int(row["quantity"]),
            unit_price=int(row["unit_price"])
        )
        sales.append(sale)

# ====== ↓ ここから追加 ======
# カテゴリ別の集計
summaries = {}
for sale in sales:
    if sale.category not in summaries:
        summaries[sale.category] = CategorySummary(sale.category)
    summaries[sale.category].add(sale)

# 集計結果の表示
print("=== カテゴリ別売上集計 ===")
for summary in summaries.values():
    print(f"  {summary.category}: {summary.total_quantity}個 / {summary.total_amount}円")
# ====== ↑ ここまで追加 ======

追加した集計部分を解説します。

summaries = {}

カテゴリ別の集計結果を格納するための空の辞書を用意します。カテゴリ名("文房具""飲料"など)をキー、CategorySummaryインスタンスを値として管理します。

    if sale.category not in summaries:
        summaries[sale.category] = CategorySummary(sale.category)

if sale.category not in summariesで、そのカテゴリがまだ辞書に存在しないかを確認しています。初めて出てきたカテゴリの場合は、新しいCategorySummaryインスタンスを作成して辞書に登録します。

    summaries[sale.category].add(sale)

辞書から該当カテゴリのCategorySummaryを取り出し、add()メソッドで数量と金額を加算しています。同じカテゴリの売上データが繰り返し処理されるたびに、合計値が積み上がっていきます。

main.pyを実行して動作を確認しましょう。以下のようにカテゴリ別の集計結果が表示されます。

=== カテゴリ別売上集計 ===
  文房具: 39個 / 6200円
  飲料: 35個 / 7200円

文房具カテゴリの集計を確認すると、ノート(10個 + 3個)+ ボールペン(5個 + 15個)+ 消しゴム(6個)= 39個、金額は2,000 + 600 + 750 + 2,250 + 600 = 6,200円です。正しく集計されていることが確認できます。

6. JSONファイルへの出力

集計結果をJSON形式のファイルに出力します。ただし、前の章で学んだjson.dump()は辞書やリストをJSONに変換できますが、CategorySummaryのようなクラスのインスタンスをそのまま渡すことはできません。そこで、インスタンスを辞書に変換するto_dict()メソッドを追加します。

6.1 to_dict()メソッドの追加

クラスのインスタンスを辞書に変換するメソッドの基本的な構文は以下のとおりです。

class クラス名:
    def to_dict(self):
        return {
            "キー1": self.属性1,
            "キー2": self.属性2,
        }

to_dict()メソッドは、インスタンスの属性を辞書にまとめて返します。これにより、json.dump()に渡せる形式に変換できます。

models.pyCategorySummaryクラスにto_dict()メソッドを追加します。add()メソッドの下に以下のコードを追加してください。

class CategorySummary:
    def __init__(self, category):
        self.category = category
        self.total_quantity = 0
        self.total_amount = 0

    def add(self, sale):
        self.total_quantity += sale.quantity
        self.total_amount += sale.amount

    # ====== ↓ ここから追加 ======
    def to_dict(self):
        return {
            "category": self.category,
            "total_quantity": self.total_quantity,
            "total_amount": self.total_amount
        }
    # ====== ↑ ここまで追加 ======

追加したto_dict()メソッドを解説します。

    def to_dict(self):
        return {
            "category": self.category,
            "total_quantity": self.total_quantity,
            "total_amount": self.total_amount
        }

インスタンスの属性を辞書にまとめて返しています。例えば、文房具カテゴリのCategorySummaryto_dict()を呼ぶと、{"category": "文房具", "total_quantity": 39, "total_amount": 6200}という辞書が返されます。この辞書をjson.dump()に渡すことで、JSON形式でファイルに出力できるようになります。

6.2 main.pyの更新

main.pyにJSON出力処理を追加します。インポート文にjsonを追加し、集計結果の表示の後にJSON出力のコードを追加してください。

import csv
import json  # ← 追加
from models import Sale, CategorySummary

# CSVファイルの読み込み
sales = []
with open("sales.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        sale = Sale(
            date=row["date"],
            product=row["product"],
            category=row["category"],
            quantity=int(row["quantity"]),
            unit_price=int(row["unit_price"])
        )
        sales.append(sale)

# カテゴリ別の集計
summaries = {}
for sale in sales:
    if sale.category not in summaries:
        summaries[sale.category] = CategorySummary(sale.category)
    summaries[sale.category].add(sale)

# 集計結果の表示
print("=== カテゴリ別売上集計 ===")
for summary in summaries.values():
    print(f"  {summary.category}: {summary.total_quantity}個 / {summary.total_amount}円")

# ====== ↓ ここから追加 ======
# JSONファイルへの出力
result = []
for summary in summaries.values():
    result.append(summary.to_dict())

with open("summary.json", "w", encoding="utf-8") as f:
    json.dump(result, f, ensure_ascii=False, indent=4)
print("\n集計結果を summary.json に出力しました")
# ====== ↑ ここまで追加 ======

追加したJSON出力部分を解説します。

result = []
for summary in summaries.values():
    result.append(summary.to_dict())

summaries辞書から各CategorySummaryインスタンスを取り出し、to_dict()で辞書に変換してリストにまとめています。この時点でresult[{"category": "文房具", "total_quantity": 39, ...}, {"category": "飲料", ...}]のようなリストになります。

with open("summary.json", "w", encoding="utf-8") as f:
    json.dump(result, f, ensure_ascii=False, indent=4)

json.dump()は前の章で学んだとおり、Pythonのリストや辞書をJSON形式でファイルに書き出す関数です。ensure_ascii=Falseで日本語をそのまま出力し、indent=4で読みやすくインデントを付けています。

main.pyを実行して動作を確認しましょう。summary.jsonファイルが作成されるので、Visual Studio Codeのエクスプローラーからsummary.jsonを開いて内容を確認してください。以下のようなJSONが出力されていれば成功です。

[
    {
        "category": "文房具",
        "total_quantity": 39,
        "total_amount": 6200
    },
    {
        "category": "飲料",
        "total_quantity": 35,
        "total_amount": 7200
    }
]

カテゴリごとの販売個数と売上合計がJSON形式で出力されています。このJSONファイルは、他のプログラムやツールで読み込んで利用することができます。

7. 売上目標の判定

集計結果をただ表示するだけでなく、売上合計が目標金額に対してどの程度達成しているかを判定する機能を追加します。目標金額は前の章で学んだコマンドライン引数sys.argv)で実行時に指定できるようにし、if/elif/elseで達成状況に応じたメッセージを表示します。

7.1 判定ロジックの追加

main.pyにコマンドライン引数の処理と売上目標の判定を追加します。インポート文にsysを追加し、JSON出力の後に判定ロジックを追加してください。

import csv
import json
import sys  # ← 追加
from models import Sale, CategorySummary

# CSVファイルの読み込み
sales = []
with open("sales.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        sale = Sale(
            date=row["date"],
            product=row["product"],
            category=row["category"],
            quantity=int(row["quantity"]),
            unit_price=int(row["unit_price"])
        )
        sales.append(sale)

# カテゴリ別の集計
summaries = {}
for sale in sales:
    if sale.category not in summaries:
        summaries[sale.category] = CategorySummary(sale.category)
    summaries[sale.category].add(sale)

# 集計結果の表示
print("=== カテゴリ別売上集計 ===")
for summary in summaries.values():
    print(f"  {summary.category}: {summary.total_quantity}個 / {summary.total_amount}円")

# JSONファイルへの出力
result = []
for summary in summaries.values():
    result.append(summary.to_dict())

with open("summary.json", "w", encoding="utf-8") as f:
    json.dump(result, f, ensure_ascii=False, indent=4)
print("\n集計結果を summary.json に出力しました")

# ====== ↓ ここから追加 ======
# コマンドライン引数から目標金額を取得
if len(sys.argv) < 2:
    print("エラー: 目標金額を引数で指定してください(例: python main.py 15000)")
    exit(1)

target = int(sys.argv[1])

# 売上目標の判定
total_amount = 0
for summary in summaries.values():
    total_amount += summary.total_amount

print(f"\n売上合計: {total_amount}円(目標: {target}円)")

if total_amount >= target:
    print("目標達成です!")
elif total_amount >= target * 0.8:
    print(f"目標まであと一歩です(あと{target - total_amount}円)")
else:
    print(f"目標未達成です(あと{target - total_amount}円)")
# ====== ↑ ここまで追加 ======

追加したコードを解説します。

if len(sys.argv) < 2:
    print("エラー: 目標金額を引数で指定してください(例: python main.py 15000)")
    exit(1)

len(sys.argv) < 2で引数が指定されているかを確認しています。sys.argvはコマンドライン引数のリストで、sys.argv[0]にはスクリプト名が入るため、引数が1つも指定されていない場合は長さが1になります。引数がない場合は使い方を表示してexit(1)で終了します。

target = int(sys.argv[1])

引数がある場合はsys.argv[1]で目標金額を取得し、int()で整数に変換しています。コマンドライン引数はすべて文字列として渡されるため、計算に使うには型変換が必要です。

if total_amount >= target:
    print("目標達成です!")
elif total_amount >= target * 0.8:
    print(f"目標まであと一歩です(あと{target - total_amount}円)")
else:
    print(f"目標未達成です(あと{target - total_amount}円)")

売上合計金額に対して、if/elif/elseで3段階の判定を行っています。

条件 メッセージ
目標の100%以上 「目標達成です!」
目標の80%以上100%未満 「目標まであと一歩です(あと○円)」
目標の80%未満 「目標未達成です(あと○円)」

main.pyを実行して動作を確認しましょう。今回から引数に目標金額を指定します。目標金額を15,000円に設定して実行してみてください。

Windowsの場合:

python main.py 15000

Macの場合:

python3 main.py 15000

文房具(6,200円)+ 飲料(7,200円)= 合計13,400円のため、以下のように表示されます。

=== カテゴリ別売上集計 ===
  文房具: 39個 / 6200円
  飲料: 35個 / 7200円

集計結果を summary.json に出力しました

売上合計: 13400円(目標: 15000円)
目標まであと一歩です(あと1600円)

目標金額を変えて、3パターンのメッセージが正しく切り替わることも確認してみましょう。引数を10000にして実行すると以下のように表示されます。

売上合計: 13400円(目標: 10000円)
目標達成です!

引数を20000にして実行すると以下のように表示されます。

売上合計: 13400円(目標: 20000円)
目標未達成です(あと6600円)

コードを変更せずに、実行時の引数を変えるだけで異なる目標金額でテストできることがわかります。

8. エラーハンドリング

実際にツールを使う場面では、CSVファイルが存在しない場合など、想定外の状況が発生することがあります。まず、エラーハンドリングがない状態でどうなるかを確認してみましょう。

8.1 エラーを体験する

Visual Studio Codeのエクスプローラーでsales.csvのファイル名をsales_backup.csvに変更してください。ファイルを右クリックし「名前の変更」を選択すると変更できます。

この状態でmain.pyを実行してみましょう。以下のようなエラーが表示され、プログラムが途中で停止します。

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    with open("sales.csv", "r", encoding="utf-8") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'sales.csv'

これはFileNotFoundErrorというエラーで、指定したファイルが見つからなかったことを示しています。エラーメッセージは英語で表示され、プログラムもその場で停止してしまいます。このままでは、ツールを使うユーザにとって何が起きたのかわかりにくい状態です。また、エラー処理が入っていないと、処理が途中で止まることで中途半端なファイルが作成されるなど、予期しない挙動を引き起こすこともあります。

8.2 try/exceptの追加

前の章で学んだtry/exceptを使って、このエラーに対処します。main.pyのCSVファイル読み込み部分を以下のように変更してください。

import csv
import json
import sys
from models import Sale, CategorySummary

# CSVファイルの読み込み
sales = []
try:  # ← 追加
    with open("sales.csv", "r", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            sale = Sale(
                date=row["date"],
                product=row["product"],
                category=row["category"],
                quantity=int(row["quantity"]),
                unit_price=int(row["unit_price"])
            )
            sales.append(sale)
# ====== ↓ ここから追加 ======
except FileNotFoundError:
    print("エラー: ファイル 'sales.csv' が見つかりません。")
    exit(1)

if len(sales) == 0:
    print("エラー: 売上データが0件です。CSVファイルの内容を確認してください。")
    exit(1)
# ====== ↑ ここまで追加 ======

# カテゴリ別の集計
summaries = {}
for sale in sales:
    if sale.category not in summaries:
        summaries[sale.category] = CategorySummary(sale.category)
    summaries[sale.category].add(sale)

# 集計結果の表示
print("=== カテゴリ別売上集計 ===")
for summary in summaries.values():
    print(f"  {summary.category}: {summary.total_quantity}個 / {summary.total_amount}円")

# JSONファイルへの出力
result = []
for summary in summaries.values():
    result.append(summary.to_dict())

with open("summary.json", "w", encoding="utf-8") as f:
    json.dump(result, f, ensure_ascii=False, indent=4)
print("\n集計結果を summary.json に出力しました")

# コマンドライン引数から目標金額を取得
if len(sys.argv) < 2:
    print("エラー: 目標金額を引数で指定してください(例: python main.py 15000)")
    exit(1)

target = int(sys.argv[1])

# 売上目標の判定
total_amount = 0
for summary in summaries.values():
    total_amount += summary.total_amount

print(f"\n売上合計: {total_amount}円(目標: {target}円)")

if total_amount >= target:
    print("目標達成です!")
elif total_amount >= target * 0.8:
    print(f"目標まであと一歩です(あと{target - total_amount}円)")
else:
    print(f"目標未達成です(あと{target - total_amount}円)")

追加したコードを解説します。

try:
    with open("sales.csv", "r", encoding="utf-8") as f:

tryブロックでopen()を囲むことで、ファイルが存在しない場合のエラーに備えています。

except FileNotFoundError:
    print("エラー: ファイル 'sales.csv' が見つかりません。")
    exit(1)

FileNotFoundErrorが発生した場合、日本語のエラーメッセージを表示し、exit(1)でプログラムを終了します。

if len(sales) == 0:
    print("エラー: 売上データが0件です。CSVファイルの内容を確認してください。")
    exit(1)

ファイルが存在してもデータが0件の場合は、その旨を表示して終了します。

8.3 動作確認

ファイル名は先ほどsales_backup.csvに変更したままなので、そのままmain.pyを実行してみましょう。今度は以下のようにわかりやすいメッセージが表示されます。

エラー: ファイル 'sales.csv' が見つかりません。

先ほどの英語のエラーメッセージと比べて、何が起きたかがすぐにわかるようになりました。確認できたら、sales_backup.csvのファイル名をsales.csvに戻してください。

💡 ポイント
try/exceptを使うことで、プログラムがエラーで停止するのを防ぎ、ユーザにわかりやすいメッセージを表示できます。特にファイル操作では、ファイルが存在しないなどのエラーが起こりやすいため、エラーハンドリングを行うことが重要です。

9. まとめ

この章では、Pythonを使った売上データ集計の自動化を体験しました。

  • csvモジュールの csv.DictReader() を使い、CSVファイルの各行を辞書として読み込める
  • CSVから読み込んだ値をアプリケーション内で扱う型に、int() などで安全に変換できる
  • クラスを使い、データの構造を明確にしたうえで sale.date のような形で扱える
  • 集計処理をメソッド(add() / to_dict())としてクラスに閉じ込め、責務を整理できる
  • sys.argv でコマンドライン引数を受け取り、実行時にパラメータを指定できるツールを構築できる
  • try/except によりファイル操作のエラーに対処し、実用に耐える形に仕上げられる
  • Pythonの標準ライブラリだけで、CSV読み込みから集計・JSON出力までの一連の自動化ツールを構築できる

次の章では、PythonからMySQLに接続してデータを操作する流れをハンズオン形式で体験します。

実務で通用するPython開発スキルを身につけたい方は、ぜひ次のステップに進んでみてください。

この教材は役に立ちましたか?

いいねをたくさんいただけると、制作者の励みになり、より多くのセクションが作れるようになります。

感想を一言(任意)

いただいたコメントは次の制作のヒントになります。ぜひお気軽にご投稿ください。

このコメントは他の受講生には公開されません。DevOps Camp運営が、教材改善のために確認します。

0 / 2000