[BOD供養寺] スクレイピングしてきたデータの文字コードがおかしかったので修正した

Code for Japan Summit の人気企画に、「BADオープンデータ供養寺」というコンテンツがあります。

[BADオープンデータ供養寺](https://youtu.be/hWs0jal7R1Q)
> 【セッション概要】
> 世の中のBADオープンデータが二度とこの世を彷徨わないように、「供養（データクレンジング）」する方法を考える場です。
> 
> データの公開に携わる行政職員の方や、データを利活用するエンジニア・データサイエンティスト等の皆さまと、より使いやすく品質の高いオープンデータの公開と加工の仕組みを考えていくために建立されました。
> 
> 前半はパネリストが、日頃の業務の中で、どのようなBADオープンデータにいかに対処してきたか、実例やクレンジング技術を紹介します。
> 後半では事前投稿されたBADオープンデータを紹介しながら、オーディエンスの皆さまと一緒に成仏させる方法を考えて行きたいと思います。

ちょうど最近、総務省が公開している[マイナンバーカードの交付状況](https://www.soumu.go.jp/kojinbango_card/)というPDFからデータを抜き出してCSVデータ化する中で、BADなデータを見つけたので供養の経緯を書いておきます。

# 埼玉県が2つある？

PDFからテーブルを抜き出してテキスト化できる[camelot](https://camelot-py.readthedocs.io/en/master/)を使ってデータを取得してCSVファイルに保存した後、それを Tableau から表示しようとしたところ、いくつかうまくUnionできないデータがあり、なぜか「埼玉県」のデータが2種類できてしまいました。
そこで良く調べてみた所、[マイナンバーカード交付状況（平成29年3月8日時点）](https://www.soumu.go.jp/main_content/000490028.pdf)とそれ以降（例えば[マイナンバーカード交付状況（平成29年5月15日時点）](https://www.soumu.go.jp/main_content/000490029.pdf)）では、「玉」という時の文字コードが違っていたのです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">総務省のマイナンバーカード交付状況をデータ化していて、データの結合がうまくいかないなーと思ったら、なんと、同じ埼玉でも文字コードが違うという落とし穴が・・<br>3月8日では \u2f5f が使われていて、それ以降では\u7389…<a href="https://t.co/AOkV3iojaz">https://t.co/AOkV3iojaz</a> <a href="https://t.co/jU4P583Ad5">pic.twitter.com/jU4P583Ad5</a></p>&mdash; Hal Seki (@hal_sk) <a href="https://twitter.com/hal_sk/status/1281853581218336768?ref_src=twsrc%5Etfw">July 11, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# unicodedata.normalize で正規化

色々調べてみた結果、「山」や「大」、「手」などといった文字でも平成29年3月8日時点のみ、2f から始まるコードが使われていました。2fシリーズは、部首作成用のコードらしいのですが、どうやって入力したのだろう・・・

```python

>>> '⽟'.encode('utf-8')
b'\xe2\xbd\x9f'
>>> '玉'.encode('utf-8')
b'\xe7\x8e\x89'
```

※[PDFからコピペしてそうなったのでは？](https://twitter.com/0guma/status/1281937597929943041)という推理がありました。確かに、PDFからコピーしたときに、濁点とかが2文字に別れちゃったりもしますよね。

`unicodedata.normalize` で正規化をすることで、一般的な玉（`u7389`）にすることができました。

```python
>>> import unicodedata
>>> unicodedata.normalize('NFKC', '⽟').encode('utf-8') #こちらは 2f から始まる玉
b'\xe7\x8e\x89'
>>> unicodedata.normalize('NFKC', '玉').encode('utf-8')
b'\xe7\x8e\x89'
```

## 余計な変換までしないように、対象を限定

ただし、安易に `normalize` してしまうと、意図しない文字まで変換してしまう可能性があるというご指摘をいただきました。
そのため、テキストが [Kangxi Radicals](https://en.wikipedia.org/wiki/Kangxi_radical#:~:text=They%20are%20officially%20part%20of,the%20%22CJK%20Radicals%20Supplement%22.)にある範囲のみに絞って文字を `normalize` することにします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1399/a5a9e553-0aef-f8ac-dc70-6b5ca164c0cb.png" width="400px")>

```python
if (c >= b'\xe2\xbc\x80'.decode('utf-8') and
    c <= b'\xe2\xbf\x95'.decode('utf-8')):  # '⼀'から'⿕' の範囲
    return unicodedata.normalize('NFKC', c)
else:
    return c
```

[全体のフローはこちら](https://github.com/codeforjapan/mynumbercard_statistics/blob/fbfd2266cd568367b6d1a5da060570377ca6b274/converter.py#L16) にあります。

というわけで、無事BADオープンデータを修正し、お焚き上げすることができました。南〜無〜。

ちなみに、マイナンバーカードの普及率を一覧で見れ、生データもダウンロードできるというサイトを作り中です。

サイト：https://mynumbercard.code4japan.org/
GitHub：https://github.com/codeforjapan/mynumbercard_statistics/

仲間を募集中なので、もしご協力いただける方がいたら、ぜひご連絡ください。

[Code for Japan の Slack](https://cfjslackin.herokuapp.com/) に入り、`Hal Seki(@hal_sk)` にお声がけいただいても大丈です。

(そもそも、元々のオープンデータをPDFではなく、自治体カードつきの csv ファイルで提供してくれればこんな回りくどいことは必要ないのですけどね。)