2012/12/22

[C++]goo.glを使ってみた。

,
 C++ならこの世のすべて森羅万象揺りかごから墓場までやりたいことは全てできると信じてやまない俺たちは…

 ということで、「googleの短縮URLサービス(goo.gl)」をC++で使ってみました。
 例のごとく、「自分で書かずになるべくライブラリで済ます」という他力本願なスタイルです。(勉強にはなってない)

目標時間(コード書き始めてから):1時間半
実際にかかった時間:2時間ちょい

goo.glのAPIを使うには

まず、goo.glのAPIを使うにはどうしたら良いか。goo.glがAPI公開されたらしいので試してみた | Mach3.laBlogさんを参考にしました。

 短縮URLを取得するときは、POSTで、Content-Typeをapplication/jsonにして、{"longUrl":"http://www.XXX.com/"}のようなjsonデータを送るみたいです。逆に、元のURLを取得するときは、https://www.googleapis.com/urlshortener/v1/url?key=[apiKey]&shortUrl=[短縮URL]としたURLをGETすれば、jsonデータが返ってくるみたいです。

で、纏めると以下のようなことを行いました。
・APIkeyの取得(下準備)
・jsonのパーサライブラリを探して、APIと送受信するデータを整形する
・HTTP通信をするライブラリを探し、通信部分を実装する。


 APIkeyの取得方法です。まずは、Google apisの左メニューServicesを選び、URL Shortener APIのStatusのOFFというところをクリックしてONに変えます。すると、APIkeyが、左メニューのAPI Accessを選ぶと出てきます。適当にコピペしておきます。


 次、C++でjsonをパースするパーサを探します。今回は「picojson」という軽量なライブラリを使うことにしました。使い方の参考 : picojsonを使ってみた - Capsule


 最後に、HTTP通信を行うライブラリです。これは、windowsのAPIであるWinHTTPを使いました。最初はC言語向けAPIを呼び出していたのですが(参考)、これだとGetLastError()で12019が返ってきて進みませんでした。で、WinHTTPのクラスを呼ぶことにしました。参考にしたのはこちら

 困ったことに、このWinHTTPは使用例が少なく、インターネットを見ていても、わからないことが多かったです。
 例えば、POSTの際のデータの送り方が謎でした。(そもそもHTTPプロトコルをあまり理解してないのですが、)どうやらBodyにデータを付けて送るらしく、Send()に引数で渡してみたら動きました。

 次に、HTTPヘッダのContent-Typeの変え方がわかりませんでした。ダメ元でMSDNでIWinHttpRequestのメンバを見ていたら、どうやらSetRequestHeaderがある模様。なので、サンプルコード(ヘッダのDateを設定する)を少しいじってたら、動くようにはなりました。
 因みに、OpenしてからSetRequestHeaderしないとダメらしくて、逆にしてて例外だかけになり、30分くらい無駄にしました…。

コード

現状はこんな感じ。
※エラーチェックしてない部分があります。それ抜きでも、使うときは自己責任で。
#include < iostream > 
#include < string >

#include < windows.h >
#include < winhttp.h >

#include "picojson.h"

#import "winhttp.dll" named_guids

class googl{
private:
 const static std::string apiKey;

public:
 static std::string getLongUrl(const std::string& shortUrl){
  //叩くAPIのURLの生成
  const std::string uri = "https://www.googleapis.com/urlshortener/v1/url?key=" + apiKey + "&shortUrl=" + shortUrl;

  //ここで通信する
  //参考:http://www.sutosoft.com/room/archives/000314.html
  ::CoInitialize(NULL);
  WinHttp::IWinHttpRequestPtr request;
  request.CreateInstance(WinHttp::CLSID_WinHttpRequest);

  request->Open("GET", uri.c_str());
  request->Send();
  //int status = request->Status;
  std::string resp = request->ResponseText;

  ::CoUninitialize();

  //帰ってきたjsonをデコードする(がんばれパーサ)
  using namespace picojson;

  value v;
  parse(v, resp.begin(), resp.end());
  
  std::string ret = "";
  //呪文
  if(v.is< object >()){
   value longUrl = v.get("longUrl");
   if(longUrl.is< std::string>()){
    //std::cout << "longUrl:" << longUrl.get< std::string>() << std::endl;
    ret = longUrl.get< std::string>();
   }
  }

  return ret;
 }

 static std::string getShortUrl(const std::string& longUrl)
 {
  //Google先生に渡すjsonのデータを作ります。{"longUrl":"http://www.XXXXXX./"}の形。
  using namespace picojson;

  object o;
  o["longUrl"] = (value)std::string(longUrl);

  value v(o);
  std::string json = v.serialize(); //なんか"/"が"\/"になってる気がするけど

  //叩くAPIのURL(http://.../url?key=[apikey])の生成
  const std::string uri = "https://www.googleapis.com/urlshortener/v1/url?key=" + apiKey;

  //ここで通信する
  //参考:http://www.sutosoft.com/room/archives/000314.html
  ::CoInitialize(NULL);
  WinHttp::IWinHttpRequestPtr request;
  request.CreateInstance(WinHttp::CLSID_WinHttpRequest);

  //短縮URLを頼むときはPOST
  request->Open("POST", uri.c_str());
  
  //ヘッダーのContent-Typeをjsonにする。Openの後に呼ぶ!
  BSTR bstrName  = SysAllocString(L"Content-Type");
  BSTR bstrValue = SysAllocString(L"application/json");
  request->SetRequestHeader(bstrName, bstrValue);
  SysFreeString(bstrName);
  SysFreeString(bstrValue);

  //POSTなのでBodyにjsonのデータを入れて送る。
  request->Send(json.c_str());

  //返ってきたデータを保存
  std::string resp = request->ResponseText;
  ::CoUninitialize();

  //返ってきたデータから短縮URLを抜き出す
  value v2;
  parse(v2, resp.begin(), resp.end());
  
  std::string ret = "";

  //呪文
  if(v2.is< object >()){
   value id = v2.get("id");
   if(id.is< std::string>()){
    //std::cout << "shortUrl:" << shortUrl.get< std::string>() << std::endl;
    ret = id.get< std::string>();
   }
  }

  return ret;
 }
};

//static constメンバの初期化
//http://frog.raindrop.jp/knowledge/archives/000281.html
const std::string googl::apiKey = "ここに取得したapikeyを書いてね";



int main()
{
 //短縮
 std::string shorten = googl::getShortUrl("https://twitter.com/"); 
 std::cout << shorten << std::endl;

 //元のURLを取得
 std::cout << googl::getLongUrl(shorten) << std::endl;

 return 0;
}

こうやってmainで呼ぶと…


こんな感じで取得できました。これで、Google先生のAPIを、またひとつ使えるようになりました。

0 コメント to “[C++]goo.glを使ってみた。”

コメントを投稿