はじめに
先日、e-Gov データポータルを利用する機会がありました。
このサイト(e-Govデータポータル)は、行政機関等が保有する公共データのうち、オープンデータとして提供するものを対象としてカタログを整備し、横断的な検索を可能とするとともに、提供されるオープンデータの内容をわかりやすく提示することにより、オープンデータの活用に資することを目的としてデジタル庁が整備、運営するWebサイトです。
(https://data.e-gov.go.jp/info/ja/about-site から引用)
ある統計データを探していたのですが、各機関のリソースが無秩序に突っ込まれている感があり、残念ながらWebサイトでの検索は使い勝手がいいとはいえません。
PowerShellでの検索操作を試行したところ、それなりに活用できそうなので備忘録も兼ねて投稿します。
動作確認環境
PowerShell 7.3.4
データセットの検索
例として「人口動態」に関するデータセットを検索してみます。
APIの使い方はCKANのAPIガイドが参考になります。
とりあえず「人口動態」をキーワードに検索してみます。
URLで呼び出しが可能なAPI関数は多数ありますが、今回はpackage_searchのみを使っています。
Invoke-WebRequestでAPIを呼び出した結果はJSONで得られるのでPSCustomObjectに変換します。
$egovApi = 'https://data.e-gov.go.jp/data/api/3/action/package_search?'
$query = 'q=人口動態'
$dataset = Invoke-WebRequest "$egovApi$query" | ConvertFrom-Json
引っかかったデータセットの数です。(※ 以下、ヒット数は 2023.6.15 現在の値)
当然、Webサイトで検索した結果も同数です。
$dataset.result.count
# 2171
公開されているデータセットは複数のリソース(個別データ等)から成り立っています。
検索結果はデータセットの表題以外に、リソースの名前や説明文などにもヒットしているので、その数は大きくなります。
なお、ヒット数に関係なく、Invoke-WebRequestが返すデータセットは「関連性」順の上位10セット(既定値:変更可能)です。
$dataset.result.results | % title
# 人口動態調査_人口動態統計_確定数_総覧_年次_2012年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2013年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2011年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2019年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2018年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2016年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2017年
# 人口動態調査_人口動態統計_確定数_死亡_年次_2016年
# 人口動態調査_人口動態統計_確定数_用語及び比率の解説_年次_2020年
# 人口動態調査_人口動態統計_確定数_用語及び比率の解説_年次_2019年
雰囲気がわかったので、次の条件でクエリを書き直し、再度検索してみます。
● 表題に「人口動態」と「総覧」を含む
● 結果を表題で降順ソートする
● ソートされたデータセットの先頭から最大20セットのデータを取得する
各パラメータのフィールドや値はWebサイトで検索した際のアドレスバーから推測して動作確認しています。
$query = 'q=title:人口動態+title:総覧&sort=title_string desc&start=0&rows=20'
$dataset = Invoke-WebRequest "$egovApi$query" | ConvertFrom-Json
$dataset.result.count
# 16
ヒット数は16まで絞られました。
20セットに収まったので、全表題を書き出してみます。
$dataset.result.results | % title
# 人口動態調査_人口動態統計_確定数_総覧_年次_2021年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2020年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2019年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2019年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2018年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2018年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2017年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2017年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2016年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2016年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2015年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2014年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2014年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2013年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2012年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2011年
何故か同じ表題のデータセットが複数あります。
Webサイトで同条件を検索すると(下のリンクカード)残念な事実が判明します。
データセットをフォーマットで分割した年としない年もあれば、 XLS と XLSX も混在しています。😩
リソースの取得
気を取り直してリソースの取得まで頑張ります。
例1 Excelファイルの場合
幸い、クエリにはフォーマットも指示できます。
Excel データを検索する場合はqパラメータにres_format:XLS*を追加します。
XLSは大文字です。また、res_format:XLSでは XLSX にヒットしません。
$query = 'q=title:人口動態+title:総覧+res_format:XLS*&sort=title_string desc&start=0&rows=20'
$dataset = Invoke-WebRequest "$egovApi$query" | ConvertFrom-Json
$dataset.result.results | % title
# 人口動態調査_人口動態統計_確定数_総覧_年次_2019年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2018年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2017年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2016年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2015年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2014年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2013年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2012年
# 人口動態調査_人口動態統計_確定数_総覧_年次_2011年
検索結果中、フォーマットが混在していた2015年のデータセットを例にしてExcelファイルを探します。
$dataset.result.results[4].resources | ? format -match XLS | Format-Table position, name, format
#
# position name format
# -------- ---- ------
# 6 上巻_3-4_世界各国における人口動態 XLSX
リソースの位置や見出しがわかったのでファイルを保存します。
先にデータのアドレスを取得します。
$url = $dataset.result.results[4].resources[6].url
ユーザーの環境(拡張子の関連づけ)にもよりますが、通常は次の1行でブラウザが起動し、ファイルのダウンロードまで完了します。
Start-Process $url
任意の場所や名前で保存したい場合はInvoke-WebRequestに-OutFileオプションをつけて保存します。
# 例
$filename = "$($dataset.result.results[4].resources[6].name)2015.xlsx"
Invoke-WebRequest $url -OutFile $filename
# 保存されたか確認
Get-ChildItem $filename | Format-Table Name, LastWriteTime, Length
#
# Name LastWriteTime Length
# ---- ------------- ------
# 上巻_3-4_世界各国における人口動態2015.xlsx 2023/06/15 5:55:00 33370
例2 csvファイルの場合
ファイルの保存は、Excelの場合と同様ですので省略します。
ここではcsvの直接処理を試みます。
先の検索結果から、最新になる2021年のデータを題材にします。
$query = 'q=title:人口動態+title:総覧+title:2021'
$dataset = Invoke-WebRequest "$egovApi$query" | ConvertFrom-Json
$dataset.result.count
# 1
$dataset.result.results[0].num_resources
# 54
ヒットしたデータセットは想定どおり1つで、そのリソース数は54です。
少し多いのでリソースの先頭10項目を表示してみます。
$dataset.result.results[0].resources[0..9] | Format-Table position, name, format
#
# position name format
# -------- ---- ------
# 0 上巻_3-1-1_人口動態総覧及び平均発生間隔 -対前年比較- CSV
# 1 上巻_3-1-2_本報告において別掲とした件数 CSV
# 2 上巻_3-2-1_年次別にみた人口動態総覧 CSV
# 3 上巻_3-2-2_年次別にみた人口動態総覧(率) CSV
# 4 上巻_3-3-1_都道府県(特別区-指定都市再掲)別にみた人口動態総覧 CSV
# 5 上巻_3-3-2_都道府県(特別区-指定都市再掲)別にみた人口動態総覧(率) CSV
# 6 中巻_1_人口動態総覧,都道府県(特別区-指定都市再掲)別 CSV
# 7 中巻_2_人口動態総覧(件数),都道府県・市部-郡部-保健所-市区町村別_01 北海道 CSV
# 8 中巻_2_人口動態総覧(件数),都道府県・市部-郡部-保健所-市区町村別_02 青森県 CSV
# 9 中巻_2_人口動態総覧(件数),都道府県・市部-郡部-保健所-市区町村別_03 岩手県 CSV
残りは都道府県別データと推測できます。
新潟県のデータを探してみます。
$dataset.result.results[0].resources | ? name -Match 新潟県 | Format-Table position, name, format
#
# position name format
# -------- ---- ------
# 21 中巻_2_人口動態総覧(件数),都道府県・市部-郡部-保健所-市区町村別_15 新潟県 CSV
上記リソースのurlからデータを読み込みます。
$url = $dataset.result.results[0].resources[21].url
$response = Invoke-WebRequest $url
csvテキストと想定されるデータについて最初の100文字を表示します。
$response.Content.Substring(0, 99)
# �ߘa�R�N,�l�����ԓ��v,,
# �����@�����@��Q�\�i15�V�����j�@�l�����ԑ����i�����j�C�s���{���E�s���|�S���|�ی
お察しのとおり、シフトJISが原因で文字化けしていますが、化け方に違和感があります。
この手の文字化け対策は過去にも記事が投稿されていますが、この方法が通用しません。
違和感の原因はこれです。
$response.Headers.'Content-Type'
# text/csv; charset=UTF-8
あろうことか、シフトJISのバイト列をUTF-8でデコードするように指示されています。😩😩
Invoke-WebRequestは、指示どおりシフトJISのバイト列をUTF-8として解釈しようとするので、2バイト文字の大半は'�'U+FFFD<REPLACEMENT CHARACTER>に置き換えられています。
つまり$response.Contentで取得した文字列から、元のシフトJISバイト列への復元は不可能です。
なお、上記の記事のように、charsetがISO-8859-1(未指定時の既定文字コード)であれば、1バイト文字コードなのでバイト列の復元は可能です。
ここでは、System.Net.Http.HttpClientを使うことにします。
本体をバイト列で受け取り、シフトJISでデコードします。
$httpClient = New-Object System.Net.Http.HttpClient
$dataBin = $httpClient.GetByteArrayAsync($url).GetAwaiter().GetResult()
$encSJIS = [System.Text.Encoding]::GetEncoding(932)
$dataCsv = $encSJIS.GetString($dataBin)
先頭の10行を表示してみます。
($dataCsv -split "`r`n")[0..9]
# 令和3年,人口動態統計,,
# 中巻 総覧 第2表(15新潟県) 人口動態総覧(件数),都道府県・市部-郡部-保健所-市区町村別
# ,出生数,(再掲),死亡数,(再掲), ,死産数, ,周産期死亡, , ,婚姻件数,離婚件数
# , ,2500g未満, ,乳児死亡数,新生児,自然死産,人工死産,総 数,22週以後,早期新生児, ,
# ,,,,,死亡数,,,,の死産数,死亡数,,
# 15 新潟県 ,12608,1114,30990,21,12,138,124,59,47,12,7088,2617
# 新潟市 ,5132,472,9595,7,4,50,52,24,20,4,2906,1023
# その他の市,7131,627,20111,14,8,87,70,35,27,8,3980,1511
# 郡 部 ,345,15,1284,-,-,1,2,-,-,-,202,83
# 1501新潟市 ,5132,472,9595,7,4,50,52,24,20,4,...,...
データを正しく取得できました。
csvデータとしてはかなり汚い(おそらく、Excelの表をそのままcsvで保存した?)ものですが、とりあえず文字列を取得できればPowerShellで加工できます。
なお、現在は非推奨とされていますがSystem.Net.WebClientでもデータを読むことができます。
$webClient = New-Object System.Net.WebClient
$webClient.Encoding = $encSJIS
$dataCsv = $webClient.DownloadString($url)
例3 csvファイルで「正統派?」文字化けの例
もう一例、「国民の祝日」を取り上げますが、こちらは正統派の文字化けです。
$query = 'q=title:祝日'
$dataset = Invoke-WebRequest "$egovApi$query" | ConvertFrom-Json
$dataset.result.count
# 1
$dataset.result.results[0].num_resources
# 1
ヒットしたデータセットは1つで、そのリソースも1つです。
$dataset.result.results[0].resources[0] | Format-Table name, format
#
# name format
# ---- ------
# 昭和30年(1955年)から令和2年(2020年)国民の祝日等(いわゆる振替休日等を含む)(csv形式:19KB) CSV
目的のデータで間違いありません。
文字化けを見るために先頭100文字を表示します。
$url = $dataset.result.results[0].resources[0].url
$response = Invoke-WebRequest $url
$response.Content.Substring(0, 99)
# ¯ÌjúExúú,¯ÌjúExú¼Ì
# 1955/1/1,³ú
# 1955/1/15,Ìú
# 1955/3/21,tªÌú
# 1
前の例と文字化けの質が違います。「正統派」の文字化けです。
charsetは未指定なので、ISO-8859-1(Latin-1)が適用されています。
$response.Headers.'Content-Type'
# text/csv
$response.Encoding.WebName
# iso-8859-1
あらためて$response.Contentを見ると、2バイト文字がISO-8859-1の上位領域に割り振られている文字に化けています。
復元は前例のようにHttpClientを使うこともできますが、こちらは先ほど参照した記事の手法を検証します。
$enc8859 = [System.Text.Encoding]::Latin1
$dataBin = $enc8859.GetBytes($response.Content)
$dataCsv = $encSJIS.GetString($dataBin)
先頭10行を見ます。
($dataCsv -split "`r`n")[0..9]
# 国民の祝日・休日月日,国民の祝日・休日名称
# 1955/1/1,元日
# 1955/1/15,成人の日
# 1955/3/21,春分の日
# 1955/4/29,天皇誕生日
# 1955/5/3,憲法記念日
# 1955/5/5,こどもの日
# 1955/9/24,秋分の日
# 1955/11/3,文化の日
# 1955/11/23,勤労感謝の日
しっかり復元できています。
データのフォーマットも世間の荒波に揉まれて前例より行儀のよいcsvになっています。
その推移についてはこちらにまとめられています。 → 「祝日オープンデータを巡って」
ついでにデータをいじってみます。
今年度(2023.4 〜 2024.3)の祝日一覧を抽出し、クリップボードにコピーします。
-split $dataCsv -match '2023|2024/[1-3]/' -notmatch '2023/[1-3]/' | % replace ',' `t | Set-Clipboard
タブ区切りにしたのでスプレッドシートにそのままペーストできます。

まだまだ整備が不十分なe-Govデータポータルですが、データ処理の練習材料にできそうです。