GASで定期的にWebサイト上のテーブルのデータをkintoneに追加する

毎日のように息子からコロナ感染者数を聞かれるので、
ここ2週間ほど毎日市のホームページのコロナ感染者のところをコピペしてExcelに貼り付け加工して、
kintoneに読み込ませる・・・という作業をしていました。

ただただ面倒だったので、1日1回、GASでスクレイピングしてkintoneに登録するという仕組みを作ってみました。

⚠⚠ホームページで発表された内容が変わってしまったら使えなくなっちゃいます。

# アプリの準備

最初にExcelでアプリを作成したのでドロップダウンが何個かあります。
ほぼホームページにある通りのフィールド名にしてます。
フィールドコード＝フィールド名にしています。

| フィールド種類 | フィールドコード | 備考 |
|:-:|:-:|:-:|
|  数値| 症例番号  |  重複禁止 |
|  日付 | 発症日  |  発症した日。日付以外が入っていたら発表日を入れる。|
|  数値 | クラスター  | 今回、GASからの登録は想定してない👀 |
|  日付 | 発表日  | 発表があった日  |
|  ドロップダウン（文字列のほうがいいかも） | 年代  | 10代未満～90代 |
| ラジオボタン  | 性別等  | 男性・女性  |
| ドロップダウン（文字列のほうがいいかも） | 重症度  | 軽症・無症状・中等症・・・・など  （文字列のほうがいいかも？）|
|  文字列(1行) |  職業 |   |
|  文字列(1行) | 居住地  |   |
|  数値 |  濃厚接触者 | 市内の濃厚接触者の症例番号。特記事項を書き換えて使っている  |
|  文字列(1行) |  備考 | 発症日＝発表日のときは「発表日」とする  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/d9c86e64-6902-bed9-fde4-d4247e202384.png)

お好みで関連レコードを追加してもいいかも？
クラスターのアプリは想像してつくってね👀
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/519351cd-bcec-d669-837b-0f38b2abcc4e.png)

## グラフの例
**👀発症日グラフ**
発表日ではなくて発症日で感染者数を見たい場合はこんな感じ。
備考が「発表日」のレコードを除いてグラフにする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/b8ff6a11-f67e-cb9a-25ca-f473bd2aae74.png)

**👀年代別円グラフ**
年代別に見たいときはこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/87903231-d55d-57f9-723d-268886167677.png)

他にも工夫してグラフをつくってみてね👀✨✨

# Google Apps Script (GAS)

ライブラリに下記を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/c8475432-ff8a-340f-a931-466cf45fb421.png)

**Parser**
公式のサイトがわからないですが、ご存知のかたいらっしゃったら教えて下さい👀💦
`1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw`

**KintoneManager**
[kintone とGoogle Apps Script連携](https://qiita.com/Arahabica/items/063877b0da439020d2c2)で紹介されている素敵ライブラリです。
`1M8DHQSWWGOM2RSRjDZmylHMl03nZHnliaVMGMk84m6jQ05ditPBTCSM8`

## コードを書く

```js
function myFunction() {

  // 久留米市の感染者9月のページ
  const url =
    "https://www.city.kurume.fukuoka.jp/1070kenkou/2040hokeneisei/3005cov2019/4309patient/index.html";
  const html = UrlFetchApp.fetch(url).getContentText("UTF-8");
  const urlList = Parser.data(html).from('<a href="2021-').to('">').iterate();

  // 感染者ページのリンクの一番上のやつが最新
  const todayUrl =
    "https://www.city.kurume.fukuoka.jp/1070kenkou/2040hokeneisei/3005cov2019/4309patient/2021-" +
    urlList[0];
  const todayHtml = UrlFetchApp.fetch(todayUrl).getContentText("UTF-8");
  const todayDataHTML = Parser.data(todayHtml)
    .from("<tr>")
    .to("</tr>")
    .iterate();
  // 発表日は今日
  const today = new Date();
  const todayStr =
    today.getFullYear() +
    "-" +
    ("00" + (today.getMonth() + 1)).slice(-2) +
    "-" +
    ("00" + today.getDate()).slice(-2);
  
  // 今日の感染者データの配列を作る。kintoneに登録できるようにhtmlを加工します。
  let todayData = [];
  todayDataHTML.forEach((v) => {
    // 苦し紛れにhtmlを整理する。もっといいやり方ありそう。
    v = v.replace(/\r?\n/g, "");
    v = v.replaceAll("<br>", "");
    v = v.replaceAll("th", "td");
    v = v.replaceAll(' scope="col"', "");
    v = v.replace("特記事項", "濃厚接触者");
    v = v.replace('</td><td style="text-align:left;">', "</td><td>");

    const todayDataRow = v.split("</td><td>");
    const b = todayDataRow.map((w, idx) => {
      w = w.replace("<td>", "");
      w = w.replace("</td>", "");

      // 発症日不明などの場合は発表日（今日の日付）を入れるようにする。
      if (idx === 5 && w != "発症日") {
        if (!w.match(/(\d+)月(\d+)日/)) {
          w = todayStr;
        } else {
          const matched = /(\d+)年(\d+)月(\d+)日/.exec(
            today.getFullYear() + "年" + w
          );
          w =
            matched[1] +
            "-" +
            ("00" + matched[2]).slice(-2) +
            "-" +
            ("00" + matched[3]).slice(-2);
        }
      }
      // 市内の濃厚接触者だけ症例番号残して他の文字は削除。
      if (idx === 7 && w != "濃厚接触者") {
        if (w.match(/^市.*/)) {
          w = w.replace(/[^0-9]/g, "");
        } else {
          w = "";
        }
      }
      return w;
    });
    todayData.push(b);
  });

  const newRecords = [];
  todayData.forEach((r, idx) => {
    if (idx === 0) {
      return;
    }
    const rec = {
      症例番号: { value: r[0] },
      年代: { value: r[1] },
      性別等: { value: r[2] },
      居住地: { value: r[3] },
      職業: { value: r[4] },
      発症日: { value: r[5] },
      重症度: { value: r[6] },
      濃厚接触者: { value: r[7] },
      発表日: { value: todayStr },
      備考: { value: r[5] === todayStr ? "発表日" : "" }, // 発症日が今日の日付のときは備考に発表日と書いておく
    };
    newRecords.push(rec);
  });

  // KintoneManagerを使う準備
  const subdomain = "【サブドメイン】";
  const apps = {
    YOUR_APP_NAME1: {
      appid: "【アプリID】",
      name: "久留米市コロナ感染者",
      token: "【APIトークン】",
    },
  };
  const kintone_manager = new KintoneManager.KintoneManager(subdomain, apps);
  // アプリにデータを登録
  const response = kintone_manager.create("YOUR_APP_NAME1", newRecords);
  const code = response.getResponseCode();
  console.log(code);
}


```

## トリガーの設定

久留米市のホームページの更新はだいたい17時台なので、18時～19時頃に動くように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/33bac5e9-2aee-5b22-5fd2-fc41443dfd83.png)


# まとめ
初めてスクレイピングというものにチャレンジしてみましたが、
スクレイピングとはこんな地道な作業なのか！？と思いました。
もっといいやり方があるのではないか？と疑っています。

スクレイピングしたいホームページに合わせてhtmlを加工する必要があると思いますので。
そこは工夫してやってみてね！