简体   繁体   English

Google Apps 脚本在后端工作,但不在工作表上

[英]Google Apps Script Working on backend but not on sheets

I am trying to create a script that pulls from the coin market cap API and displays the current price.我正在尝试创建一个从硬币市值 API 中提取并显示当前价格的脚本。 The script is working fine on the back end when I assign the variable a value.当我为变量赋值时,脚本在后端运行良好。 However, when I try to run the function on sheets the returned value is null.但是,当我尝试在工作表上运行 function 时,返回的值为 null。

function marketview(ticker) {
  var url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?CMC_PRO_API_KEY=XXX&symbol=" + ticker;
  var data = UrlFetchApp.fetch(url);
  const jsondata = JSON.parse(data);

  Logger.log(jsondata.data[ticker].quote['USD'].price)
  
}

My execution logs show that the scripts are running, but when when I use the function and try and quote ETH for example, the script is running for BTC.我的执行日志显示脚本正在运行,但是当我使用 function 并尝试引用 ETH 时,脚本正在为 BTC 运行。

When I do this on the backend and assign ETH the script works fine and returns the right quote.当我在后端执行此操作并分配 ETH 时,脚本可以正常工作并返回正确的报价。 Any ideas on what I'm missing?关于我所缺少的任何想法?

I did the same with coingecko API and add an issue having all my requests being rejected with quota exceeded error.我对 coingecko API 做了同样的事情,并添加了一个问题,即我的所有请求都因配额超出错误而被拒绝。

I understood that Google sheets servers IPs address were already spamming coingecko server.我知道 Google 表格服务器的 IP 地址已经在向 coingecko 服务器发送垃圾邮件。 (I was obviously not the only one to try this). (我显然不是唯一一个尝试这个的人)。

This is why I used an external service like apify.com to pull the data and re-expose data over their API.这就是为什么我使用像 apify.com 这样的外部服务来提取数据并在其 API 上重新公开数据。

This is my AppScripts coingecko.gs :这是我的 AppScripts coingecko.gs

/**
 * get latest coingecko market prices dataset
 */
async function GET_COINGECKO_PRICES(key, actor) {
  const coinGeckoUrl = `https://api.apify.com/v2/acts/${actor}/runs/last/dataset/items?token=${key}&status=SUCCEEDED`
  return ImportJSON(coinGeckoUrl);
}

You need ImportJSON function, available here: https://github.com/bradjasper/ImportJSON/blob/master/ImportJSON.gs您需要ImportJSON function,可在此处获得: https://github.com/bradjasper/ImportJSON/blob/master/ImportJSON.gs

Then in a cell I write: =GET_COINGECKO_PRICES(APIFY_API_KEY,APIFY_COINGECKO_MARKET_PRICES) , you will have to create two field named APIFY_API_KEY and APIFY_COINGECKO_MARKET_PRICES in order for this to work.然后在我写的单元格中: =GET_COINGECKO_PRICES(APIFY_API_KEY,APIFY_COINGECKO_MARKET_PRICES) ,您必须创建两个名为APIFY_API_KEYAPIFY_COINGECKO_MARKET_PRICES的字段才能使其正常工作。

Then register on apify.com, then you'll have to create an actor by forking apify-webscraper actor.然后在 apify.com 上注册,然后你必须通过 fork apify-webscraper actor 创建一个 actor。

I set the StartURLs with https://api.coingecko.com/api/v3/coins/list , this will give me the total number of existing crypto (approx 11000 as of today), and number of page so I can run the request concurrently (rate limit is 10 concurrent requests on coingecko), then I just replace /list with /market and set the proper limit to get all the pages I need.我将StartURLs设置为https://api.coingecko.com/api/v3/coins/list ,这将为我提供现有加密的总数(截至今天约为 11000)和页数,以便我可以运行并发请求(在 coingecko 上的速率限制是 10 个并发请求),然后我只需将/list替换为/market并设置适当的限制以获取我需要的所有页面。

I use the following for the tasks page function:我对任务页面 function 使用以下内容:

async function pageFunction(context) {
    let marketPrices = [];
    const ENABLE_CONCURRENCY_BATCH = true;
    const PRICE_CHANGE_PERCENTAGE = ['1h', '24h', '7d'];
    const MAX_PAGE_TO_SCRAP = 10;
    const MAX_PER_PAGE = 250;
    const MAX_CONCURRENCY_BATCH_LIMIT = 10;
    await context.WaitFor(5000);
    const cryptoList = readJson();
    const totalPage = Math.ceil(cryptoList.length / MAX_PER_PAGE);

    context.log.info(`[Coingecko total cryptos count: ${cryptoList.length} (${totalPage} pages)]`)
    
    function readJson() {
        try {
            const preEl = document.querySelector('body > pre');
            return JSON.parse(preEl.innerText);
        } catch (error) {
            throw Error(`Failed to read JSON: ${error.message}`)
        }
    }


    async function loadPage($page) {
        try {
            const params = {
                vs_currency: 'usd',
                page: $page,
                per_page: MAX_PER_PAGE,
                price_change_percentage: PRICE_CHANGE_PERCENTAGE.join(','),
                sparkline: true,
            }
            
            let pageUrl = `${context.request.url.replace(/\/list$/, '/markets')}?`;
            pageUrl += [
                `vs_currency=${params.vs_currency}`,
                `page=${params.page}`,
                `per_page=${params.per_page}`,
                `price_change_percentage=${params.price_change_percentage}`,
            ].join('&');
                        
            context.log.info(`GET page ${params.page} URL: ${pageUrl}`);
            const page = await fetch(pageUrl).then((response) => response.json());
            context.log.info(`Done GET page ${params.page} size ${page.length}`);
            marketPrices = [...marketPrices, ...page];
            return page
        } catch (error) {
            throw Error(`Fail to load page ${$page}: ${error.message}`)
        }
    }

    try {
        if (ENABLE_CONCURRENCY_BATCH) {
            const fetchers = Array.from({ length: totalPage }).map((_, i) => {
                const pageIndex = i + 1;
                if (pageIndex > MAX_PAGE_TO_SCRAP) {
                    return null;
                }
                return () => loadPage(pageIndex);
            }).filter(Boolean);
            while (fetchers.length) {   
                await Promise.all(
                    fetchers.splice(0, MAX_CONCURRENCY_BATCH_LIMIT).map((f) => f())
                );
            }
        } else {
            let pageIndex = 1
            let page = await loadPage(pageIndex)
            while (page.length !== 0 && page <= MAX_PAGE_TO_SCRAP) {
                pageIndex += 1
                page = await loadPage(pageIndex)
            }
        }
    } catch (error) {
        context.log.info(`Fetchers failed: ${error.message}`);
    }

    context.log.info(`End: Updated ${marketPrices.length} prices for ${cryptoList.length} cryptos`);
    const data = marketPrices.sort((a, b) => a.id.toLowerCase() > b.id.toLowerCase() ? 1 : -1);
    context.log.info(JSON.stringify(data.find((item) => item.id.toLowerCase() === 'bitcoin')));

    function sanitizer(item) {
        item.symbol = item.symbol.toUpperCase()
        return item;
    }
    return data.map(sanitizer)
}

I presume you are hiting the same issue I had with coinmarketcap, and that you could do the same with it.我认为您遇到了与 coinmarketcap 相同的问题,并且您可以对它做同样的事情。

You're not return ing anything to the sheet, but just logging it.您不会将任何内容return到工作表,而只是将其记录下来。 Return it:把它返还:

return jsondata.data[ticker].quote['USD'].price

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM