会議録データ

対象: 衆議院 / 参議院 / 京都市議会 / 豊橋市議会 ほか

👀 ざっくり言うと

国会・京都市議会・豊橋市議会などの会議録を、それぞれの公式サイトから自動で収集しています。 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.pycouncil_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_meetingMeeting のハブ(ビジネスキー)
sat_meetingMeeting の属性サテライト
hub_conversationConversation のハブ
sat_conversationConversation の属性サテライト
link_meeting_conferenceMeeting ↔ Conference のリンク
link_conversation_speakerConversation ↔ Speaker のリンク

BigQuery公開テーブル

テーブルデータセット
meetingssagebase
minutessagebase
conversationssagebase
speakerssagebase
hub_meetingsagebase_vault

このデータ群の処理が副次的に作成/更新するレコード

一部のスクレイピング処理は、主目的のレコードを登録する過程で、 関連する未知のマスタレコードを自動作成します。処理元と生成先の対応を下表に示します。

生成/更新されるテーブルトリガー挙動
conferencesscripts/scrape_kaigiroku_meetings.py(get_or_create_conference)/ sagebase kokkai import-speeches会議タイトルや『院名+会議名』から会議体名を抽出し、既存マスタに無ければConferenceを自動作成する
speakers会議録スクレイピング全般(backfill_kaigiroku_minutes.py など)発言者名が既存speakersに無ければ新規Speakerを自動登録。その後LLM照合で政治家に紐付け
politiciansStreamlit 発言者マッチング画面(conversations/components/politician_creation_form.py)照合対象の政治家が存在しないとき、発言者情報からPoliticianを新規作成するパスが存在