如何在 Google Apps 脚本中使用 Cheerio 提取 HTML table 数据 (Yahoo Finance)?

How to pull HTML table data (Yahoo Finance) with Cheerio in Google Apps Script?

我正在尝试从 https://finance.yahoo.com/quote/CL%3DF/history?p=CL%3DF 获取整个 table 数据。在浏览器上,该网页默认显示截至 2020 年 10 月 12 日的 1 年数据。但是由于某些原因,下面的代码没有提取整个 table 数据。它只提取了部分数据,不到 5 个月的数据,直到 2021 年 5 月 20 日。我错过了什么?任何人都可以帮助修复代码中的任何错误吗?谢谢!

function test() {
  const url = 'https://finance.yahoo.com/quote/CL%3DF/history?p=CL%3DF';
  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true }).getContentText();
  const $ = Cheerio.load(res);
  // The URL webpage shows one year data down to Oct 12, 2021 on the browser.
  // But the code below got data only down to May 20, 2020.  Why am I mssing?
  var data = $('table').find('td').toArray().map(x => $(x).text());
  console.log(data[data.length-8]);     // Print the last row date other than the web note
}

当我看到HTML数据时,似乎table标签没有所有数据。但幸运的是,我注意到 Javascript 中的对象具有您期望的所有数据。那么下面的修改脚本怎么样?

修改后的脚本:

在这个修改后的脚本中,使用了Spreadsheet的容器绑定脚本。当然,您可以使用独立类型。但那样的话,请修改SpreadsheetApp.getActiveSpreadsheet().

当您使用此脚本时,请将以下修改后的脚本复制并粘贴到Spreadsheet的脚本编辑器中,并设置sheet名称和运行。通过这种方式,所有数据都被检索并放入 Spreadsheet.

function test() {
  const url = 'https://finance.yahoo.com/quote/CL%3DF/history?p=CL%3DF';
  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true }).getContentText();
  const $ = Cheerio.load(res);

  // I modified below script
  const data = $('script').toArray().reduce((ar, x) => {
    const c = $(x).get()[0].children;
    if (c.length > 0) {
      const d = c[0].data.trim().match(/({"context"[\s\S\w]+);\n}\(this\)\);/);
      if (d && d.length == 2) {
        ar.push(JSON.parse(d[1]));
      }
    }
    return ar;
  }, []);
  if (data.length == 0) throw new Error("No data.");
  const header = ["date","open","high","low","close","adjclose","volume"];
  const ar = data[0].context.dispatcher.stores.HistoricalPriceStore.prices.map(o => header.map(h => h == "date" ? new Date(o[h] * 1000) : (o[h] || "")));
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // <--- Please set the sheet name you want to put the values.
  sheet.getRange(1, 1, ar.length, ar[0].length).setValues(ar);
}

结果:

当上述脚本为运行时,得到如下结果

参考文献: