はじめに
データの可視化を行う当たってGoogleの各種APIを触ってみましたが、最初は認証の仕方とか基本的な書き方とかでつまづく所もあるので、ハンズオンとして触り方をなるべくわかりやすく紹介したいと思います。
今回紹介するのは
- Google Analyticsのレポート
- Google SpreadSheet
- Big Query
- Google Ad Managerのレポート
のAPIになります。
サンプルコードはこちらに上げています。
Google Analytics の レポートAPI
概要
- GAのAPIドキュメントを検索すると、Reporting API v4 と Core Reporting API が出てきますが、最新は Reporting API v4 です。
- ガイドをみると、Node.js用のクライアントは無い様なので、HTTP POSTリクエストを自分で生成する必要があります。
- 認証に関してはサービスアカウントを作る方法が固いと思います。こちらからサービスアカウントを新規作成し、登録完了ページからクレデンシャルjsonをダウンロードしておいてください。
- サンプルコードでは
credentials/ga.json
という名前で保存する想定です。 - 参考: サービス アカウント キーの作成
- サンプルコードでは
- サービスアカウントの権限でクエリーを実行するので、作成したサービスアカウントをGAにユーザとして追加し、レポートを実行するための権限を与えておいてください。
- 実際にAPIリクエストを行う際の認証はOAuthトークンを使用します。なにやら難しそうな感じですが、
google-auth-library
を使用すると簡単に取得ができる様になっています。 - クエリの書き方はこちらが参考になります。
実装
パッケージのインストール
npm i --save google-auth-library axios
コード
// GAのAPIの利用例です// トークンの取得のためconst{auth}=require('google-auth-library');// HTTPリクエストにはaxiosを使用します。constaxios=require('axios');constpath=require('path');(async()=>{// --- start OAuthアクセストークンを手に入れる ---// credentialファイルを読み込むconstcredentialJson=require(path.resolve(`${__dirname}/../credentials/ga.json`));constclient=auth.fromJSON(credentialJson);// 許可するスコープを定義client.scopes=['https://www.googleapis.com/auth/analytics.readonly'];awaitclient.authorize();consttoken=client.credentials.access_token;// --- end OAuthアクセストークンを手に入れる ---// --- start Queryを作成する ---// ブラウザに対するページビューを取得するconstquery={reportRequests:[{viewId:'{{ GAのビューIDを入力 }}',// todo// レポートの期間dateRanges:[{startDate:'2020-04-01',endDate:'2020-04-02',}],// ディメンジョンdimensions:[{name:'ga:browser'}],// 指標metrics:[{expression:'ga:pageviews'},],// 指標のフィルターをかけたい場合は以下の様に記述する// dimensionFilterClauses: [// {// operator: 'OR',// filters: [// {// dimensionName: 'ga:pagePath',// operator: 'REGEXP', // or EXACT// expressions: ['hoge']// }// ]// }// ],// GAのレポートはコンソールからの操作と同様にサンプリングされる可能性があることに注意するsamplingLevel:'DEFAULT'}]};// --- end Queryを作成する ---// --- start リクエストを実行する ---constresponse=awaitaxios.post('https://analyticsreporting.googleapis.com/v4/reports:batchGet',query,{headers:{// アクセストークンを設定Authorization:`Bearer ${token}`}});// --- start リクエストを実行する ---// 出力console.table(response.data.reports[0].data.rows.map(row=>{return[...row.dimensions,...row.metrics[0].values];}));})().catch(e=>console.log(e));
補足
- クエリーでどんなディメンジョンや指標が使えるかはDimensions & Metrics Explorerを参考
- GAのレポートはコンソールからの操作と同様に結果がサンプリングされる可能性があることに注意してください。
BigQueryのクエリをAPIで実行
概要
- BigQueryのAPIにはNode.jsのクライアントライブラリがありますのでこちらを利用します。
- 自身のGCPアカウントのプロジェクトでAPIを有効化します。
- こちらから有効化できます。https://console.cloud.google.com/apis/library/bigquery.googleapis.com?q=bigquery
- 有効化したアカウントのアカウント名は下記コードに記載します。
- 認証はGAと同じくサービスアカウントで行うのがいいでしょう。サービスアカウントに
BigQuery ジョブユーザー
などのAPIが実行可能な権限をつけてください。- サンプルコードでは
credentials/bq.json
という名前で保存する想定です。
- サンプルコードでは
- こちらのページにサンプルがあるので簡単に記述ができますが、サービスアカウントで認証を行う部分だけ以下のコードに追記しておきます。
実装
パッケージのインストール
npm i --save @google-cloud/bigquery
コード
constpath=require('path');(async()=>{// クライアントを初期化const{BigQuery}=require('@google-cloud/bigquery');constbigquery=newBigQuery({// 認証はクレデンシャルファイルのパスを渡すだけでいいkeyFilename:path.resolve(`${__dirname}/../credentials/bq.json`),projectId:'{{ プロジェクト名を入力 }}'});// クエリを作成constquery=`SELECT name
FROM \`bigquery-public-data.usa_names.usa_1910_2013\`
WHERE state = 'TX'
LIMIT 100`;// 全てのオプションは -> https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/queryconstoptions={query:query,location:'US',};// ジョブを実行const[job]=awaitbigquery.createQueryJob(options);// ジョブの完了を待つconst[rows]=awaitjob.getQueryResults();// 結果を表示console.log('Rows:');console.table(rows);})();
Google Spread Sheet の API
はじめに
- 今回はシートにデータを吐き出してみます。
- Google Sheets APIを使用します。
- Node.jsのクライアントがあるので、使用します。
- こちらも上記サンプルページの通りなのですが、認証の通し方を少し変えます。
- 自身のGCPのプロジェクトで Google Sheets APIを有効にします。
- マイドライブから操作したいSpreadSheetを任意のサービスアカウントに対して編集権限で共有します。
- サンプルコードでは
credentials/sheets.json
という名前でサービスアカウントのクレデンシャルjsonを保存する想定です。
- サンプルコードでは
実装
実装
パッケージのインストール
npm i --save googleapis
コード
constpath=require('path');const{google}=require('googleapis');(async()=>{// 認証するconstauthClient=awaitgoogle.auth.getClient({keyFile:path.resolve(`${__dirname}/../credentials/sheets.json`),scopes:['https://www.googleapis.com/auth/spreadsheets']});constsheets=google.sheets({version:'v4',auth:authClient});// 保存するデータconstcells=[[1,2,3],[4,5,6]];// 書き込みconstreq={// スプレッドシートのID, URLの一部// https://docs.google.com/spreadsheets/d/{{ここの部分}}/edit#gid=0spreadsheetId:'{{SPREAD_SHEET_ID}}',range:`シート1!A:AZ`,valueInputOption:'USER_ENTERED',resource:{values:cells}};awaitsheets.spreadsheets.values.append(req);})();
実装
Google Ad Manager のレポートAPI
はじめに
- 一番の曲者でした。
- Node.jsのクライアントライブラリは無いので、HTTPリクエストを自分で作る必要があります。
- クエリの方法ですが、SOAPという古いAPIの仕様が採用されているので、XMLでクエリを記述してPOSTのBODYに埋めこみます。
- JSでXMLを扱うために
xml-js
というライブラリを使用します。クエリはXMLに変換するので、xml-js
に合わせた形式で記述しています。 - 執筆時点で最新バージョンはv202002
- 認証は同様にサービスアカウントで行う。GAMの管理画面からGCPコンソールで作成したサービスアカウントを追加し、レポート用の権限をつけてください。
- サンプルコードでは
credentials/gam.json
にサービスアカウントのクレデンシャルが保存されている想定です。 - GAMはレポートの条件によって、APIからは実行できないレポート条件が存在します。API経由で保存済みのレポートのクエリを取得する機能がありますので、GAMの管理画面からサービスアカウントのユーザに「成り代わる」をして、レポートを保存しておくとAPI経由でクエリの内容とAPIに対応しているかを取得できます。(こちらはサンプルコードに書いていません。)
レポート取得の流れ
こちらの通り
https://developers.google.com/ad-manager/api/reference/v202002/ReportService
ReportService.runReportJob
でジョブを作成ReportService.getReportJob
でジョブの状態を取得し、完了するまでポーリングする- 完了後、
ReportService.getReportDownloadURL
を使用して、レポートのダウンロード用URLを取得する - ダウンロードする
実装
パッケージのインストール
npm i --save google-auth-library request axios xml-js
コード
constpath=require('path');const{auth}=require('google-auth-library');constaxios=require('axios');const{js2xml,xml2js}=require('xml-js');// SOAPのベース// soapenv:Body部分を色々変えるconstsoapJsonBase={_declaration:{_attributes:{version:'1.0',encoding:'UTF-8'}},'soapenv:Envelope':{_attributes:{'xmlns:soapenv':'http://schemas.xmlsoap.org/soap/envelope/','xmlns:xsd':'http://www.w3.org/2001/XMLSchema','xmlns:xsi':'http://www.w3.org/2001/XMLSchema-instance'},'soapenv:Header':{'ns1:RequestHeader':{_attributes:{'soapenv:actor':'http://schemas.xmlsoap.org/soap/actor/next','soapenv:mustUnderstand':'0',// APIのバージョンはここで指定'xmlns:ns1':`https://www.google.com/apis/ads/publisher/v202002`},'ns1:networkCode':{_text:'{{ GAMのネットワークIDを指定 }}'},'ns1:applicationName':{_text:'{{ GCPのプロジェクト名を指定 }}'}}},'soapenv:Body':undefined}};(async()=>{// --- クエリの作成 ---// 日時と広告ユニットごとの合計のインプレッションを取得constquery={// ディメンジョンdimensions:[{_text:'DATE'},{_text:'AD_UNIT_NAME'},// 広告ユニット],adUnitView:{_text:'FLAT'},// 指標columns:[// 全体系{_text:'TOTAL_LINE_ITEM_LEVEL_IMPRESSIONS'},// 合計のインプレッション],// レポートの期間startDate:{year:{_text:'2020'},month:{_text:'4'},day:{_text:'1'}},endDate:{year:{_text:'2020'},month:{_text:'4'},day:{_text:'2'}},// 日時範囲の期間は「固定」dateRangeType:{_text:'CUSTOM_DATE'},// フィルターをかける場合は以下の様に記述//statement: {// query: {// _text: ` where PARENT_AD_UNIT_ID = {{広告ユニットID}}`// }//},// 管理画面とタイムゾーンを合わせるtimeZoneType:{_text:'PUBLISHER'}};// --- クエリの作成 ---// --- アクセストークンを取得 ---constkeys=require(path.resolve(`${__dirname}/../credentials/gam.json`));constclient=auth.fromJSON(keys);client.scopes=['https://www.googleapis.com/auth/dfp','https://www.googleapis.com/auth/analytics.readonly'];awaitclient.authorize();consttoken=client.credentials.access_token;// --- クエリの作成 ---// SOAPヘッダーに変換するオブジェクトを作成// xml-jsでXMLに変換する// 変換後↓// <runReportJob xmlns="https://www.google.com/apis/ads/publisher/v202002">// <reportJob>// <reportQuery> クエリー </reportQuery>// </reportJob>// <runReportJob>letsoapJson={...soapJsonBase};letbody={// ここで実行するコマンドを指定runReportJob:{// 中身はコマンドの引数_attributes:{xmlns:`https://www.google.com/apis/ads/publisher/v202002`},reportJob:{reportQuery:query}}};soapJson['soapenv:Envelope']['soapenv:Body']=body;// APIリクエストを実行letsoapXML=js2xml(soapJson,{compact:true});letresponse=awaitaxios.post(`https://ads.google.com/apis/ads/publisher/v202002/ReportService`,soapXML,{headers:{Authorization:`Bearer ${token}`}});// レスポンスもXMLなのでjsonに変換するletdata=xml2js(response.data,{compact:true});constjobId=data['soap:Envelope']['soap:Body']['runReportJobResponse']['rval']['id']['_text'];console.log('ジョブID',jobId);// ジョブの完了をポーリングするconstsleep=()=>newPromise(resolve=>setTimeout(()=>resolve(),1000));while(true){console.log('ポーリング');soapJson={...soapJsonBase};body={getReportJobStatus:{_attributes:{xmlns:`https://www.google.com/apis/ads/publisher/v202002`},reportJobId:{_text:jobId}}};soapJson['soapenv:Envelope']['soapenv:Body']=body;// APIリクエスト実行soapXML=js2xml(soapJson,{compact:true});response=awaitaxios.post(`https://ads.google.com/apis/ads/publisher/v202002/ReportService`,soapXML,{headers:{Authorization:`Bearer ${token}`}});data=xml2js(response.data,{compact:true});// ステータスを取得conststatus=data['soap:Envelope']['soap:Body']['getReportJobStatusResponse']['rval']['_text'];console.log('ステータス',status);if(['COMPLETED','FAILED'].includes(status)){console.log('完了');break;}awaitsleep();}// レポートのダウンロード用URLを取得するconsole.log('URLを取得');soapJson={...soapJsonBase};body={getReportDownloadURL:{_attributes:{xmlns:`https://www.google.com/apis/ads/publisher/v202002`},reportJobId:{_text:jobId},exportFormat:{_text:'CSV_DUMP'},}};soapJson['soapenv:Envelope']['soapenv:Body']=body;// APIリクエスト実行soapXML=js2xml(soapJson,{compact:true});response=awaitaxios.post(`https://ads.google.com/apis/ads/publisher/v202002/ReportService`,soapXML,{headers:{Authorization:`Bearer ${token}`}});data=xml2js(response.data,{compact:true});constdownloadURL=data['soap:Envelope']['soap:Body']['getReportDownloadURLResponse']['rval']['_text'];console.log(downloadURL);// ダウンロードconstfs=require('fs');constrequest=require('request');awaitnewPromise((resolve)=>{request(downloadURL).pipe(fs.createWriteStream('report.csv.gz')).on('close',async()=>{resolve();});});// gzipなので、解答するconstchild_process=require('child_process');awaitchild_process.execSync(`gunzip -f ./report.csv.gz`);})().catch(e=>console.log(e));
補足
試して見ると結構エラーする条件があると思います。特に多いのはフィルター条件です。例えば定義済みのkey-valueでレポートを作成したい場合があるかと思いますが、GAMの管理画面のレポートと違い文字列でkay-valueを指定することはできません。この場合CUSTOM_CRITERIAと言うディメンジョンをしてすると、CUSTOM_TARGETING_VALUE_IDと言うカラムが追加されるので、これでフィルターをかけます。何でフィルターできて、できないのかはドキュメントにほとんどのディメンジョンごとに記載されています。
使用可能なディメンジョン、指標、フィルター条件などはこちらを参考ください。
https://developers.google.com/ad-manager/api/reference/v202002/ReportService.ReportQuery
以上
参考になれば幸いです。