Claude Codeで自治体オープンデータを標準データセットの形式に整える
台帳CSVを自治体標準オープンデータセット(旧・推奨データセット)の公共施設一覧に整える手順。項目名・ファイル名・全角半角・IDをスクリプトで変換し、公開前の検査までClaude Codeで回します。
自治体の施設台帳は、庁内では読めても、そのままではオープンデータとして使えません。列名が課ごとにばらばらで、全角と半角が混ざり、IDは行の並び順で振られているからです。公開用の形に整える作業は、ルールが決まっていて繰り返しも多く、スクリプトにするのに向いています。
この記事では、台帳CSVを自治体標準オープンデータセットの「公共施設一覧」に整える手順を、変換と検査の2本のスクリプトで組みます。書かせるのも直させるのもClaude Codeです。例の台帳は手順確認用に作った合成データで、実在の施設ではありません。
「推奨データセット」は今「自治体標準オープンデータセット」と呼ぶ
検索すると今も「推奨データセット」の名前で手順が出てきます。デジタル庁のページでは、2023年3月31日付けで「推奨データセット」から「自治体標準オープンデータセット」(略称:自治体標準ODS)へ名称が変わりました。古い名前の資料は、オープンデータのアーカイブページ側に移されています。
自治体標準ODSは、公開ニーズの高いデータについて、データ作成時に従うルールやフォーマットをまとめたものです。地方公共団体には、今後オープンデータを更新する際にこの形式を適用することが推奨されています。一覧には公共施設、文化財、緊急避難場所、AED設置箇所、医療機関、公衆トイレ、学校給食の献立情報などが並びます。
すでに旧・推奨データセットの形式で公開しているなら、変換用のコンバーターツール(推奨DS→自治体標準ODS)がExcel上で動くものとして配布されています。この記事が扱うのは、旧形式がなく、庁内の台帳から最初に作る場合です。
定義書から整形ルールを読み取る
整形の正解は、デジタル庁が公開する「データ項目定義書A」(Excel)にあります。公共施設一覧のシートには、項目No.1の団体コードからNo.56の備考まで56項目が並び、各項目に区分・説明・形式・記入例が付いています。
区分の読み方は次のとおりです。
- ◎:必須項目。データセットの核になる
- ○:推奨項目。アプリの基本機能を実現するために公開が望ましい
- 空白:任意項目
公共施設一覧で◎が付くのは、No.4の名称とNo.13の所在地_連結表記の2項目だけです。団体コードやIDは○(推奨)の扱いで、定義書の区分だけを見ると「無くてもよい」と読めます。ただ、ファイル名にもIDにも団体コードを使う作りなので、台帳から作るなら2項目とも入れておくほうが後の更新で困りません。
「データ項目特記事項」シートの共通ルールからは、変換スクリプトに効くものを拾いました。
| ルール | 定義書の記述 | スクリプトでの扱い |
|---|---|---|
| 文字種 | 定義書の記述英数字は半角、カタカナは全角 | スクリプトでの扱い全角の英数字を半角に直す |
| 機種依存文字 | 定義書の記述ローマ数字・丸数字・㈱や㎡のような組文字は使用不可 | スクリプトでの扱い検出して人が直す |
| 改行 | 定義書の記述データ中の改行は使用不可 | スクリプトでの扱いセル内の改行を空白に置換 |
| カナ項目 | 定義書の記述「ー」は全角ハイフンでなく全角長音 | スクリプトでの扱いハイフン類を長音に置換 |
| 団体コード | 定義書の記述半角数字6桁、足りなければ先頭を0埋め | スクリプトでの扱い検査で6桁を確認 |
| ID | 定義書の記述小文字英字・数字・- _ . ~ の6種類、12桁 | スクリプトでの扱い検査で文字種と桁数を確認 |
| 時刻 | 定義書の記述hh:mm 形式、記入例は09:00 | スクリプトでの扱い9:00 や 9時00分 を変換 |
| 必須項目が埋まらない行 | 定義書の記述他の項目が有用なら行を削除せず空欄で残す | スクリプトでの扱い検査で空欄を報告するだけにして、行は消さない |
ファイル名の規則は別シートにあり、公共施設一覧は [都道府県コード又は市区町村コード]_public_facility です。記入例は 011002_public_facility で、拡張子はCSVにします。
台帳CSVを定義書の列に写す変換スクリプト
最初にCLAUDE.mdへ整形ルールを書き、Claude Codeがスクリプトを書くときにも、直すときにも同じ前提を読めるようにします。
# オープンデータ整備
- 出力は自治体標準オープンデータセットの公共施設一覧(定義書A「01.公共施設一覧」)の項目名に従う
- 英数字は半角、カタカナは全角。データ中の改行は使わない
- 複数の値を並べるときは「;」で区切る
- 必須項目が空でも、他の項目が有用なら行を削除せず空欄で残す
- ID は台帳の管理番号を使う。過去に使った ID は再利用しない
- 台帳(ledger.csv)は書き換えない。変換は build_ods.py、検査は check_ods.py で行う最後の1行が肝です。ClaudeにCSVを直接書き換えさせると、実行のたびに結果がぶれる余地が残ります。台帳には触らせず、変換はスクリプトに固定します。IDの継続使用ルールがあるので、再実行しても同じ行に同じIDが付く仕組みが要るからです。
次のように頼みます。
ledger.csv(列: 管理番号, 施設名, フリガナ, 住所, 緯度, 経度, TEL, 開館, 閉館, HP。管理番号は12桁の小文字英数字)を、
公共施設一覧の項目名に合わせて011002_public_facility.csvへ変換する
build_ods.pyを書いて。CLAUDE.mdのルールに従い、UTF-8(BOM付き)で出力すること出力をBOM付きUTF-8にしているのは、Excelで開いたときの文字化けを避けるための運用上の選択です。公共施設一覧の定義書には、文字コードを指定する項目がありません。文字コードの項目があるのは、たとえば学校給食の献立情報のメタデータで、説明は「UTF-8, Shift-JISなど」、記入例はUTF-8です。
出来上がるスクリプトの一例が次のものです。台帳の列名と定義書の項目名を対応づける辞書が中心になります。
import csv, re, sys, unicodedata
CODE = "011002"
MAP = {"管理番号": "ID", "施設名": "名称", "フリガナ": "名称_カナ",
"住所": "所在地_連結表記", "緯度": "緯度", "経度": "経度",
"TEL": "電話番号", "開館": "開始時間", "閉館": "終了時間", "HP": "URL"}
COLS = ["全国地方公共団体コード", "ID", "名称", "名称_カナ", "所在地_連結表記",
"緯度", "経度", "電話番号", "開始時間", "終了時間", "URL"]
ASCII_COLS = {"ID", "緯度", "経度", "電話番号", "URL"}
def hhmm(s):
m = re.fullmatch(r"(\d{1,2})[::時](\d{2})分?", unicodedata.normalize("NFKC", s))
return f"{int(m[1]):02d}:{m[2]}" if m else s
def kana(s):
s = unicodedata.normalize("NFKC", s)
return re.sub(r"(?<=[ァ-ヶ])[--‐−](?=[ァ-ヶ]|$)", "ー", s)
WIDE = {c: c - 0xFEE0 for c in
[*range(0xFF10, 0xFF1A), *range(0xFF21, 0xFF3B), *range(0xFF41, 0xFF5B)]}
def convert(src, dst):
with open(src, encoding="utf-8-sig", newline="") as f, \
open(dst, "w", encoding="utf-8-sig", newline="") as g:
w = csv.DictWriter(g, COLS)
w.writeheader()
for row in csv.DictReader(f):
out = {"全国地方公共団体コード": CODE}
for old, new in MAP.items():
v = (row.get(old) or "").strip().replace("\n", " ")
if new in ASCII_COLS:
v = unicodedata.normalize("NFKC", v)
out[new] = v
for col in ("名称", "所在地_連結表記"):
out[col] = out[col].translate(WIDE)
out["名称_カナ"] = kana(out["名称_カナ"])
out["開始時間"] = hhmm(out["開始時間"])
out["終了時間"] = hhmm(out["終了時間"])
w.writerow(out)
if __name__ == "__main__":
convert(sys.argv[1], sys.argv[2])ASCII_COLS の列だけにNFKC正規化をかけているのがポイントです。名称や住所にNFKCをかけると、㈱ が (株) に化けるなど、意味が変わる置換が黙って起きます。名称と住所は全角の英数字を半角にする変換だけにとどめ、機種依存文字は次の検査で見つけて人が判断します。
電話番号は全角を半角にするところまでしか扱っていません。定義書は電話番号の書式を「政府相互運用性フレームワーク(GIF)のコアデータパーツ」の規則に従うとしていて、記入例は (00)0000-0000 の形です。例のスクリプトが出力するハイフン区切りがその書式に合うかは、GIF側の規則を読んで決めてください。
公開前に機械で弾く検査スクリプト
変換した結果が定義書に合っているかは、別のスクリプトで検査します。変換を書いた同じセッションで検査も書かせると、変換の癖をそのまま「正しい」と見なしがちです。検査は定義書のルールだけを根拠に、変換とは別のセッションで書かせます。
import csv, re, sys, os
REQUIRED = ["名称", "所在地_連結表記"] # 定義書で◎の項目
RULES = {
"全国地方公共団体コード": r"\d{6}",
"ID": r"[a-z0-9._~-]{12}",
"名称_カナ": r"[ァ-ヶー・ ]*",
"緯度": r"\d{2}\.\d+",
"経度": r"\d{3}\.\d+",
"開始時間": r"(\d{2}:\d{2})?",
"終了時間": r"(\d{2}:\d{2})?",
}
# ローマ数字・丸数字・組文字(㈱ ㎡ など)
DEPENDENT = re.compile("[\u2160-\u217f\u2460-\u24ff\u3200-\u33ff]")
WIDE_ALNUM = re.compile("[0-9A-Za-z]")
def check(path):
name = os.path.basename(path)
if not re.fullmatch(r"\d{6}_public_facility\.csv", name):
print(f"{name}: ファイル名が命名規則に合いません")
with open(path, encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
ids = set()
for n, row in enumerate(rows, start=2): # 1行目はヘッダ
for col in REQUIRED:
if not row.get(col):
print(f"{n}行目 {col}: 必須項目が空です")
for col, pat in RULES.items():
v = row.get(col, "")
if not re.fullmatch(pat, v):
print(f"{n}行目 {col}: 形式NG {v!r}")
for col, v in row.items():
if v and DEPENDENT.search(v):
print(f"{n}行目 {col}: 機種依存文字 {v!r}")
if v and WIDE_ALNUM.search(v):
print(f"{n}行目 {col}: 全角の英数字 {v!r}")
if row.get("ID", "") in ids:
print(f"{n}行目 ID: 重複 {row['ID']!r}")
ids.add(row["ID"])
print(f"{len(rows)}行を検査")
if __name__ == "__main__":
check(sys.argv[1])必須の判定は定義書の◎に合わせて名称と所在地_連結表記の2項目だけにしてあります。緯度の \d{2}\.\d+ のような正規表現は、日本国内の値を想定した簡易チェックです。定義書は緯度経度の書式をGIFの地理座標の規則に委ねているので、厳密な書式の確認には使えません。
変換と検査を続けて走らせます。
python3 build_ods.py ledger.csv 011002_public_facility.csv
python3 check_ods.py 011002_public_facility.csv例の台帳3行では、次のように報告されます。
3行目 名称: 機種依存文字 'さくら会館㈱管理'
4行目 所在地_連結表記: 必須項目が空です
4行目 ID: 重複 'aa0100022202'
3行を検査2行目(青葉公民館)は通り、3行目の 桜町2-3 は変換で 桜町2-3 に直っています。残った3件は、変換では直せない問題です。㈱を含む名称をどう書き換えるか、空の住所を台帳側で埋めるか、重複したIDを誰が振り直すかは、台帳を持つ課の判断になります。
Claude Codeに任せる範囲と、任せない範囲
作業を分けると、Claude Codeの役割は次の3つに絞れます。
Claude Codeを使う場面
- 1
定義書を読んでスクリプトの骨格を書く
定義書AのExcelをCSVに書き出して渡し、項目名・形式・区分から変換辞書と検査ルールを作らせます。
- 2
検査の指摘を原因別に仕分ける
指摘のうち、変換スクリプトの修正で消えるものと、台帳側の確認が要るものを分けます。
- 3
台帳を更新するたびに同じ手順を回す
台帳が更新されたら、変換と検査をもう一度実行します。IDの付け方が変わっていないかも、差分で確かめます。
指摘の仕分けは、検査結果を標準入力から渡す非対話モード(claude -p)で自動化できます。標準入力は10MBまでで、超えるとエラーで終了します。
python3 check_ods.py 011002_public_facility.csv > report.txt
cat report.txt | claude --bare -p \
"指摘を、変換スクリプトで直せるものと台帳側の確認が必要なものに分けて表にして"--bare は、フックやCLAUDE.mdなどの自動読み込みを省いて起動する指定です。スクリプトから呼ぶときは付けておくと、実行する環境に左右されず毎回同じ設定で動きます。ただしbareモードはサブスクリプションのログインを使わないので、ANTHROPIC_API_KEY を環境変数に置く必要があります。この例ではCLAUDE.mdを読まないため、必要なルールはプロンプトに書くか、--append-system-prompt-file で渡します。
仕分け結果を別のスクリプトで扱いたいなら、--output-format json に --json-schema を添えます。スキーマに沿った結果が structured_output フィールドに入り、jq で取り出せます。実行は終了コードで成否が分かるので、検査が落ちたら公開手順に進まないようにもできます。
一方、次の判断は人に残します。
- 台帳のどの列がどの項目に当たるかの最終確認(住所の分割や管理番号の意味は課ごとに違います)
- 機種依存文字を含む名称の書き換え。施設の正式名称にかかわるため、課の確認が要ります
- 欠けている必須項目の補完
- ライセンスの決定。公共データ利用規約を適用する場合の扱いは、デジタル庁の解説資料に従います
定義書でつまずきやすい点
IDの文字種が記入例と合わない
定義書はIDに使える文字を半角小文字英字・数字・記号の6種類と定めていますが、記入例の AA0100022200 は大文字です。検査は文字種のルールに合わせて小文字だけを通すようにしています。例の台帳は管理番号を最初から12桁の小文字で振っているので、変換スクリプトは正規化のほかに手を入れていません。自分の台帳の管理番号が大文字なら、変換で小文字にするか、大文字のまま通すかを決めてから検査ルールを調整します。
◎が少なくても空欄だらけで公開しない
必須が名称と所在地の2項目でも、○の推奨項目はアプリの基本機能を実現するための項目です。緯度経度・電話番号・利用可能曜日・開始時間・終了時間・URLなどの○が空だと、地図アプリや検索から使いにくくなります。
列の数と順序は定義書に合わせる
例のスクリプトは11項目だけを出力します。実際に公開するときは、Excelの標準フォーマット例(サンプルファイル)のヘッダ行と同じ列構成にしたほうが、他の自治体のデータと並べて使えます。ヘッダのセルは、必須項目が黄色、その他がオレンジです。
IDは台帳の並び順で振らない
定義書には、内容が変わっても管理対象が同じならIDを継続使用するルールがあります。廃止したデータは「欠番とし、使い回しをしない」のが決まりです。行番号からIDを作ると、行の追加や削除で別の施設に同じIDが移ります。例のスクリプトが台帳の管理番号をそのままIDに使うのは、このためです。
定義書は改訂される
データ項目定義書Aの最新版は2026年8月1日付けで、項目の追加や形式の変更を記録した改訂履歴のシートが付いています。一度作った変換辞書を使い続けず、公開のたびに最新の定義書の版と突き合わせます。
まとめ
「推奨データセット」の整備は、いまは自治体標準オープンデータセットの定義書を読むところから始まります。ルールが文章で決まっているので、変換と検査をスクリプトに固定でき、Claude Codeの仕事は定義書を読んでスクリプトを書くことと、検査の指摘を仕分けることに集約できます。データそのものの書き換えはClaudeに任せず、台帳を持つ課の判断を残す。この分け方なら、台帳が更新されても同じ手順で再実行できます。
CSVを扱うスクリプト化の考え方は、PLCログのCSV解析と共通です。公開データの取得側の例は、EDINET APIの手順にあります。claude -p の基本は-pモードの解説にまとめています。