I wrote a function to retrieve Yahoo! Finance data for certain stocks. The sheet was, however, generating too many URL calls to Yahoo! and I was regularly restricted for the rest of the day.
Since the data that I am retrieving is not expected to change often anyway, the solution seems to be using Google Sheets cache to store any values that are retrieved for 7 days before querying live again.
The code is as follows, and a link to the spreadsheet I'm using is below:
function yahoofinance(ticker)
{
var url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';
var cache = CacheService.getDocumentCache();
var cached = cache.get(url);
if (cached != null)
{
var object = cached;
var source = 'cache';
}
else
{
var response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
var responseCode = response.getResponseCode();
if (responseCode === 200)
{
var object = JSON.parse(response.getContentText());
var source = 'live';
cache.put(url, "cached", 21600);
properties.setProperty(url, object);
}
}
var fwdPE = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
var sector = object.quoteSummary.result[0]?.assetProfile?.sector || '-';
var mktCap = object.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';
return [[source, fwdPE, sector, mktCap]];
}
Link to the Google Sheet
The problem is that any function call gets the same error:
Error
TypeError: Cannot read property 'result' of undefined (line 28).
Line 28 is:
var fwdPE = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
As far as I understand, object (and thus result[]) should be available.
What am I doing wrong. Why is this error generated?
Am Implementing Google cache correctly? What do I need to improve?
Any help is greatly appreciated!
Try it this way
function yahoofinance(ticker) {
const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';
const cache = CacheService.getDocumentCache();
const cached = cache.get("response");
let object = {};
if (cached) {
object.response = cached;
object.source = 'cache';
} else {
let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
if (response.getResponseCode() == 200) {
object.response = JSON.parse(response.getContentText());
object.source = 'live';
cache.put("response", cached, 21600);
}
}
let fwdPE = object.response.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
let sector = object.response.quoteSummary.result[0]?.assetProfile?.sector || '-';
let mktCap = object.response.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';
return [[object.source, fwdPE, sector, mktCap]];
}
Related
I'm trying to grab URL params from a page link to prepopulate a form in an iFrame, but am struggling with the params returning 'null' and need some guidance. So far, the script appears to work by populating the form with "null", however, it is unsuccessfully populating params that have valid values in my URL. The javascript below 'f.src' is scripting provided by my forms service.
(I apologize for the ugly console.logs, but am using those for troubleshooting.)
[Console Preview][1]
***UPDATE: With my updated code, per user suggestion, I updated my .get statements to specify the param with a string, but it's still returning 'null'.
try{
var endpoint = "https://forms.myformsite.com/";
console.log(endpoint);
var url_string = "https://my.site.com/landingpage?fname=Jeff&lname=Bezos&email=jeff#amazon.com&company=Amazon&title=Founder"; /*window.location.href;*/
console.log(url_string);
var url = new URL(url_string);
console.log(url_string);
var fname = url.searchParams.get('fname');
console.log(fname);
var lname = url.searchParams.get('lname');
console.log(lname);
var email = url.searchParams.get('email');
console.log(email);
var company = url.searchParams.get('company');
console.log(company);
var title = url.searchParams.get('title');
console.log(title);
var formURL = endpoint+"&fname="+fname+"&lname="+lname+"&email="+email+"&company"+company+"&title="+title;
console.log(formURL);
var f = document.createElement("iframe");
f.src = formURL;
console.log(f.src);
f.style.border = "none";
f.style.height = "878px";
f.style.width = "90%";
f.style.transition = "all 0.5s ease";
var d = document.getElementById("divFormID");
d.appendChild(f);
window.addEventListener('message', function() {
var evntData = event.data;
if (evntData && evntData.constructor == String) {
var zf_ifrm_data = evntData.split("|");
if (zf_ifrm_data.length == 2) {
var zf_perma = zf_ifrm_data[0];
var zf_ifrm_ht_nw = (parseInt(zf_ifrm_data[2], 10) + 15) + "px";
var iframe = document.getElementById("divFormID").getElementsByTagName("iframe")[0];
if ((iframe.src).indexOf('formperma') > 0 && (iframe.src).indexOf(zf_perma) > 0) {
var prevIframeHeight = iframe.style.height;
if (prevIframeHeight != zf_ifrm_ht_nw) {
iframe.style.height = zf_ifrm_ht_nw;
}
}
}
}
}, false);
} catch (e) {}
})();```
[1]: https://i.stack.imgur.com/z75q0.png
[2]: https://i.stack.imgur.com/bjqoP.png
For all .get() calls, you're passing in an (undefined) variable instead of a string:
var fname = url.searchParams.get(fname);
This should be:
var fname = url.searchParams.get('fname');
I'm calling an API and getting data going through its pagination. When I get to the last page, though, the obejct giving me the last page is empty and it's throwing the following error: TypeError: Cannot convert undefined or null to object Besides, I don't any data from that last page.
Here's the pagination information I get:
{"count":100,"total":545,"_links":
{
"self":{
"href":"\/candidates?page=0&per_page=100"
},
"next":{
"href":"\/candidates?per_page=100"
},
"last":{
"href":"\/candidates?page=6&per_page=100"
}
},
Here's the code I'm using to get the data:
function allcandidates() {
const url = "https://api.catsone.com/v3/candidates";
const params = {
'muteHttpExceptions': true,
'method': 'GET',
'redirect': 'follow',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Token ' + API_KEY
}
};
let pageNum = 1;
let lastPgNo;
let data = {}, output = [];
do {
let currentUrl = url + '?' + 'per_page=100' + '&' + 'page=' + pageNum;
//One of their URL parameter is "per_page", which is 25 result/page and it go up to 100. I'm not sure if the fact that the last page doesn't have all 100 results may result in an error, too.
const response = UrlFetchApp.fetch(currentUrl, params);
const respCode = response.getResponseCode();
if (respCode != 200) {
Browser.msgBox('The server seems to be temporarily down. Please try again later.');
return;
}
//Gets the last page number
const getText = response.getContentText();
const lastPageObj = JSON.parse(getText)['_links']['last'];
const lastPgVal = Object.values(lastPageObj); //This is where the error occurs
const lastPgText = lastPgVal.toString();
lastPgNo = Number(lastPgText.split('?page=').pop().split('&')[0])
//Gets current page
const currentPageObj = JSON.parse(getText)['_links']['self'];
const currentPgVal = Object.values(currentPageObj);
const nextPgText = currentPgVal.toString();
var currentPgNo = Number(nextPgText.split('?page=').pop().split('&')[0])
const dataSet = JSON.parse(getText)['_embedded']['candidates'];
for (let i = 0; i < dataSet.length; i++) {
data = dataSet[i];
output.push([data.id]);
}
pageNum = pageNum + 1;
} while (pageNum <= lastPgNo);
}
You might use an if statement and continue. I.E. replace
const lastPgVal = Object.values(lastPageObj);
by
if(lastPageObj){
const lastPgVal = Object.values(lastPageObj);
} else {
continue;
}
Another option is to use try...catch
Resources
https://developer.mozilla.org/en-US/docs/Glossary/Truthy
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
I'm using GAS to send and receive texts. There is one function that send texts (sendTexts.gs) and one that receives (receiveTexts.gs). I have both of these functions linked to individual buttons on the sheet, but when I run one function, both are running (texts get sent every time). Is there a cache or something that needs to be cleared? The receiveTexts has no commands in the code that could send messages in it, and based on logger testing, I know that both are running when I only click one.
EDIT: This also occurs in the GAS "terminal". If I click run in the script editor both run.
Here is the code with personal/individual info (codes/phone numbers edited out):
function sendSms(to, body) {
var playerArray = getMeta();
Logger.log(playerArray);
var messages_url = "https://api.twilio.com/2010-04-01/Accounts/EDIT/Messages.json";
var payload = {
"To": to,
"Body" : body,
"From" : "EDIT"
};
var options = {
"method" : "post",
"payload" : payload
};
options.headers = {
"Authorization" : "Basic " + Utilities.base64Encode("EDIT")
};
UrlFetchApp.fetch(messages_url, options);
}
function sendAll() {
var spreadsheet = SpreadsheetApp.getActive();
var text = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName('Meta')).getRange('B4').getValue();
var playerArray = getMeta();
Logger.log(text);
for (i=0; i<playerArray.length;i++) {
try {
var number = playerArray[i][1];
Logger.log(number);
response_data = sendSms(number, text);
status = "sent";
} catch(err) {
Logger.log(err);
status = "error";
}
Logger.log(status);
}
}
function sendTexts() {
sendAll();
}
Logger.log("ran send texts");
Here is the receive texts code with the same adjustments:
function receiveTexts() {
var spreadsheet = SpreadsheetApp.getActive();
var ACCOUNT_SID = "EDIT";
var ACCOUNT_TOKEN = "EDIT";
var toPhoneNumber = "+EDIT";
var sheet = spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Meta'), true);
var playerArray = getMeta();
var numberToRetrieve = playerArray.length;
var hoursOffset = 0;
var options = {
"method" : "get"
};
options.headers = {
"Authorization" : "Basic " + Utilities.base64Encode(ACCOUNT_SID + ":" + ACCOUNT_TOKEN)
};
var url="https://api.twilio.com/2010-04-01/Accounts/" + ACCOUNT_SID + "/Messages.json?To=" + toPhoneNumber + "&PageSize=" + numberToRetrieve;
var response = UrlFetchApp.fetch(url,options);
// -------------------------------------------
// Parse the JSON data and put it into the spreadsheet's active page.
// Documentation: https://www.twilio.com/docs/api/rest/response
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Meta'), true);
var numRounds = spreadsheet.getRange('B2').getValue();
var theSheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName('Attacks'),true);
var theColumn = (numRounds*2)+1;
var dataAll = JSON.parse(response.getContentText());
for (i=0; i<dataAll.messages.length; i++){
var sentNumber = dataAll.messages[i].from;
Logger.log(sentNumber);
for (k=0; k<playerArray.length;k++){
Logger.log(playerArray[k][1]);
if (playerArray[k][1]==sentNumber){
var player = k;
Logger.log('Success');
Logger.log(player);
break;
}
}
var playerRow = playerArray[player][0];
Logger.log(playerRow);
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Attacks'),true);
theSheet.getRange(playerRow, theColumn).setValue(dataAll.messages[i].body);
}
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Meta'), true);
sheet.getRange(2,2).setValue(numRounds +1);
}
Logger.log("texts received ran");
EDIT2: I separated out getMeta into a separate file. Now when I run getMeta, it also runs the other two scripts. Anytime any one script is run, all three run. This makes me think it's not related to the code, but related to some setting or something. Does the order of the scripts in the sidebar matter? I feel like that's not it because running any of the three causes all three to be run.
I've some issues when I am trying to fetch the data and render in ReactFullpage Component.
The error says: TypeError: Cannot read property '0' of null
Since in the attached script he will get the data from the row here is an example for the var url = https://www.amazon.de/dp/B07YD776RP?ref=myi_title_dp
// script for scraping amazon data outgoing from an url
function import_amazon_data() {
//go to Google Sheet & get new income form column 4
var scraperSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("product_seller_data")
var lrow = scraperSheet.getLastRow();
for(var i=2;i<=lrow;i++){
var url = scraperSheet.getRange(i, 4).getValue()
var regExprice = /<span id="priceblock_ourprice.*<\/span>/gi
var regExsellername = /id=['"]sellerProfileTriggerId['"]>.*?<\/a>/gi
var regEximgsrc = /data-old-hires=".*?"/gi
var getContent = UrlFetchApp.fetch(url).getContentText().trim();
//match of HTML elements with fetched URL
var price = getContent.match(regExprice)
price = price[0]
price = price.replace('<span id="priceblock_ourprice" class="a-size-medium a-color-price priceBlockBuyingPriceString">',"")
.replace('</span>',"")
scraperSheet.getRange(i, 6).setValue(price)
var sellername = getContent.match(regExsellername)
sellername = sellername[0]
sellername = sellername.replace("id='sellerProfileTriggerId'>","")
.replace('id="sellerProfileTriggerId">',"")
.replace('<\/a>',"")
scraperSheet.getRange(i, 7).setValue(sellername)
var imgsrc = getContent.match(regEximgsrc)
imgsrc = imgsrc[0]
imgsrc = imgsrc.replace('data-old-hires="',"")
.replace('"',"")
scraperSheet.getRange(i, 5).setValue(imgsrc)
}
}
Place a check before accessing these arrays: price[0], sellername[0] and imgsrc[0].
To check in development you may use
if(Array.isArray(price) && price.length > 0) {
price = price[0];
} else {
alert('Price array is empty')
}
I am making a website using YouTube API and i need to get users channel id to retrieve there data
How can i do this in JavaScript
Thanks in advance 😊
this code is working already in my project.
can you try this code,
Maybe solve your problem with this code.
youtubeChannelId = channelName => {
if (channelName) {
var name = getChannelFromUrl(channelName);
var url =
'https://gdata.youtube.com/feeds/api/users/' +
name +
'?fields=id&alt=json';
var result = UrlFetchApp.fetch(url);
var data = Utilities.jsonParse(result.getContentText());
if (
typeof data['entry'] !== 'undefined' &&
data['entry']['id']['$t'] !== 'undefined'
) {
var id = 'UC' + data['entry']['id']['$t'].split('/').pop();
return id;
}
}
return '';
};
getChannelFromUrl = url => {
var pattern = new RegExp(
'^(https?://)?(www.)?youtube.com/(user/)?([a-z-_0-9]+)/?([?#]?.*)',
'i',
);
var matches = url.match(pattern);
if (matches) {
return matches[4];
}
return url;
};