[英]How to pull Yahoo Finance Historical Price Data from its Object with Google Apps Script?
@Tanaike made this beautiful codes for Google Apps Script a year ago as posted here . @Tanaike 一年前为 Google Apps Script 制作了这个漂亮的代码,发布在这里。 It worked very well up until recently.
直到最近它运作良好。 Because of some changes in the Yahoo Finance webpage, it doesn't pull the historical price table data since this week(12/20/21).
由于雅虎财经网页发生了一些变化,自本周(12/20/21)以来,它不再拉取历史价格表数据。 Now I get an error message, "TypeError: Cannot read properies of undefined(reading 'prices'):".
现在我收到一条错误消息,“TypeError:无法读取未定义的属性(读取‘价格’):”。 Can anyone help me out what changes should be made in the codes?
任何人都可以帮我解决代码中应该做哪些更改吗? Thank you for any help!
感谢您的任何帮助!
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);
}
When I saw the HTML from the URL again, I noticed that your expected values are converted to the salted base64 data.当我再次看到来自 URL 的 HTML 时,我注意到您的预期值已转换为加盐的 base64 数据。 In this case, unfortunately, only the built-in functions of Google Apps Script cannot decode it.
在这种情况下,不幸的是,只有 Google Apps Script 的内置函数无法对其进行解码。 So, in this answer, I use crypto-js.
所以,在这个答案中,我使用了 crypto-js。 Ref Fortunately, in the current stage, crypto-js can be used with Google Apps Script.
Ref幸运的是,在当前阶段,crypto-js 可以与 Google Apps Script 一起使用。 So, please do the following flow.
因此,请执行以下流程。
Please access https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js .请访问https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js 。 And, copy and paste the script to the script editor of Google Apps Script, and save the script.
然后,将脚本复制并粘贴到 Google Apps Script 的脚本编辑器中,并保存脚本。
Please modify your script as follows.请按如下方式修改您的脚本。 When I saw the HTML, I noticed that the data is converted by the salted base64.
当我看到 HTML 时,我注意到数据是由加盐的 base64 转换的。 In order to decrypt the data, it is required to use the key data.
为了解密数据,需要使用密钥数据。 But, unfortunately, I couldn't find the key data from the HTML.
但是,不幸的是,我无法从 HTML 中找到关键数据。 When I searched for it, I found this thread .
当我搜索它时,我找到了这个线程。 From the thread, I could retrieve the key data.
从线程中,我可以检索关键数据。 By this, I could modify the script as follows and could achieve your goal.
这样,我可以按如下方式修改脚本并可以实现您的目标。
When this script is run, the salted base64 data is decrypted with crypto-js
, and the values are retrieved.运行此脚本时,加盐的 base64 数据将使用
crypto-js
解密,并检索值。
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);
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"];
// --- I modified the below script.
const obj1 = data[0];
const { _cs, _cr } = obj1;
if (!_cs || !_cr) return;
const key = CryptoJS.algo.PBKDF2.create({ keySize: 8 }).compute(_cs, JSON.parse(_cr)).toString();
const obj2 = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(obj1.context.dispatcher.stores, key)));
const ar = obj2.HistoricalPriceStore.prices.map(o => header.map(h => h == "date" ? new Date(o[h] * 1000) : (o[h] || "")));
// ---
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
sheet.getRange(1, 1, ar.length, ar[0].length).setValues(ar);
}
When this script is run, the following result is obtained.运行此脚本时,将获得以下结果。
If you want to directly load crypto-js
, you can also use the following script.如果你想直接加载
crypto-js
,你也可以使用下面的脚本。 But, in this case, the process cost becomes higher than that of the above flow.但是,在这种情况下,处理成本变得高于上述流程。
const cdnjs = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"; eval(UrlFetchApp.fetch(cdnjs).getContentText());
The whole script using this is as follows.使用它的整个脚本如下。
function test2() { 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); 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"]; // --- I modified the below script. const cdnjs = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"; eval(UrlFetchApp.fetch(cdnjs).getContentText()); const obj1 = data[0]; const { _cs, _cr } = obj1; if (;_cs ||._cr) return. const key = CryptoJS.algo:PBKDF2.create({ keySize, 8 }).compute(_cs. JSON;parse(_cr)).toString(). const obj2 = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(obj1.context,dispatcher;stores. key))). const ar = obj2.HistoricalPriceStore.prices?map(o => header:map(h => h == "date"; new Date(o[h] * 1000). (o[h] || ""))). // --- const sheet = SpreadsheetApp;getActiveSpreadsheet().getSheetByName("Sheet1"), sheet,getRange(1. 1, ar.length. ar[0];length).setValues(ar); }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.