はじめに
2019年9月に国立国会図書館主催のGLAMデータを使い尽くそうハッカソンというものに参加しました。
その時に国会会議録検索システム検索用APIを使って「国会政治家相関図」というものを作って発表したのですが、思ったよりも周りからの評判が良かったです。
少し時間が経ちましたが、せっかくの機会なのでその時の内容を記事にしてみました。
国会会議録検索システム検索用APIとは?
国立国会図書館が提供しているAPIで、国会会議録検索システムで閲覧できる情報をこのAPI経由で取得できます。
国会会議録検索システムには国会の本会議や委員会での全ての発言内容がデータとして記録されており、第1回国会(1947年5月開会)分から保存されています。
国会で政治家や役人がどのような発言をしたのか、ある議論に関して誰がどのような発言をしたのかなどを確認することができます。
ただ、国会会議録検索システムもそうですが、APIも決して使い勝手が良いとは言えません。
例えば、
- APIのレスポンスが遅い
- APIのレスポンスがJSON形式はなくXML形式のみ
- クエリパラメータ全体をURLエンコードする必要がある
- 「増税」という単語で検索したい場合、
増税
ではなくany=増税
をURLエンコードする必要があります
- 「増税」という単語で検索したい場合、
という状況です。
中の人に聞いたところ、国会会議録検索システムは国立国会図書館の中でも一番レガシーなシステムであり、簡単にアップデートできるものではないようです。
ただ、ちょうどシステムを作り直しているところで1、来年(2020年)にはアップデート版がリリースされるそうです。
APIを使ってデータ収集
目的が特に決まっているわけではないので、特定の政治家名やキーワードで絞らずに国会での発言をまるっと全部取得します。
ただ、1947年から全部となると、
- APIを使っても全件収集するのに一ヶ月以上掛かる
- 昔の政治家はほとんど知らない
という理由から、2018年だけに絞りました。
import requests
import urllib.parse
import xml.etree.ElementTree as ET
import datetime
def main():
# 1月、2月、3月、…と月毎に収集
for month in range(1, 13):
# クエリパラメータの設定
q_maximumRecords = 100
q_from = str(datetime.date(year, month, 1))
if month != 12:
q_until = str(datetime.date(year, month+1, 1) - datetime.timedelta(days=1))
else:
q_until = str(datetime.date(year+1, 1, 1) - datetime.timedelta(days=1))
# 一回当たりの抽出件数が最大100件のため、全体のレコード数から必要なループ回数を決定
payload = 'from=' + q_from + '&until=' + q_until + '&maximumRecords=' + str(q_maximumRecords) + '&startRecord=1'
payload_encoded = urllib.parse.quote(payload)
r = requests.get(base_url + payload_encoded)
root = ET.fromstring(r.text)
loop_num = int(root[0].text) // q_maximumRecords + 1
# ループを回し、APIでデータ収集
i = 0
while i < loop_num:
q_startRecord = 1 + i * q_maximumRecords
payload = 'from=' + q_from + '&until=' + q_until + '&maximumRecords=' + str(q_maximumRecords) + '&startRecord=' + str(q_startRecord)
payload_encoded = urllib.parse.quote(payload)
r = requests.get(base_url + payload_encoded)
with open('./dat/' + str(year) + '_' + str(month) + '_' + str(i) + '.xml', mode='w') as f:
f.write(r.text)
i += 1
if __name__ == "__main__":
base_url = 'http://kokkai.ndl.go.jp/api/1.0/speech?'
year = 2018
main()
もっとシンプルに書きたかったのですが、クエリパラメータ全体をURLエンコードする必要などがあり、少し冗長なソースコードになっています。
このプログラムを実行すると、dat
配下に大量のXMLファイルが作成されます(私の環境では5,6時間程度掛かりました)。
国会での発言回数
2018年の一年分のデータを収集できたので、早速集計・分析に取り掛かります。
まずはシンプルに人物毎の発言回数を集計してみました。
先ほど収集したXMLファイルを一つずつ読み込んでいき、人物毎の発言回数を集計するだけです。
import xml.etree.ElementTree as ET
from collections import Counter
import glob
def main():
count_by_speaker = Counter()
file_paths = glob.glob('./dat/*.xml', recursive=True)
for file_path in file_paths:
tree = ET.parse(file_path)
root = tree.getroot()
num = int(root[1].text)
if root.find('nextRecordPosition') is not None:
for i in range(num):
count_by_speaker[root[4][i][0][0][6].text] += 1
else:
for i in range(num):
count_by_speaker[root[3][i][0][0][6].text] += 1
for speaker, count in sorted(count_by_speaker.items(), key=lambda x: -x[1]):
print(count, speaker, sep='\t')
if __name__ == "__main__":
main()
上記のプログラムを実行すると、発言回数が多い順に発言回数と人物名が表示されます。
結果は次のようになりました(上位20人だけを表示)。
総理大臣や国務大臣(例:外務大臣、財務大臣)が概ね上位に来ていますが、「太田充」さんや「山越敬一」さんなどの官僚の方も出てきます。
特に「太田充」さんは、2018年3月に森友学園問題が国会で集中的に取り上げられ、その影響で発言回数が多くなったのだと考えられます。
あと、1位が「安倍晋三」さんではなく「加藤勝信」さんだったのは、厚生労働省の守備範囲の広さを表しているのではないかと思います(「加藤勝信」さんは2017年11月1日〜2018年10月2日まで厚生労働大臣を務めた)。
3460 加藤勝信
2216 安倍晋三
1907 河野太郎
1340 太田充
1323 麻生太郎
1230 石井啓一
1206 世耕弘成
1062 金子原二郎
1057 林芳正
1045 伊達忠一
1032 山越敬一
1019 大島理森
1000 齋藤健
988 高鳥修一
951 野田聖子
853 福島みずほ
847 小野寺五典
769 河村建夫
733 薬師寺みちよ
721 石橋通宏
発言回数が多い順に表示できるということは、発言回数が少ない順にも表示できるということです。
ただ、当たり前ですが、発言をしないと国会会議録には残りません。
そのため、今回のプログラムでは発言回数がゼロの人は表示されません。
そこで、有名な政治家で発言回数がゼロの人を個別に調べました。
すると、「小泉進次郎」さん、「石破茂」さん、「小沢一郎」さんなどが該当しました。特に、「小泉進次郎」さんはテレビではよく見かけるので、かなり意外でした…。
後から知ったのですが、この辺りの内情は朝日新聞が発行しているAERAの記事でも取り上げられていました。
国会での発言内容
発言回数だけでは面白くないので、発言内容も見ていきます。
具体的には、「保育園」や「消費税」のように特定のキーワードを含む発言に絞った上で、人物毎の発言回数を集計してみました。
こうすることで、「保育園」や「消費税」を国会で取り上げている人が分かります。
import xml.etree.ElementTree as ET
from collections import Counter
import glob
def main():
count_by_speaker = Counter()
file_paths = glob.glob('./dat/*.xml', recursive=True)
for file_path in file_paths:
tree = ET.parse(file_path)
root = tree.getroot()
num = int(root[1].text)
if root.find('nextRecordPosition') is not None:
for i in range(num):
if '保育園' in root[4][i][0][0][7].text:
# if '消費税' in root[4][i][0][0][7].text:
count_by_speaker[root[4][i][0][0][6].text] += 1
else:
for i in range(num):
if '保育園' in root[3][i][0][0][7].text:
# if '消費税' in root[3][i][0][0][7].text:
count_by_speaker[root[3][i][0][0][6].text] += 1
for speaker, count in sorted(count_by_speaker.items(), key=lambda x: -x[1]):
print(count, speaker, sep='\t')
if __name__ == "__main__":
main()
上記のプログラムを実行すると、先ほどと同じように、発言回数が多い順に発言回数と人物名が表示されます。
まずは、「保育園」を含む発言に絞った場合の結果です(上位20人だけを表示)。
1位は厚生労働省の官僚、2位は厚生労働相の大臣ですが、3位以下には政治家が並んでいます。
女性議員、および男性議員でも子育て政策に重点的に取り組んでいる方が並んでいます。例えば、「浦野靖人」さんは公式ホームページで子どもたちと一緒に写っている写真が大きく掲載されており2、子育て政策に力を入れていることが分かります。
43 成田裕紀
26 加藤勝信
23 浦野靖人
13 矢田わか子
11 田村智子
9 高木美智代
9 和田政宗
9 吉田学
9 赤嶺政賢
8 松山政司
8 伊波洋一
8 中川正春
7 森田俊和
7 阿部知子
7 山本太郎
7 安倍晋三
7 永岡桂子
6 小野寺五典
6 杉本和巳
6 大河原雅子
次に、「消費税」を含む発言に絞った場合の結果です(上位20人だけを表示)。
先ほどの「保育園」とは異なった顔ぶれとなっております。
1位が総理大臣、2位が財務大臣、4位が経済再生担当大臣となっており、政府として消費税増税問題に力を入れていたことが分かると思います。
101 安倍晋三
72 麻生太郎
24 野田佳彦
21 茂木敏充
20 黒田東彦
20 山本太郎
18 稲富修二
16 加藤勝信
15 星野次彦
15 野田聖子
15 松山政司
14 宮腰光寛
14 川内博史
14 古本伸一郎
13 世耕弘成
13 山井和則
13 東徹
13 中山恭子
13 吉川沙織
12 礒崎哲史
政治家相関関係
政治家毎に国会での発言に含まれるキーワードに特徴(偏り)があるのであれば、それらのキーワードをもとに政治家同士の相関関係を出すことができます。
今回は可能性や有用性を確認するという観点から、各単語の発言回数から相関係数を算出するというシンプルな手法を採用しました。
具体的には、次のようなステップで相関係数を計算しました。
- 発言者に関係なく、全ての発言を対象に単語の発言回数を集計する。単語分割にはMeCabを利用。
- 1で集計した単語の中から、発言回数が20回以上の単語を抽出する。
- 2で抽出した単語の発言回数を発言者毎に集計し、発言者毎のベクトルを得る。例えば、Aさんの発言回数が「憲法」で10回、「増税」で3回、「子育て」で5回だったとします。その場合、Aさんのベクトルは
[10, 3, 5]
となります。ただし、2で抽出した単語の数はもっと多いので、実際のベクトルの次元数は3ではなく数千次元になりました。 - 3で得た発言者毎のベクトルからコサイン類似度を計算し、発言者同士の相関係数を得る。
今回、プログラムは少し長くなるので省略します。
そして、全員分の結果を載せることはできないので、「安倍晋三」さんとの相関係数が高い上位10人だけを載せました。類似度が高い順にコサイン類似度と人物名となります。
10位の「田中良生」さんを除いては、全員が立憲民主党や国民民主党などに所属する(していた)野党の政治家でした。
その中で、1位は国民民主党党首の「玉木雄一郎」さんでした。
「安倍晋三」さんと「玉木雄一郎」さんの類似度が一番高くなった原因を調べるべく、この二人にだけ特に共通して出てくる単語を調べました。
そうすると、浮かび上がってきたのは改憲
や憲法
などの憲法改正に関連する単語でした。なお、2018年の国会の中で改憲
という言葉を一番多く使ったのは「玉木雄一郎」さんでした。
2019年の参議院選挙が終わった後、
先の参議院選挙で、憲法改正に前向きな勢力が、改正の発議に必要な3分の2の議席を維持できなかったことを受けて、安倍総理大臣は「国民民主党の中には、憲法改正について、議論すべきと考えている方々もたくさんいる」と述べていて、今後、与野党の枠を超えた連携が進むのか注目されます。
というニュースがありましたが、今回の結果はこのような発言が出てきた背景を映し出しているかもしれません。
0.75 玉木雄一郎
0.73 大野元裕
0.71 阿部知子
0.71 藤田幸久
0.70 牧山ひろえ
0.70 白眞勲
0.70 難波奨二
0.70 伊藤孝恵
0.69 枝野幸男
0.69 田中良生
今回は、単語の発言回数から発言者毎のベクトルを得て、発言者同士の類似度を計算するというシンプルな手法を試しました。
さらに精度を高めようと思えば、例えば、単語の発言回数ではなくて単語の分散表現を利用するという方法が考えられます。
単語の分散表現については、二年前に書いた記事にまとめていますので、興味がある方はこちらをご覧下さい。
また、上記の相関係数は国会の発言だけ、さらに言えば発言に含まれる単語だけから計算したものです。
文脈や意図などは全く考慮できていませんので、その点はご留意下さい。
おわりに
国会会議録検索システム、およびそのAPIを利用し、2018年の国会の中で
- 発言が多い人/少ない人
- 特定の単語について多く発言している人
- 政治家同士の相関関係 ※あくまでも国会での発言に含まれる単語から計算したもの
を出しました。
GLAMデータを使い尽くそうハッカソンで国会会議録を扱ったのは、若者の政治離れなどを叫ばれる昨今の状況の中で、政治、特に国会での議論をより身近なものにしたいという思いがあったからです。
例えば、特定の単語について多く発言している人などは、選挙で誰に投票するのか?の判断材料の一つになり、政治をより身近なものにするための架け橋になるかもしれません。
さらに、Twitterなどのデータも使えば、国会での発言機会がない新人候補のテキストデータも扱うことが可能になります。
政治をより身近なものにするための手段としては、Yahoo!ニュースによる「政党との相性診断」などが既にありますが、国会での議論に着目した今回のようなサービスをウェブサイトなどで公開できればと思っています3。