京都府の発生状況のPDFをスクレイピング

!apt install ghostscript
!pip install camelot-py[cv]
import re
from urllib.parse import urljoin
import datetime

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

# 月日をdatetimeに変換
def days2date(s):

    y = dt_now.year

    days = re.findall("[0-9]{1,2}", s)

    if len(days) == 2:
        m, d = map(int, days)

        if dt_now.month < m:

            y -= 1

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

# 和暦をdatetimeに変換
def era2date(s):

    days = re.findall("[0-9]{1,2}", s)

    if len(days) == 3:
        y, m, d = map(int, days)

        y += 2018

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

url = "https://www.pref.kyoto.jp/kentai/corona/hassei1-50.html"

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

# 現在の日付
JST = datetime.timezone(datetime.timedelta(hours=+9), "JST")
dt_now = datetime.datetime.now(JST)

サイトの一覧表を取得

tmp = pd.read_html(url)

# 入院・療養中
df1 = tmp[0].rename(columns={"Unnamed: 0": "府番号"})
df1["状態"] = "入院"

# 退院等(死亡退院・転院を含む)
df2 = tmp[1].rename(columns={"Unnamed: 0": "府番号"})
df2["状態"] = "退院"

# 入院・療養中と退院等(死亡退院・転院を含む)を結合
df_html = pd.concat([df1, df2])

# 府番号から例目を除去
df_html["府番号"] = df_html["府番号"].str.replace("例目", "").astype(int)

# 府番号をindexにセット
df_html.set_index("府番号", inplace=True)

# ソート
df_html.sort_index(inplace=True)

府番号と京都府京都市別のPDFのURLを抽出

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

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

pref_links = set()
city_links = set()

data = []

for i in soup.select("table.datatable > tbody > tr > td:nth-of-type(6) > a"):

    id = int(i.find_parent("tr").td.get_text(strip=True).replace("例目", ""))

    s = i.get_text(strip=True)

    pdf_link = urljoin(url, i.get("href"))

    data.append({"id":id, "url": pdf_link})

    if re.match("府報道発表資料", s):

        pref_links.add(pdf_link)

    elif re.match("京都市報道発表資料", s):

        city_links.add(pdf_link)

df_id = pd.DataFrame(data).sort_values(by="id").reset_index(drop=True)

# URLを基準に連番を付与
df_id["index"] = df_id.groupby("url").cumcount()

df_id

京都府のPDFから表を抽出

dfs_pref = []

# 直近30件
for link in sorted(pref_links, reverse=True)[:30]:

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

    for table in tables:

        r, c = table.shape

        if (r > 1) and (c == 7):

            tmp = pd.DataFrame(table.data[1:], columns=table.data[0])

            tmp["url"] = link

            dfs_pref.append(tmp)

# PDFを結合
df_pref = pd.concat(dfs_pref).reset_index()

# 府番号と在住地域等に分割
pref_number = df_pref["府番号在住地域等"].str.extract("(\d+)(.+)").rename(columns={0: "府番号", 1:"在住地域等"})

# 発症日と症状に分割
pref_onset = df_pref["発症日症状"].str.normalize("NFKC").str.extract("(\d{1,2}月\d{1,2}日)?(.+)").rename(columns={0: "発症日", 1:"症状"})

# 検査日を抽出
pref_inspection = df_pref["現在判明している概要"].str.normalize("NFKC").str.extract("・検査日:(\d{1,2}月\d{1,2}日)(.+)?").rename(columns={0: "検査日", 1:"概要"})

# 上記を結合
df_pref = df_pref.join([pref_number, pref_onset, pref_inspection])

# 不要の列を削除
df_pref.drop(["府番号在住地域等", "発症日症状", "現在判明している概要"], axis=1, inplace=True)

# 月日からdatetimeに変換
df_pref["発症日"] = df_pref["発症日"].fillna("").apply(days2date)
df_pref["検査日"] = df_pref["検査日"].fillna("").apply(days2date)

# 府番号を整数に変換
df_pref["府番号"] = df_pref["府番号"].astype(int)

# 府番号をindexにセット
df_pref.set_index("府番号", inplace=True)

# サイトの表と結合
df_pref = df_pref.join(df_html, rsuffix="_HTML")

# CSVに出力
df_pref.to_csv("kyoto_pref.csv", encoding="utf_8_sig")

京都市のPDFから表を抽出

dfs_city = []

# 直近30件
for link in sorted(city_links, reverse=True)[:30]:

    tables = camelot.read_pdf(link, pages="2-end", split_text=True, strip_text=" \n")

    for table in tables:

        r, c = table.shape

        if (r > 2) and (c == 8):

            tmp = pd.DataFrame(
                table.data[2:],
                columns=[
                    "番号",
                    "年代",
                    "性別",
                    "居住地",
                    "発症日",
                    "症状",
                    "現在の症状程度",
                    "その他特記事項",
                ],
            )

            tmp["url"] = link

            dfs_city.append(tmp)

# PDFを結合
df_city = pd.concat(dfs_city).reset_index()

# 和暦からdatetimeに変換
df_city["発症日"] = df_city["発症日"].apply(era2date)

# 市番号を基準に府番号を結合
df_city = pd.merge(df_city, df_id)

# 不要の列を削除
df_city.drop("index", axis=1, inplace=True)

# 府番号をindexにセット
df_city.set_index("id", inplace=True)

# サイトの表と結合
df_city = df_city.join(df_html, rsuffix="_HTML")

# CSVに出力
df_city.to_csv("kyoto_city.csv", encoding="utf_8_sig")