会議録データ
対象: 衆議院 / 参議院 / 京都市議会 / 豊橋市議会 ほか
👀 ざっくり言うと
国会・京都市議会・豊橋市議会などの会議録を、それぞれの公式サイトから自動で収集しています。 PDFやHTMLをパースして、「誰が」「何を」「いつ」発言したかを1発言ずつ切り出し、議員データと紐付けた状態でデータベースに保存しています。
🔄 データの流れ
graph TD
A["国会会議録検索API
kokkai.ndl.go.jp"]:::source
B["kaigiroku.net
地方議会540自治体"]:::source
M["管理画面からの手動入力
Streamlit 会議管理"]:::source
P1["発言データ取得
APIページングで全発言を回収"]:::process
P2["会議一覧巡回
council_idを順に探索し会議を検出"]:::process
P3["会議タイトル解析
タイトル文字列から開催日を抽出"]:::process
P4["本文HTMLを取得
#plain-minute をPlaywrightで取得"]:::process
P5["発言単位に分割
◯/◎マーカーで発言者ごとに切り出し"]:::process
P6["発言者名寄せ
speakersに登録・政治家候補と対応付け"]:::process
P7["会議の手動登録・編集
URLを入力してスクレイピング処理を起動"]:::process
D1{"会議体が
既存マスタに存在するか"}:::process
SC["Conferenceを自動作成
名称/種別を抽出して新規登録"]:::process
T_MT["meetings
テーブル(会議)"]:::table
T_MIN["minutes
テーブル(会議録)"]:::table
T_CV["conversations
テーブル(発言)"]:::table
T_SP["speakers
テーブル(発言者)"]:::table
V["dbt Data Vault層
hub / sat / link モデル"]:::process
H["BigQuery公開
sagebase スキーマ"]:::public
A --> P1
B --> P2
P2 --> P3
P2 --> P4
P4 --> P5
P1 --> P6
P5 --> P6
P3 --> D1
P1 --> D1
D1 -- "無し" --> SC
SC --> T_MT
D1 -- "有り" --> T_MT
M --> P7
P7 --> D1
P1 --> T_MIN
P4 --> T_MIN
P1 --> T_CV
P5 --> T_CV
P6 --> T_SP
T_MT --> V
T_MIN --> V
T_CV --> V
T_SP --> V
V --> H
classDef source fill:#ffffff,stroke:#1a1a1a,stroke-width:2.5px,color:#1a1a1a
classDef process fill:#ffffff,stroke:#737373,stroke-width:1px,color:#1a1a1a
classDef table fill:#ffffff,stroke:#1a1a1a,stroke-width:1.5px,stroke-dasharray: 6 3,color:#1a1a1a
classDef public fill:#1a1a1a,stroke:#1a1a1a,stroke-width:2.5px,color:#ffffff
元データ
加工
DB
公開
📂 技術的な詳細(開発者向け)
スクレイパー・インポーター
| データソース | スクリプト | 主要ロジック |
|---|---|---|
| 国会会議録検索API https://kokkai.ndl.go.jp/api.html | sagebase kokkai import-speeches(src/infrastructure/external/kokkai_api/service.py) | Speech APIを発言単位でページング取得。APIが返す発言者名(speaker_from_api)をそのままspeakersに登録 |
| kaigiroku.net(会議一覧) https://ssp.kaigiroku.net/ | scripts/scrape_kaigiroku_meetings.py | council_idを連番で探索し、タイトル文字列から開催日を正規表現抽出。Conferenceが未登録ならタイトルから自動生成 |
| kaigiroku.net(本文・発言抽出) | scripts/backfill_kaigiroku_minutes.py(parse_kaigiroku_speakers) | #plain-minuteのテキストに対しkaigiroku専用パーサで◯/◎マーカー発言を切り出し。LLM不使用 |
| 発言者 ↔ 政治家 の名寄せ | src/infrastructure/external/langgraph_politician_matching_agent.py | ルールベース(信頼度0.9以上)でマッチできないケースはBAML(LangGraph)で会議体・政党名を考慮したハイブリッドマッチング |
| 管理画面からの手動入力(Streamlit) | src/interfaces/web/streamlit/views/meetings_view.py | 会議URL・開催日・会議体を入力して個別のMeetingを登録・編集。スクレイピング未対応URLの手動補完やデータ訂正に使用 |
DBテーブル(PostgreSQL)
| テーブル | 説明 |
|---|---|
meetings | 会議の開催メタデータ(会議体・開催日・URL) |
minutes | 会議録本体(テキスト・処理済みフラグ) |
conversations | 発言単位のレコード(議事録内のシーケンス順) |
speakers | 発言者エンティティ(politicians / government_officials にマッチング) |
dbtモデル(Data Vault層)
| モデル | 役割 |
|---|---|
hub_meeting | Meeting のハブ(ビジネスキー) |
sat_meeting | Meeting の属性サテライト |
hub_conversation | Conversation のハブ |
sat_conversation | Conversation の属性サテライト |
link_meeting_conference | Meeting ↔ Conference のリンク |
link_conversation_speaker | Conversation ↔ Speaker のリンク |
BigQuery公開テーブル
| テーブル | データセット |
|---|---|
meetings | sagebase |
minutes | sagebase |
conversations | sagebase |
speakers | sagebase |
hub_meeting | sagebase_vault |
このデータ群の処理が副次的に作成/更新するレコード
一部のスクレイピング処理は、主目的のレコードを登録する過程で、 関連する未知のマスタレコードを自動作成します。処理元と生成先の対応を下表に示します。
| 生成/更新されるテーブル | トリガー | 挙動 |
|---|---|---|
conferences | scripts/scrape_kaigiroku_meetings.py(get_or_create_conference)/ sagebase kokkai import-speeches | 会議タイトルや『院名+会議名』から会議体名を抽出し、既存マスタに無ければConferenceを自動作成する |
speakers | 会議録スクレイピング全般(backfill_kaigiroku_minutes.py など) | 発言者名が既存speakersに無ければ新規Speakerを自動登録。その後LLM照合で政治家に紐付け |
politicians | Streamlit 発言者マッチング画面(conversations/components/politician_creation_form.py) | 照合対象の政治家が存在しないとき、発言者情報からPoliticianを新規作成するパスが存在 |