Webスクレイピング練習 Elasticsearch との連携してみる
※東京都コロナサイトの仕様が変わったようで、このコードは数字とれなくなっています
文中に Github開発ソースから取得する版を載せています
Webスクレイピングの練習で、新型コロナ情報を取得してElasticsearch・Kibanaで見える化してみます
ついつい陽性者の数ばっか目がいってしまって、なんだか全容を(自分が)わかってないなぁってのも動機のひとつです
【関連サイト】
stopcovid19.metro.tokyo.lg.jp
「Github 東京都 新型コロナウイルス感染症対策」の開発ソースはこちらで公開されています
github.com
東京都の感染者数のソースデータは、下記より入手できます
catalog.data.metro.tokyo.lg.jp
が、
検査数と陽性率はの元ネタをどこからとってくればよいのか分からなかったので、「東京都 新型コロナウイルス感染症対策サイト」より、Webスクレイピングでとってきます
大したアクセスはしないのですが、念のためコロナWebサイトの負担にならないように
開発用サイト(https://dev-covid19-tokyo.netlify.app/)から取得します
=開発環境====
Python 3.8
Elasticsearh 7.8
========
Webスクレイピングで数字を取得
開発用サイトの「モニタリング項目(4) 検査の陽性率」からデータを入手するところから始めます
↓このテーブルのところ
from bs4 import BeautifulSoup import requests # モニタリング項目(4)のURlをスクレイピング url = 'https://stopcovid19.metro.tokyo.lg.jp/cards/positive-rate/' r = requests.get(url) soup = BeautifulSoup(r.text, 'html.parser') # データのTable部分からデータ抜き出し tb = soup.find_all('tbody') tr = tb[0].find_all('tr') # IDを日付順にする為に tr.reverse()
こんな感じで感染者数が入っているテーブル部分が取得できます
tr [<tr><th class="cardTable-header" scope="row">2/15</th> <td class="text-end"> 8 </td><td class="text-end"> </td><td class="text-end"> 122 </td><td class="text-end"> </td><td class="text-end"> </td><td class="text-end"> 7.7 </td></tr>, <tr><th class="cardTable-header" scope="row">2/16</th> <td class="text-end"> 5 </td><td class="text-end"> </td><td class="text-end"> 69 </td><td class="text-end"> </td><td class="text-end"> </td><td class="text-end"> 7.5
Elasticsearchにデータを送る
Elasticsearch に送る数値をうまいこと抜き出します
日付の部分はこの先長くなることも覚悟のうえで、2020年をつけて "date"型に変換しておきます
#空は0を挿入、カンマ(,)付きの数字はバラして結合 def num_get(data): match = re.compile('\d+') num = match.findall(data) if num: num = int(''.join(num)) else: num = 0 return num # elasticsearch へ import datetime import re i=0 actions = [] index_name = 'covid19-tokyo' for tr_day in tr: i+=1 doc ={} th = tr_day.find_all('th') _date = re.compile('\d+').findall(th[0].string) doc['date'] = datetime.date(2020, int(_date[0]), int(_date[1])) kensa = tr_day.find_all('td') # 検査結果取り出し doc['pcr_positive'] = num_get(kensa[0].string) doc['antigen_positive'] = num_get(kensa[1].string) doc['pcr_negative'] = num_get(kensa[2].string) doc['antigen_negative'] = num_get(kensa[3].string) # 検査人数合計と陽性率 doc['pos_total'] = doc['pcr_positive'] + doc['antigen_positive'] doc['kensa_total'] = doc['pcr_positive'] + doc['antigen_positive'] + doc['pcr_negative'] + doc['antigen_negative'] # 0除算対策 if doc['kensa_total'] == 0: doc['pos_rate'] = 0 else: pos_rate = doc['pos_total'] / doc['kensa_total'] * 100 doc['pos_rate'] = round(pos_rate, 2) actions.append({'_index':index_name, '_id':i, '_source':doc}) # Elasticsearchへのインサートをまとめて実行 helpers.bulk(es, actions)
ここまでのまとめ
※東京コロナサイトの仕様変更に伴いWebスクレイピングできなくなったので、Github開発ソールからデータ取得版
Kibanaの設定は共通でいけます
Kibanaで可視化
Dev Tools で Mapping を確認してみると、ちゃんと date は 'date'型になっているのが確認できます
Visualizations のメニューから "Vertical Bar" を選択してグラフを作っていきます
Dataとしては
・Y軸の1つ目には、'kensa-tatal'(検査人数)
・Y軸の2つ目には、'pos-rate'(陽性率)
Metricとして
・検査人数を Left-Axis, Bar
・陽性率 Right-Axis, Line
こんな感じでぱっと見検査人数と陽性率がみれるようになりました
やっぱGWあたりは陽性率高かったんだなぁなんてのが、分かるようになりました
これを Save しておけば、Dashboard で呼び出すことができます
もっと色々な情報をDashboardに増やしていきたい
このあたり全部 Kubernetes 上で開発しているので、応用が利くので
さぁ今度こそ、そろそろ、いろいろ、準備が整ってきたので
Xi IoTアプリに生かしていこう