新型コロナウイルスまとめサイトdata.jsonの作り方(テキストマイニング)

imabari.hateblo.jp

サンプル

ベストプラクティス

富山県のようなオープンデータだとテキストマイニング不要で集計するだけですみます

opendata.pref.toyama.jp

opendata.pref.toyama.jp

Webページ(リンク先抽出)

import requests
from bs4 import BeautifulSoup

import re
import datetime

from urllib.parse import urljoin

# データのあるページのURL
url = "http://example.jp"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
}

r = requests.get(url, headers=headers)
r.raise_for_status()
soup = BeautifulSoup(r.content, "html.parser")

# CSV・XLSX・PDFのリンクのテキストを入力
tag = soup.find("a", text=re.compile("^○○○○○○○○"))

link = urljoin(url, tag.get("href"))

PDF

import camelot
import pandas as pd

# 短い縦線が検出できず分割できない場合が多いのでline_scale=40に設定
tables = camelot.read_pdf(
    link, pages="all", split_text=True, strip_text="\n", line_scale=40
)

# ページごとに分かれているので結合する
dfs = [table.df for table in tables]
df = pd.concat(dfs)

XLSX

import pandas as pd

df = pd.read_excel(link)

CSV

import pandas as pd

df = pd.read_csv(link)

新型コロナウイルスまとめサイトdata.jsonの作り方(前準備)

テーブル

  • 下記ソース種類からテキストを抽出し、Pandasを使ってこのテーブルを作成する
年月日 検査数 陽性数 陰性数 入院 退院 死亡 軽症 中等症 重症 相談件数
2020-04-01
2020-04-02
2020-04-03
2020-04-04
2020-04-05

ソースの種類

Pythonの場合、下記ライブラリを使いテキストを抽出する

Webページ

PDF

  • Camelot(PDFからDataFrameに変換)
  • Pandas(データラングリング)

xlsx

  • Pandas(データラングリング)

CSV

  • Pandas(データラングリング)

画像

  • Tesseract OCROCR
  • Pyocr(OCR
  • Pandas(データラングリング)

インストール ※Ubuntuの場合

共通

pip install pandas

Webページ

pip install requests
pip install beautifulsoup4

PDF

apt install python3-tk ghostscript
pip install camelot-py[cv]
pip install jaconv

OCR

!add-apt-repository ppa:alex-p/tesseract-ocr -y
!apt update
!apt install tesseract-ocr
!apt install libtesseract-dev
!apt install tesseract-ocr-jpn  tesseract-ocr-jpn-vert
!apt install tesseract-ocr-script-jpan tesseract-ocr-script-jpan-vert
!apt install python3-pil
!pip install pyocr

つづき imabari.hateblo.jp

栃木県における新型コロナウイルス感染症の発生状況一覧をスクレイピング

www.pref.tochigi.lg.jp

github.com

apt install python3-tk ghostscript
pip install camelot-py[cv]
pip install jaconv
import requests
from bs4 import BeautifulSoup

import re
import jaconv
import datetime

from urllib.parse import urljoin

import camelot
import pandas as pd

url = "http://www.pref.tochigi.lg.jp/e04/welfare/hoken-eisei/kansen/hp/coronakensahasseijyoukyou.html"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
}

r = requests.get(url, headers=headers)
r.raise_for_status()
soup = BeautifulSoup(r.content, "html5lib")

tag = soup.find("a", text=re.compile("^栃木県における新型コロナウイルス感染症の発生状況一覧"))

link = urljoin(url, tag.get("href"))

tables = camelot.read_pdf(
    link, pages="all", split_text=True, strip_text="\n", line_scale=40
)

dfs = [table.df for table in tables]

df_tmp = pd.concat(dfs)

df = df_tmp.T.set_index(0).T.set_index("番号")

df["陽性判明日"] = df["陽性判明日"].apply(
    lambda s: jaconv.z2h(s, kana=False, digit=True, ascii=True)
)

df_date = df["陽性判明日"].str.extract(
    "(\d{1,2}/\d{1,2})\s*(\((\d{1,2}/\d{1,2}) +(.+)\))?", expand=True
)

df_date.fillna("", inplace=True)

dt_now = datetime.datetime.now()


def my_parser(s):

    if s:
        y = dt_now.year
        m, d = map(int, re.findall("[0-9]{1,2}", s))

        return pd.Timestamp(year=y, month=m, day=d)

    else:
        return pd.NaT


df["陽性判明日"] = df_date[0].apply(my_parser)

df_date[2] = df_date[2].apply(my_parser)

df["退院"] = df_date[2].where(df_date[3] == "退院")

df_date

df.to_csv("covid19.csv")

陽性患者情報のフォームを作成

サンプルは富山県のオープンデータで作成しました

opendata.pref.toyama.jp

出典:[コロナウィルス関連データ(陽性患者属性のみ)(CSV)]富山県ホームページ(当該ページのURL http://opendata.pref.toyama.jp/dataset/covid19/resource/f3cd8c90-bf77-4072-96a3-96bd5942ff20)を加工して作成

imabari.hateblo.jp

上記に準拠

フォームサンプル

docs.google.com

公開用オープンデータ

https://docs.google.com/spreadsheets/d/e/2PACX-1vSVIpXMkJv3ety4y0JmZBQlITgPvkPrx0-PgPe_OuQRisMbZkNivnHINAXPS2qUk07cHsBjjdXS-oCq/pub?output=csv

docs.google.com

新型コロナウイルス感染症対策に関するオープンデータ項目定義書

cio.go.jp

www.code4japan.org

新型コロナウイルス感染症対策に関するオープンデータ項目定義書

  • 非常に使いにくい
  • ファイル数が多い

  • 日別のファイルを一つにまとめたものから上記に準拠するファイルに切り出した方が管理も楽

石川県の患者情報スクレイピング

github.com

www.pref.ishikawa.lg.jp

import datetime
import re

import jaconv
import pandas as pd
import requests
from bs4 import BeautifulSoup


# 和暦から西暦のdateに変換
def wareki2date(s):

    m = re.match(r"(昭和|平成|令和)(\d{1,2})年(\d{1,2})月(\d{1,2})日", s)

    year = int(m.group(2))
    month = int(m.group(3))
    day = int(m.group(4))

    if m.group(1) == "昭和":
        year += 1925
    elif m.group(1) == "平成":
        year += 1988
    elif m.group(1) == "令和":
        year += 2018

    result = datetime.date(year, month, day)

    return result


url = "https://www.pref.ishikawa.lg.jp/kansen/coronakennai.html"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
}

r = requests.get(url, headers=headers)

r.raise_for_status()

soup = BeautifulSoup(r.content, "html.parser")


# スクレイピング
contents = soup.find("div", id="tmp_contents")

result = []

# 感染者番号を検索
for tag in contents.find_all("h3"):

    data = {}

    # 日付取得のため上に検索
    h2 = tag.find_previous_sibling("h2")
    data["日付"] = wareki2date(h2.get_text(strip=True))

    h3 = tag.get_text(strip=True)
    data["番号"] = int(re.match(r"感染者(\d+)", h3).group(1))

    tmp = []

   # 患者情報がpかdiv以外は終了
    for i in tag.find_next_siblings():
        if i.name in ["p", "div"]:
            tmp.append(
                jaconv.z2h(i.get_text(strip=True), kana=False, digit=True, ascii=True)
            )
        else:
            break

    p = "\n".join(tmp)

    m = re.search(r"\(1\)年代:?(.+)\(2\)性別(.+)\(3\)居住地(.+)\(4\)(.+)", p, re.DOTALL)

    s = [j.strip() for j in m.groups() if j]

    data["年代"] = s[0]
    data["性別"] = s[1]
    data["居住地"] = s[2]
    data["内容"] = s[3]

    result.append(data)

# データラングリング
df = pd.DataFrame(result)

# 内容を「症状・経過」と「行動歴」に分割
df_tmp = df["内容"].str.split(r"\(5\)", expand=True)
df_tmp.rename(columns={0: "症状・経過", 1: "行動歴"}, inplace=True)

# 結合
df_kanja = pd.concat([df, df_tmp], axis=1)

df_kanja.drop(columns="内容", inplace=True)

df_kanja.set_index("番号", inplace=True)

df_kanja.sort_index(inplace=True)

# 感染者1のみ「症状・経過」と「行動歴」を交換
df_kanja.at[1, "症状・経過"], df_kanja.at[1, "行動歴"] = df_kanja.at[1, "行動歴"], df_kanja.at[1, "症状・経過"]

df_kanja

兵庫県の患者情報のPDFをCSV変換

!wget "https://web.pref.hyogo.lg.jp/kk03/documents/corona-kanjajokyo0403.pdf" -O kanja.pdf

!apt install python3-tk ghostscript
!pip install camelot-py[cv]
import camelot

import pandas as pd

tables = camelot.read_pdf("kanja.pdf", pages="all", split_text=True, strip_text="\n", line_scale=40)

# ページごとのデータを結合
df_head = pd.concat([table.df for table in tables])

# 上2行を結合して列名に設定
df_head.columns = ["".join(i).strip() for i in df_head.head(2).fillna("").T.values]

# 上3行目から最後までをコピー
df_tmp = df_head.iloc[2:, :].copy()

# 番号が数字のみ抽出
df = df_tmp[df_tmp["番号"].astype(str).str.isdigit()].copy()

# CSVに保存
df.to_csv("kanja.csv", index=False)