こちらのコロナ情報から、それぞれの市（横浜市、山北町等）のコロナ感染者の累計を取得（スクレイピング）したいです。  
<https://www.pref.kanagawa.jp/docs/ga4/covid19/occurrence.html>

【環境】  
GoogleAppsScript  
【利用ライブラリ】  
Cheerio

GASでCheerioライブラリを使い、jQueryでの要素指定をして、データを取得するスクレイピングをしたいです。  
サイトのtableタグ等に、class名、id等が振られていないため、要素数の番号で指定する方法で取得しております。

現状のコードは以下のとおりです。

```

      //スクレイピングスタートするURL
      const url = "https://www.pref.kanagawa.jp/docs/ga4/covid19/occurrence.html";
     
      // UrlFetchAppにて取得（UTF-8を指定）
      const html = UrlFetchApp.fetch(url).getContentText('UTF-8');
    
      // 返り値をCheerioに食わせる
      let $ = Cheerio.load(html);
    
      // 指定したいテーブルにid:area01を追加
      $('table').eq(3).attr('id', 'area01')
    
      const hiratsuka_data = $('#area01 tr:nth-child(6) td:nth-child(5)').text();
    
```

テーブル番号で指定したいテーブルにidを付与し、それをもとに、  
nth-childの番号で取得する・・・という、なんとも強引なやり方になっております。

jQueryの知識が乏しいことでこれ以上のコードが書けないのですが、  
もっとスマートに要素を指定する方法はありませんでしょうか？？

もしわかる方いましたら、ご教示いただけますと助かります。  
よろしくお願い致します。
CC BY-SA 4.0
2021-10-28T00:42:05.977
83288
2021-10-28T03:18:45.657
2021-10-28T02:55:17.940
19460
48836
question
javascript google-apps-script
GAS(jQuery使用)のスクレイピングで、class,idの振られていない複数のtableタグから、要素を取得したいです。
指定されたURLの表を見ますと、マージされた列があることと、それぞれの行で列数が異なることから、HTMLデータをパースすること自体がすこし複雑そうに思いました。そこで、Cheerioを使用しない方法でHTMLデータをパースしつつ、データを取得する方法について提案させていただきたいと思います。この場合、Sheets
APIを使ってHTML tableをパースします。Sheets APIのHTML
tableパースは非常に強力と思いますので、これを提案させていただきました。

スクリプトのフローは次の通りです。

  1. HTMLデータの取得
  2. HTMLデータからそれぞれのTableを取得
  3. テンポラルファイルとしてスプレッドシートを作成
  4. Sheets APIを使ってHTML tableをパースしてスプレッドシートの各セルへ挿入
  5. スプレッドシートからデータを取得し、JSONオブジェクトへ変換
  6. テンポラルファイルの削除
  7. 結果の表示

このフローをスクリプトに反映させると、次のようになります。

### サンプルスクリプト

このスクリプトは、HTML tableをパースするためにSheets APIを使用しますので、Advanced Google servicesでSheets
APIを有効にしてください。 [Ref](https://developers.google.com/apps-
script/guides/services/advanced#enable_advanced_services)

```

    function myFunction() {
      const url = "https://www.pref.kanagawa.jp/docs/ga4/covid19/occurrence.html";
    
      // 1. HTMLデータの取得
      const html = UrlFetchApp.fetch(url).getContentText();
    
      // 2. HTMLデータからそれぞれのTableを取得
      const tables = [...html.matchAll(/<table[\s\S\w]+?<\/table>/g)];
    
      // 3. テンポラルファイルとしてスプレッドシートを作成
      const ss = SpreadsheetApp.create("temp");
      const sheet = ss.getSheets()[0];
      const spreadsheetId = ss.getId();
      const sheetId = sheet.getSheetId();
    
      // 4. Sheets APIを使ってHTML tableをパースしてスプレッドシートの各セルへ挿入
      Sheets.Spreadsheets.batchUpdate({
        requests: [
          { pasteData: { data: tables[3][0], html: true, coordinate: { sheetId: sheetId } } },
          { deleteDimension: { range: { sheetId: sheetId, startIndex: 0, endIndex: 4, dimension: "ROWS" } } },
          { deleteDimension: { range: { sheetId: sheetId, startIndex: 0, endIndex: 1, dimension: "COLUMNS" } } }
        ]
      }, spreadsheetId);
    
      // 5. スプレッドシートからデータを取得し、JSONオブジェクトへ変換
      const values = sheet.getDataRange().getValues();
      const res = values[0].map((_, c) => values.map(r => r[c])).reduce((o, r) => {
        while (r.length > 0) {
          const [a, b] = r.splice(0, 2)
          if (a) o[a] = b || 0;
        }
        return o;
      }, {});
    
      // 6. テンポラルファイルの削除
      DriveApp.getFileById(spreadsheetId).setTrashed(true);
    
      // 7. 結果の表示
      console.log(res)
    }
    
```

### 結果

このスクリプトを実行すると、Logへ次のような結果を確認できます。

```

    {
       "横浜市":903,
       "藤沢市":540,
       "厚木市":4003,
       "綾瀬市":1517,
       "大井町":202,
       "湯河原町":206,
       "川崎市":41,
       "小田原市":2177,
       "大和市":4381,
       "葉山町":360,
       "松田町":80,
       "愛川町":778,
       "相模原市":418,
       "茅ヶ崎市":81,
       "伊勢原市":1082,
       "寒川町":65,
       "山北町":57,
       "清川村":10,
       "横須賀市":74,
       "逗子市":686,
       "海老名市":1983,
       "大磯町":288,
       "開成町":220,
       "その他":177,
       "平塚市":3126,
       "三浦市":424,
       "座間市":1983,
       "二宮町":216,
       "箱根町":79,
       "鎌倉市":2051,
       "秦野市":1588,
       "南足柄市":475,
       "中井町":137,
       "真鶴町":47
    }
    
```

この場合、横浜市の値を見たい場合は、`console.log(res["横浜市"])`により`903`を得ることができます。

### 参考

  * [Method: spreadsheets.batchUpdate](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate)
  * [PasteDataRequest](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#pastedatarequest)
CC BY-SA 4.0
2021-10-28T02:23:28.203
83293
2021-10-28T02:23:28.203
19460
83288
answer