显示带有标签的帖子 urlfetchapp. 显示所有帖子
显示带有标签的帖子 urlfetchapp. 显示所有帖子

2017年6月8日星期四

使用Yahoo!将股票报价导入AdWords脚本财务API

最近有人在Twitter上问我是否见过 使用股市表现来调整出价的脚本。老实说我从来没有,但是我多次被问到这种能力。所以我认为我会为此做些事情。

为股票数据找到可靠且免费的API有点困难,但是每个人似乎都在指出某种程度上隐藏的Yahoo!。财务API。尽管事实是围绕它构建了多个库,但是除了 StackOverflow发布 谈论它。长话短说,此API可能会随时停止运行,因此,后果自负。

这是一些示例代码,可帮助您开始使用此代码。下面的代码仅查找了一些引号(一个来自比特币),并将它们加载到您选择的Google Spreadsheet中。非常简单。令人困惑的一件事是您需要传递给API的“ f =“参数。它记录在 此博客文章 但还是很混乱。它是一两个或两个字符代码的字符串,用于定义要返回的列。对于大多数人来说,符号,名称和当前价格应该足够了。随时根据需要自定义它。

谢谢,
拉斯
/******************************************
* Yahoo Finance API Class Example
* Version 1.0 
* Created By: 拉斯 Savage
* FreeAdWordsScripts.com
******************************************/
function main() {
  var sheetUrl = 'ENTER A GOOGLE SHEET URL HERE';
  
  var yfa = new YahooFinanceAPI({
    symbols: ['^GSPC','VTI','^IXIC','BTCUSD=X'],
    f: 'snl1' // or something longer like this 'sl1abb2b3d1t1c1ohgv'
  });
  for(var key in yfa.results) {
    Logger.log(Utilities.formatString('Name: "%s", Symbol: "%s", Last Trade Price: $%s', 
                                      yfa.results[key].name,
                                      key,
                                      yfa.results[key].last_trade_price_only));
  }
  
  var includeColumnHeaders = true;
  var sheetData = yfa.toGoogleSheet(includeColumnHeaders);
  var ss = SpreadsheetApp.openByUrl(sheetUrl).getActiveSheet();
  for(var i in sheetData) {
    ss.appendRow(sheetData[i]);
  }
}

只需将以下代码复制到AdWords脚本的底部即可,您应该一切顺利。
/******************************************
* Yahoo Finance API Class
* Use this to pull 股市 quotes from Yahoo Finance
* Version 1.0 
* Created By: 拉斯 Savage
* FreeAdWordsScripts.com
******************************************/
function YahooFinanceAPI(configVars) {
  var QUERY_URL_BASE = '//query.yahooapis.com/v1/public/yql';
  var FINANCE_URL_BASE = 'http://download.finance.yahoo.com/d/quotes.csv';
  this.configVars = configVars;
  
  /*************
   * The results are stored here in a 
   * map where the key is the ticker symbol
   * { 'AAPL' : { ... }, 'GOOG' : { ... }
   *************/
  this.results = {};
  
  /************
   * Function used to refresh the results
   * from Yahoo! Finance API. Called automatically
   * during object reaction.
   ************/
  this.refresh = function() {
    var queryUrl = getQueryUrl(this.configVars);
    var resp = UrlFetchApp.fetch(queryUrl,{muteHttpExceptions:true});
    if(resp.getResponseCode() == 200) {
      var jsonResp = JSON.parse(resp.getContentText());
      if(jsonResp.query.count == 1) {
        var row = jsonResp.query.results.row;
        this.results[row.symbol] = row;
      } else if(jsonResp.query.count > 1) {
        for(var i in jsonResp.query.results.row) {
          var row = jsonResp.query.results.row[i];
          this.results[row.symbol] = row;
        }
      }
    } else {
      throw resp.getContentText();
    }
  }
  
  /************
   * Translates the results into a 2d array
   * to make it easier to 广告 d into a Google Sheet.
   * includeColumnHeaders - true or false if you want
   *   headers returned in the results.
   ************/
  this.toGoogleSheet = function(includeColumnHeaders) {
    if(!this.results) { return [[]]; }
    var retVal = [];
    var headers = null;
    for(var key in this.results) {
      if(!headers) {
        headers = Object.keys(this.results[key]).sort();
      }
      var row = [];
      for(var i in headers) {
        row.push(this.results[key][headers[i]]);
      }
      retVal.push(row);
    }
    if(includeColumnHeaders) {
      return [headers].concat(retVal);
    } else {
      return retVal;
    }
  }
  
  // Perform a refresh on object creation.
  this.refresh();
  
  // Private functions
  
  /************
   * Builds Yahoo Finance Url
   ************/
  function getFinanceUrl(configVars) {
    var financeUrlParams = {
      s : configVars.symbols.join(','),
      f : configVars.f,
      e : '.json'
    }
    return FINANCE_URL_BASE + serialize(financeUrlParams);
  }
  
  /************
   * Builds Yahoo Query Url
   ************/
  function getQueryUrl(configVars) {
    var financeUrl = getFinanceUrl(configVars);
    var cols = fToCols(configVars.f);
    var queryTemplate = "select * from CSV where url='%s' and columns='%s'";
    var query = Utilities.formatString(queryTemplate, financeUrl,cols.join(','));
    var params = {
      q : query,
      format : 'json'
    }
    var finalRestUrl = QUERY_URL_BASE + serialize(params);
    return finalRestUrl;
  }

  /************
   * This function translates the f parameter
   * into actual field names to use for columns
   ************/
  function fToCols(f) {
    var cols = [];
    var chunk = '';
    var fBits = f.split('').reverse();
    for(var i in fBits) {
      chunk = (fBits[i] + chunk);
      if(fLookup(chunk)) {
        cols.push(fLookup(chunk));
        chunk = '';
      }
    }
    return cols.reverse();
  }
  
  /************
   * Copied from: http://stackoverflow.com/a/18116302
   * This function 兑换s a hash into 
   * a url encoded query string.
   ************/
  function serialize( obj ) {
    return '?'+
      Object.keys(obj).reduce(
        function(a,k) { 
          a.push(k+'='+encodeURIComponent(obj[k]));
          return a
        },
        []).join('&');
  }
  
  /************
   * Adapted from http://www.jarloo.com/yahoo_finance/
   * This function maps f codes into 
   * friendly column names.
   ************/
  function fLookup(f){
    return{
      a:'ask',b:'bid',b2:'ask realtime',b3:'bid realtime',p:'previous close',o:'open',
      y:'dividend yield',d:'dividend per share',r1:'dividend pay date',
      q:'ex-dividend date',c1:'change',c:'change & percent change',c6:'change realtime',
      k2:'change percent realtime',p2:'change in percent',d1:'last trade date',
      d2:'trade date',t1:'last trade time',c8:'after hours change realtime',
      c3:'commission',g:'days low',h:'days high',k1:'last trade realtime with time',
      l:'last trade with time',l1:'last trade price only',t8:'1 yr target price',
      m5:'change from 200 day moving average',m6:'percent change from 200 day moving average',
      m7:'change from 50 day moving average',m8:'percent change from 50 day moving average',
      m3:'50 day moving average',m4:'200 day moving average',w1:'days value change',
      w4:'days value change realtime',p1:'price paid',m:'days range',m2:'days range realtime',
      g1:'holdings gain percent',g3:'annualized gain',g4:'holdings gain',
      g5:'holdings gain percent realtime',g6:'holdings gain realtime',t7:'ticker trend',
      t6:'trade links',i5:'order book realtime',l2:'high limit',l3:'low limit',
      v1:'holdings value',v7:'holdings value realtime',s6: 'revenue',k:'52 week high',
      j:'52 week low',j5:'change from 52 week low',k4:'change from 52 week high',
      j6:'percent change from 52 week low',k5:'percent change from 52 week high',
      w:'52 week range',v:'more info',j1:'market 大写',j3:'market cap realtime',
      f6:'float shares',n:'name',n4:'notes',s:'symbol',s1:'shares owned',x:'stock exchange',
      j2:'shares outstanding',v:'volume',a5:'ask size',b6:'bid size',k3:'last trade size',
      a2:'average daily volume',e:'earnings per share',e7:'eps estimate current year',
      e8:'eps estimate next year',e9:'eps estimate next quarter',b4:'book value',j4:'ebitda',
      p5:'price sales',p6:'price book',r:'pe ratio',r2:'pe ratio realtime',r5:'peg ratio',
      r6:'price eps estimate current year',r7:'price eps estimate next year',s7:'short ratio'
    }[f];
  }
}

2013年10月30日,星期三

禁用缺货商品的广告和关键字

UPDATE 2016-01-26:为响应一些常见问题,此脚本已更新为包含一些增强功能。请参阅v1.2的更改日志说明。

作为我的后续行动 有关黑色星期五/网络星期一酷脚本的问题,今天,我整理了一个脚本来遍历您的网址,并检查网站上该商品是否缺货。如果是这样,我们将暂停广告组。

该脚本具有与我相同的一些元素 有关检查帐户中断开链接的脚本,但实际上会提取每个页面的html源,并搜索可配置的字符串,让其知道何时缺货。

让我们来看一个例子。我喜欢一些我发现的古怪礼物 ModCloth.com。但是,像任何在线商店一样,有些商品也无货。这是我在测试此脚本时发现的。
为了使该脚本正常工作,我需要找出页面缺货时的区别。如果我右键单击并查看页面源,然后搜索作品“库存”,则可以看到使用它的几个不同地方。其中之一是以下内容,内容为“ in_stock”:false。
这看起来很有希望。我检查了一个库存项目,并且确定该页面上确实有“ in_stock”:true。

好了,现在我知道在代码中需要使用什么文本来填充OUT_OF_STOCK_TEXT变量。现在每个站点都将有所不同,因此我有一个简单的脚本,该脚本使用与可用于测试的完整脚本相同的url逻辑。

一旦您在着陆页的源中找到一些HTML文本来标识某件商品是否缺货,那么最好使用完整的脚本。脚本中还有一些其他选项,可让您启用或禁用脚本中的各种url操作。请记住,这只会暂停链接到页面且缺货的广告或关键字。

谢谢,
拉斯

2013年2月26日,星期二

使用AdWords脚本对OAuth服务进行身份验证

如今,许多API使用OAuth来使用户进行身份验证并调用其服务。 一个非常受欢迎的例子是 Twitter REST API.  浏览AdWords脚本参考,您会发现,使用AdWords脚本可以做的一件很酷的事情是使用UrlFetchApp从URL提取数据。

因此,我问的问题是“我可以编写脚本以使用OAuth进行身份验证并从Twitter REST API检索数据吗?”

显然,我看过的第一个地方是 OAuthConfig UrlFetchApp的类。 我设置了配置并尝试拨打电话,但始终收到验证错误。 一项小小的研究表明,我并不是唯一遇到此问题的人。 问题源于以下事实:通常,用户需要先通过某种对话框进行身份验证,然后才能访问数据。看到这个例子关于 连接到Picasa网络相册 更多细节。

但是后来我发现 一个帖子 有关某人设法通过在其脚本中重新实现OAuth身份验证系统并使用标准UrlFetchApp.fetch()请求来解决此问题的信息。

我正面临挑战,所以我自己重新创建了它。 原来是很多代码,但是最后,您真正需要担心的唯一事情是从Twitter获取正确的密钥并调用_build_authorization_string()。

谢谢,
拉斯

//-----------------------------------
// Authenticate and Connect to OAuth Service
// Created By: 拉斯 Savage
// FreeAdWordsScripts.com
//-----------------------------------
function main() {
  //Define the Twitter Keys and Secrets
  //More info on obtaining these can be found 在 //dev.twitter.com/docs/auth/tokens-devtwittercom
  var 认证_key_stuff = {
    "consumer_key" : "your consumer key",
    "consumer_secret" : "your consumer secret",
    "access_token" : "your access token",
    "access_token_secret" : "your access token secret"
  };

  // Update this with the REST url you want to 呼叫.  I only tested it with GET
  // but i don't think there is anything stopping a POST request from working.
  var url_stuff = {  
    "http_method" : 'GET',
    "base_url" : "//api.twitter.com/1.1/statuses/user_timeline.json"
  };

  //Add the parameters for the REST url you want to 呼叫.
  var url_param_stuff = {
    "screen_name" : "russellsavage" //hey that's me!
  };
  
  // Don't touch this stuff
  var other_oauth_data = {
    "认证_nonce" : Utilities.base64Encode(Math.random() +
          "secret_sauce" +
          (new Date()).getTime()).replace(/(?!\w)/g, ''),
    "认证_signature_method" : "HMAC-SHA1",
    "认证_timestamp" : Math.round((new Date()).getTime() / 1000.0),
    "认证_version" : "1.0"
  };
  
  // Here is where the magic happens
  var auth_string = _build_authorization_string(oauth_key_stuff,url_stuff,url_param_stuff,other_oauth_data);

  var options = {
    "headers" : { "Authorization" :  auth_string }
  };
    
  var url = _build_url(url_stuff,url_param_stuff);
  var response = UrlFetchApp.fetch(url, options);
  var tweets = JSON.parse(response.getContentText());
  
  //now let's log my amazing tweets!
  for(var tweet in tweets) {
    var t = tweets[tweet];
    Logger.log(t.text);
  }
 
  // HELPER FUNCTIONS BELOW
  
  function _build_url(base_url,param_stuff){
    var url = base_url.base_url;
    if(param_stuff != {}) {
      url += '?';
    }
    for(var key in param_stuff) {
      url += key + "=";
      url += encodeURIComponent(param_stuff[key]);
      url += '&';
    }
    return url.slice(0,-1);
  }
  
  function _build_param_string(auth_keys,url_data,oauth_data) {
    var data_for_param_string = {
      "认证_consumer_key" : auth_keys.consumer_key,
      "认证_nonce" : 认证_data.oauth_nonce,
      "认证_signature_method" : 认证_data.oauth_signature_method,
      "认证_timestamp" : 认证_data.oauth_timestamp,
      "认证_token" : auth_keys.access_token,
      "认证_version" : 认证_data.oauth_version
    };
    
    // 广告 d 广告 ditional url values
    for(var my_key in url_data) { 
      data_for_param_string[my_key] = url_data[my_key]; 
    }
    
    // find and sort the keys for later
    var keys = [];
    for(var key in data_for_param_string) {
      keys.push(key);
    }
    keys.sort();
    
    //finally build and return the param string
    var param_string = "";
    for(var i in keys) {
      param_string += keys[i] + "=" + encodeURIComponent(data_for_param_string[keys[i]]);
      if(i < keys.length - 1) {
        param_string += "&";
      }
    }
    
    return param_string;
  }
  
  function _build_sig_base_string(my_url_stuff,my_param_string) {
    return my_url_stuff.http_method +
      "&" + encodeURIComponent(my_url_stuff.base_url) +
      "&" + encodeURIComponent(my_param_string);
  }
  
  function _build_sigining_key(my_key_stuff) {
    return encodeURIComponent(my_key_stuff.consumer_secret) + 
      "&" + encodeURIComponent(my_key_stuff.access_token_secret);
  }
  
  function _build_oauth_signature(base_string,sign) {
    return Utilities.base64Encode(
      Utilities.computeHmacSignature(
        Utilities.MacAlgorithm.HMAC_SHA_1, 
        base_string, 
        sign
      )
    );
  }
  
  function _build_authorization_string(my_key_stuff,my_url_stuff,my_url_param_stuff,my_oauth_stuff) {
    var param_string = _build_param_string(my_key_stuff,my_url_param_stuff,my_oauth_stuff);
    var sig_base_string = _build_sig_base_string(my_url_stuff,param_string);
    var signing_key = _build_sigining_key(my_key_stuff);
    var 认证_signature = _build_oauth_signature(sig_base_string,signing_key);
    return "OAuth " +
           encodeURIComponent("认证_consumer_key") + '="' + 
             encodeURIComponent(my_key_stuff.consumer_key) + '", ' +
           encodeURIComponent("认证_nonce") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_nonce) + '", ' +
           encodeURIComponent("认证_signature") + '="' + 
             encodeURIComponent(oauth_signature) + '", ' +
           encodeURIComponent("认证_signature_method") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_signature_method) + '", ' +
           encodeURIComponent("认证_timestamp") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_timestamp) + '", ' +
           encodeURIComponent("认证_token") + '="' + 
             encodeURIComponent(my_key_stuff.access_token) + '", ' +
           encodeURIComponent("认证_version") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_version) + '"';
    
  }
  
}