konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

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) 検査の陽性率」からデータを入手するところから始めます
↓このテーブルのところ
f:id:konchangakita:20200723213025p:plain

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'型になっているのが確認できます
f:id:konchangakita:20200723213740p:plain


Visualizations のメニューから "Vertical Bar" を選択してグラフを作っていきます
f:id:konchangakita:20200723214640p:plain

Dataとしては
 ・Y軸の1つ目には、'kensa-tatal'(検査人数)
 ・Y軸の2つ目には、'pos-rate'(陽性率)
f:id:konchangakita:20200723215128p:plain

Metricとして
 ・検査人数を Left-Axis, Bar
 ・陽性率 Right-Axis, Line
f:id:konchangakita:20200723215201p:plain


こんな感じでぱっと見検査人数と陽性率がみれるようになりました
やっぱGWあたりは陽性率高かったんだなぁなんてのが、分かるようになりました
f:id:konchangakita:20200723214236p:plain


これを Save しておけば、Dashboard で呼び出すことができます
もっと色々な情報をDashboardに増やしていきたい
f:id:konchangakita:20200723225322p:plain


このあたり全部 Kubernetes 上で開発しているので、応用が利くので
さぁ今度こそ、そろそろ、いろいろ、準備が整ってきたので
Xi IoTアプリに生かしていこう