Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9134

Puppeteerなら、Google App Engine Node.jsでスクレイピングしてスプレッドシート(GAS経由)にデータを蓄積するのが無料だよ!

$
0
0

以下の記事の概要です。

  • 日経225オプションのデータを入手したい。(理由は、分析して儲けるため)
  • 私が契約している証券会社は、Javascriptでデータを取得するタイプのサイトなので、Headless Chromeの利用が必須。
  • これまでの経験で、Google App Engine(python)は使えるが、それ以外はハードル高い。
  • Cloud Runに手を出そうとしたが、ちょっとハードルが高かった。
  • Google App Engineだと、Javascriptでデータ取得するタイプのサイトにスクレイピングするのは、無理。
  • と思っていたら、Puppeteerなるツールがあって、それを使えば、Google App Engineで運用できると最近知った。
  • てことで、Puppeteerでアプリを作って、Google App Engine Node.js(GAE)で運用しようということになった。
  • GAEなら、cronで定期実行可能なので、定期的にデータを取得できる。
  • 定期的にデータが取得できるなら、Googleスプレッドシートへ書き込んでおけば、後から分析可能だよね。
  • 分析する時は、BigQueryが有るから、楽勝じゃん。
  • という思いつきで、だいたい1ヶ月(2月半ばから3月半ばまで)くらいかかって、作ったのが以下のシステムです。(1ヶ月もかかってこれかよ?ってツッコミは無しで。。。。褒めて育つタイプなので。)
  • 本業でフロントエンドをjavascriptで書いてる人なんて、こんなツールで、自動テストとかして、爆速で自動化してるんだろうなーーー(遠い目)。
  • 2020/1月後半から、コロナウイルス大流行で、日経平均爆下げ状態。(プロはこんな時こそボロ儲けのようです。:私じゃない。涙)
  • もう少し、安定してる時が良かったけど、仕方がない。
  • データをためて、頑張って分析しよう!!
  • もうちょっとこうした方が、ええ感じやで等、ゆるめのツッコミ歓迎です。
  • ちなみに、私はエンジニアではなく、ただのサンデープログラマかつ、コピペプログラマです。自分に必要な事しか知らないです。

前提事項
  • puppeteerをスクレイピング用アプリとして利用します。(言語はNode.js(javascript)です。)

    • Google App Engine(GAE) Node.jsでスクレイピングする為にpuppeteerが必要です。
    • GAEでは、特殊なメソッドなどを利用することが多いですが、ほぼpuppeteerだけで動きます。
    • puppeteerが、ブラウザを動かし、ブラウザ経由でWEBサイトにアクセスするので、GAEの特殊メソッドが不要です。
  • Google App Engine Node.jsをスクレイピングの基盤として利用します。

    • ここチェックしてくださいね。無料で利用できる枠の説明です。Google Cloud Platform の無料枠
    • Google App Engine Node.jsは、Cloud Buildが必要で、Cloud Buildを利用するには、課金が必要です。
    • Cloud Buildは、コンテナをビルドするためのツール的なものだと思われます。(分かってません。)
    • 詳しくは、こちら超入門!GCP のCIツール、Cloud Build でリリースサイクル高速化!!
    • Cloud Buildに課金は必要ですが、無料で利用可能です。(ただし、操作が必要です。操作方法は後ほど出てきます。)
    • Cloud Buildを利用するってことは、自分ではpuppeteerアプリを書いてるけど、AppEngine上ではコンテナで動いてるはず。
    • コンテナの知識不要だから、Cloud Run よりもハードルが低いです。しかも、軽い処理ならだいたい無料で動きます。
  • Google Apps Scriptを利用します。

    • puppeteerでスクレイピングしたデータを受け取る処理をします。
    • 受け取ったデータをGoogleスプレッドシートへ書き込みます。
  • Googleスプレッドシートを利用します。

    • puppeteerでスクレイピングしたデータをGoogle Apps Scriptを通じてGoogleスプレッドシートへ蓄積していきます。
    • 最終的に、BigQueryで分析して、投資で儲けます。(ホンマか?とつっこむところ!)
    • ちなみに、BigQueryは、SQLが分かれば使えます。クレジットカードの登録は必要ですが、趣味程度で金払うことは多分無いです。(自己責任でよろしく)
    • Googleスプレッドシートにデータを入れておけば、BigQueryを利用する際のデータの投入が簡単です。(ここでは触れません。)
  • 私のローカル環境は、ChromeBook(HP CromeBook x360)です。

    • Visual Studio Code 1.41.1
    • linuxは、Debian 9.11
    • Node.js v12.14.0
    • npm 6.14.3
    • これを書いた日:2020/3/24

puppeteer(Node.js)の準備
  • とりあえずは、ローカル環境でやってみる。
  • うまくいったら、Cloud Shellで実行してみる。
  • ホームディレクトリで、手動で以下を実施する。
  • (もしかして、以下の手動コマンドが無理なら、更に下のinstall.shを先に実行したらいいかも。)
mkdir ppt
cd ppt
npm init -y
  • 上記コマンドで、package.jsonができる。
  • 以下のように、"scripts": { の中に、"start": "node app.js",を追加してpackage.jsonを保存する。
package.json
{"name":"ppt","version":"1.0.0","description":"","main":"index.js","scripts":{"start":"node app.js","test":"echo \"Error: no test specified\"&& exit 1"},"keywords":[],"author":"","license":"ISC"}
  • 現時点で、lsすると、package.jsonだけがあります。

  • 次に、install.shを現在のディレクトリに作成します。

  • touch install.sh とかで空ファイル作ってもいいし、エディタで作ってもいいですね。

  • install.shに以下コードを貼り付ける。 echoうるさめです。

install.sh
#!/bin/bashecho'Cloud Shell では、毎回以下のエラーが出る'echo'(node:791) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!'echo'/home/あなたのID/ppt/node_modules/puppeteer/.local-chromium/linux-722234/chrome-linux/chrome: error while loading shared libraries: libXss.so.1: cannot open shared object file: No such file or directory'echo'回避するために、以下のsudo apt-get install libxss1 を実行する'echo'面倒なので、いろいろまとめてこのシェルを実行する'echo-----------------echo-----------------echo-----------------echo npm install request
npm install request

echo-----------------echo-----------------echo-----------------echo npm install date-fns-timezone
npm install date-fns-timezone

echo-----------------echo-----------------echo-----------------echo npm install-g npm
npm install-g npm

echo-----------------echo-----------------echo-----------------echo npm install express puppeteer --save
npm install express puppeteer --saveecho-----------------echo-----------------echo-----------------echo sudo apt-get update 
sudo apt-get update

echo-----------------echo-----------------echo-----------------echo sudo apt-get install libxss1
sudo apt-get install libxss1

echo-----------------echo-----------------echo-----------------echo cd /home/あなたのID/ppt/node_modules/puppeteer/.local-chromium/linux-722234/chrome-linux
cd /home/あなたのID/ppt/node_modules/puppeteer/.local-chromium/linux-722234/chrome-linux

echo-----------------echo-----------------echo-----------------echo'ldd chrome | grep not'echo'libXss.so.1 => not found  <=
echo   '↑↑これがでないように sudo apt-get install libxss1 した'
echo   'だから、下の命令の結果は、何も表示されない'
ldd chrome | grep not

  • 実行は、 ./install.sh 
  • 実行できない時は、以下のコマンドを試すべし(実行権限追加)
  • sudo chmod +x install.sh
  • Croud Shellでは起動のたびに実行しないとエラーが発生したので、めんどくさくて作成した。
  • シェル実行後にlsすると、install.sh node_modules package.json package-lock.json がある。
  • 最後に、app.jsを作成し、以下の「puppeteerのソースコード」を貼り付ける。
  • 「puppeteerのソースコード」の、あなたのID、あなたのパスワード、証券会社のURL、GASで発行したURLを自分の物に書き換えれば、動くはず。
  • GASで発行したURLは、後ほど出てきます。
  • 証券会社名は、ヒントが有るのですぐにわかりますよね。

puppeteerのソースコード
app.js
//Node.js//var、let、constの使い方が適当なので、ええ感じに書き換えてね!asyncfunctionrun(){constuser_id='';//s??証券のあなたのIDconstuser_pass='';//s??証券のあなたのパスワードconstshouken_url='';//証券会社のURL(s??証券)constspreadsheet_url='';//GASで発行したURLconstpptr=require('puppeteer');varnow1=newDate();constouttime=80000;constwaittime=8000;// ブラウザを起動するconstbrowser=awaitpptr.launch({// headless: false,//GAEにデプロイする時、CloudShellではコメントにして、ブラウザ非表示にする。ローカルテストの時はコメント外して、ブラウザの動きを確認してデバッグする。// 目視確認用に操作遅延(ms)これを入れておくと、間違いが少ない"slowMo":10,args:[// デフォルトでは言語設定が英語なので日本語に変更'--lang=ja,en-US,en',// Chromeウィンドウのサイズ'--window-size=1200,800',// Chromeウィンドウのポジション'--window-position=10,10','--no-sandbox',]})try{// ページつくるconstpage=awaitbrowser.newPage()page.setDefaultNavigationTimeout('120000')awaitpage.setViewport({width:1200,height:800})letnavigationPromise=page.waitForNavigation()awaitpage.goto(shouken_url,{timeout:outtime})console.log('証券会社 WEBサイトサイトへアクセス')awaitpage.waitForSelector('.sb-box-sub-02-content ',{visible:true,timeout:outtime})awaitpage.waitForSelector('.sb-box-sub-02-content > dl > dd > #user_input > input',{visible:true})awaitpage.click('.sb-box-sub-02-content > dl > dd > #user_input > input')awaitpage.type('.sb-box-sub-02-content > dl > dd > #user_input > input',user_id)awaitpage.waitForSelector('.sb-box-sub-02-content > dl > dd > #password_input > input')awaitpage.click('.sb-box-sub-02-content > dl > dd > #password_input > input')awaitpage.type('.sb-box-sub-02-content > dl > dd > #password_input > input',user_pass)awaitpage.waitForSelector('.sb-box-sub-02-inner > .sb-box-sub-02-content > .sb-position-c > a > .ov')awaitpage.click('.sb-box-sub-02-inner > .sb-box-sub-02-content > .sb-position-c > a > .ov',{timeout:outtime})console.log('クリック:ログイン')awaitnavigationPromisenavigationPromise=page.waitForNavigation()awaitpage.waitForSelector('#navi01P',{timeout:outtime})awaitpage.click('#navi01P > ul > li:nth-child(8) > a > img',{timeout:outtime})console.log('クリック:先物・オプション')awaitnavigationPromise//参考記事//Puppeteerで次ページへの遷移を待つ (別タブや別ウインドウの遷移を待つ)//https://qiita.com/hnw/items/a07e6b88d95d1656e02fconstnewPagePromise=newPromise(resolve=>browser.once('targetcreated',target=>resolve(target.page())));console.log('waitFor  : '+waittime)awaitpage.waitFor(waittime)awaitpage.waitForSelector('.md-t-box-btn-01-inner > .floatR > p > a > img')page.click('.md-t-box-btn-01-inner > .floatR > p > a > img',{timeout:outtime})console.log('クリック:先物・オプション取引サイト')constnewPage=awaitnewPagePromise;awaitnewPage.setViewport({width:1200,height:800})//newPage.waitFor(waittime)しないと、frameだけしかない状態で次の処理をしてエラーになるのを防ぐため。//もっとカッコいい方法があるはず!!console.log('waitFor  : '+waittime)awaitnewPage.waitFor(waittime)awaitnewPage.waitFor('frameset > frame',{timeout:outtime});//先物・オプション取引サイトは、frameset > frame > frameset > frameの構造//Frameの位置を正しく認識させる//画面上メニューの「取引」をクリックしたい//トップframeset下の0番目のframeを選し、その下のframeset下の1番目のframeを選択する//具体的にはココ!letframe_menu=awaitnewPage.frames()[0].childFrames()[0].childFrames()[1]//frameが選択できたので、idの#menu20をクリック//idのみでクリックできる場合は、これで良いframe_menu.click('#menu20')console.log('クリック:取引')awaitnewPage.waitFor('frameset > frame',{timeout:outtime});console.log('waitFor  : '+waittime)awaitnewPage.waitFor(waittime)//取引−オプション新規注文 をクリックawaitclick_submenu(newPage,outtime,waittime);//取引−オプション新規注文画面にいる状態//取引−オプション新規注文画面のframeに移動した状態letframe_main=awaitnewPage.frames()[0].childFrames()[2].childFrames()[1]//「20000より上」「17000より下」のボタンが有るか確認するletpath_down='/html/body/div[1]/table/tbody/tr/td[3]/span/span/form/table[2]/tbody/tr[2]/td/table/tbody/tr/td/table[1]/tbody/tr[4]/td[1]/input'letpath_up='/html/body/div[1]/table/tbody/tr/td[3]/span/span/form/table[2]/tbody/tr[2]/td/table/tbody/tr/td/table[1]/tbody/tr[2]/td[2]/input'letlink_down=awaitframe_main.$x(path_down)letlink_up=awaitframe_main.$x(path_up)varymd=awaityymmdd();if(link_up.length!=0){//「20000より上」があれば、上ページのデータを取得awaitclick_next(newPage,path_up,outtime,waittime);//画面に表示されるデータを取得するlettextdata=awaitgetTable(newPage,ymd);//取得したデータをスプレッドシートへ書きこむawaitpostSpreadsheet(textdata,spreadsheet_url);}//取引−オプション新規注文 をクリック//最初に開く画面へ移動awaitclick_submenu(newPage,outtime,waittime);//画面に表示されるデータを取得するlettextdata=awaitgetTable(newPage,ymd);//取得したデータをスプレッドシートへ書きこむawaitpostSpreadsheet(textdata,spreadsheet_url);if(link_down.length!=0){awaitclick_next(newPage,path_down,outtime,waittime);lettextdata=awaitgetTable(newPage,ymd);awaitpostSpreadsheet(textdata,spreadsheet_url);}varnow2=newDate();varnow11=now1.getMinutes()*60+now1.getSeconds()varnow21=now2.getMinutes()*60+now2.getSeconds()console.log('time : '+(now21-now11));awaitbrowser.close()console.log('Exit')console.log('Exit')}catch(e){//失敗時、すぐに止めたい。GAEのインスタンスを無駄に使わないawaitbrowser.close()console.log('ERR')console.log('ERR')console.log(e)logError(e)}//try {}//async function run(){asyncfunctionclick_submenu(newPage,outtime,waittime){letframe_menu2=awaitnewPage.frames()[0].childFrames()[0].childFrames()[1]letframe_submenu=awaitframe_menu2.$x('/html/body/form/div/table/tbody/tr/td/table[2]/tbody/tr/td[2]/span/a[2]')//                  ↑await これ忘れがち!! 忘れるとエラーになる。何度も失敗した!!awaitframe_submenu[0].click();console.log('クリック:取引 > オプション新規注文')awaitnewPage.waitFor('frameset > frame',{timeout:outtime});console.log('waitFor  : '+waittime)awaitnewPage.waitFor(waittime)}asyncfunctionclick_next(newPage,path,outtime,waittime){console.log('mainコンテンツのframe取得_click_next')letframe_main2=awaitnewPage.frames()[0].childFrames()[2].childFrames()[1]//上へor下へのボタンへのパスframe_submenu=awaitframe_main2.$x(path)console.log('クリック:次ページ')awaitframe_submenu[0].click();awaitnewPage.waitFor('frameset > frame',{timeout:outtime});console.log('waitFor  : '+waittime)awaitnewPage.waitFor(waittime)}asyncfunctiongetTable(newPage,ymd){//参考//puppeteerでの要素の取得方法//page.$のところ//https://qiita.com/go_sagawa/items/85f97deab7ccfdce53ea//日経平均、日経225先物期近 取得//日経平均、日経225先物期近、などが並ぶframeへ移動console.log('frame取得    toolBarコンテンツ')letframe_toolBar=awaitnewPage.frames()[0].childFrames()[1].childFrames()[1]//xpathのフルパスでテーブルタグのtrを指定する(IDで上手く取れない場合は、これが簡単)console.log('テーブルタグ取得 日経平均、日経225先物期近');lettbla=awaitframe_toolBar.$x('/html/body/table/tbody/tr/td/table/tbody/tr[2]')console.log('データ取得    日経平均、日経225先物期近');vartxtNikkei='';for(leti=0;i<tbla.length;i++){lettr=await(awaittbla[i].getProperty('innerHTML')).jsonValue();lettr1=tr.split('<')letdatas=[];//console.log(tr1)    for(letiintr1){//i=2は、日経平均 i=12は、日経225先物期近if(i==2||i==12){letinner=tr1[i].split('>')[1]if(inner!=''&&inner!=null&&inner!='\n'&&inner!='\n'){//上のconsole.log(tr1)で、発見ーーーーー↑↑↑↑↑↑↑↑↑↑↑↑↑↑            ↑↑↑↑↑↑↑↑↑↑↑↑ if(inner.toString().split(',').length>0){letsplitdata=inner.toString().split(',')if(splitdata[1]!=''&&splitdata[1]!=null){datas.push(splitdata[0]+splitdata[1]);//console.log('i = ' + i + ' ' + splitdata[0] + splitdata[1]);}else{datas.push(splitdata[0]);//console.log('i = ' + i + ' ' + splitdata[0]);}}}else{datas.push('-1');console.log('i = '+i+'日経平均のデータが空');}}}txtNikkei+=datas.toString();}//SQ日 取得console.log('frame取得    mainコンテンツ')letframe_main=awaitnewPage.frames()[0].childFrames()[2].childFrames()[1]console.log('テーブルタグ取得 SQ日');letsq=awaitframe_main.$x('/html/body/div[1]/table/tbody/tr/td[3]/span/span/form/table[2]/tbody/tr[2]/td/table/tbody/tr/td/table[2]/tbody/tr/td/table/tbody/tr[2]/td[2]/table/tbody/tr/td[2]')console.log('データ取得    SQ日');varSQ=await(awaitsq[0].getProperty('innerHTML')).jsonValue();//日経225オプションのデータ 取得 console.log('テーブルタグ取得 日経225オプション');lettbl1=awaitframe_main.$x('/html/body/div[1]/table/tbody/tr/td[3]/span/span/form/table[2]/tbody/tr[2]/td/table/tbody/tr/td/table[1]/tbody/tr[3]/td/table/tbody/tr[2]/td[2]/table/tbody/tr')console.log('データ取得    日経225オプション');vartextdata='';for(leti=0;i<tbl1.length;i++){vartr=await(awaittbl1[i].getProperty('innerHTML')).jsonValue();vartr1=tr.split('<')vardatas=[];for(letiintr1){letinner=tr1[i].split('>')[1]if(inner!=''&&inner!=null&&inner!='新規買'&&inner!='新規売'){datas.push(inner);}}textdata+=ymd+','+datas.toString()+','+txtNikkei+','+SQ+',\n';}console.log(textdata)returntextdata;}asyncfunctionpostSpreadsheet(textdata,spreadsheet_url){//参考 curlコマンドをPythonやnode.jsのコードに変換する方法//https://qiita.com/tottu22/items/9112d30588f0339faf9bvarrequest=require('request');varheaders={'Content-Type':'text/csv'};vardataString=textdata;varoptions={url:spreadsheet_url,method:'POST',headers:headers,body:dataString};functioncallback(error,response,body){if(!error){console.log(' OK post Google Spreadsheet');console.log(' OK statusCode :  '+response.statusCode);console.log(' OK result     :  '+body);}else{console.log(' NG post Google Spreadsheet');console.log(' NG statusCode : '+response.statusCode);console.log(' NG err        : '+error);}}request(options,callback);/*
    デプロイするまでは、curlでやっていたが、GAEでファイル書込は不可だったので、上記にした。
    GASのテストをする分には、curlが簡単で良かったかも。
    const exec = require('child_process').exec;
    exec('curl -v -H "Content-Type: text/csv" -X POST -d @./textdata.csv ' + spreadsheet_url
        , (err, stdout, stderr) => {
        if (err) { console.log(err); };
        console.log(stdout);
    });

    */};asyncfunctionyymmdd(param){//AppEngineは、UTCなのでAsia/Tokyoへタイムゾーンを変更//node.jsでタイムゾーンの変換処理にdate-fns-timezoneを利用する//https://qiita.com/kazuhiro1982/items/b1235a893ee874d8ff65const{startOfDay,addDays}=require('date-fns');const{convertToTimeZone}=require('date-fns-timezone');// タイムゾーン定義consttimeZone="Asia/Tokyo";// 現在時刻(UTC)を取得consttargetDate=newDate();// TimeZone付きDateに変換constnow=convertToTimeZone(targetDate,{timeZone:timeZone});varyear=now.getYear();// 年varmonth=now.getMonth()+1;// 月varday=now.getDate();// 日varhour=now.getHours();// 時varmin=now.getMinutes();// 分varsec=now.getSeconds();// 秒vardayOfWeek=now.getDay();//曜日 [ "日", "月", "火", "水", "木", "金", "土" ]if(year<2000){year+=1900;}if(month<10){month='0'+month}if(day<10){day='0'+day}if(hour<10){hour='0'+hour}if(min<10){min='0'+min}if(sec<10){sec='0'+sec}varymd=year+'/'+month+'/'+day+''+hour+':'+min+':'+sec;if(param=='week'){returndayOfWeek;}elseif(param=='hour'){returnhour;}else{returnymd;}};asyncfunctionrun2(){varweek=awaityymmdd('week');varhour=awaityymmdd('hour');if(week>=2&&week<=5){//火〜金[ "日", "月", "火", "水", "木", "金", "土" ]console.log('Start Job week:'+week+' hour:'+hour);run();}elseif(week==1&&hour>=7){//月曜かつ7時以上console.log('Start Job week:'+week+' hour:'+hour);run();}elseif(week==6&&hour<=7){//土曜かつ7時以下console.log('Start Job week:'+week+' hour:'+hour);run();}else{console.log('No Start  week:'+week+' hour:'+hour);}}run2();

puppeteerのソースコードの説明
  • 説明1
  • 以下の4ヵ所を書き換える
  • const user_id = '';//S??証券のあなたのID
  • const user_pass = '';//S??証券のあなたのパスワード
  • const shouken_url = '';//証券会社のURL(S??証券)
  • const spreadsheet_url = '';//GASで発行したURL(Google Apps Scriptの公開方法で、説明が出てきます。)

  • 説明2

    • // headless: false,//GAEにデプロイする時、CloudShellではコメントにして、ブラウザ非表示にする。
    • headless: false を有効にすると、ブラウザが表示されます。ローカルの開発環境で作業中の際は、こちらの方がデバッグしやすいです。
  • 説明3(frameのたどり方が分からなくて一番苦労したところ!)

    • //先物・オプション取引サイトは、frameset > frame > frameset > frameの構造
    • //Frameの位置を正しく認識させる
    • 証券会社_Frame構造01.JPG
  • 説明4(XPathが分かりにくくて苦労したところ!)

    • XPathを活用すると、ID等が簡単に指定できますが、そのままでは動かない場合や、うまくいかないことが多かった。
    • そのため、Chromeの「デベロッパーツール」で、ソースのたどりたいタグを右クリックして、Copy → Copy full XPath で full XPathを使っています。
  • 説明5

    • この時点で、app.jsが動くので、以下のコマンドで実行してみる
    • npm start  または、 node app.js
  • その他 
     - 参考にさせてもらったサイトは、ソースのコメントや、最後に記載してます。

Google Apps Scriptのソースコードとコメント
doPost(e)
functiondoPost(e){varcsvText=e.postData.getDataAsString();//テキストファイルにする(CSVになってる)Logger.log("csv : "+csvText);varspreadsheet=SpreadsheetApp.openById('');//書込先のスプレッドシートのIDvarsheet=spreadsheet.getSheetByName('シート1');//スプレッドシートのシート名vararr=csvText.split(',');//CSVをカンマでsplitする。vararrData=sheet.getDataRange().getValues();//現在スプレッドシートに入ってるデータを全部取得して、2次元配列にする。varlen_arrData=arrData.length;//現在スプレッドシートに入ってるデータが、何行あるか確認してるvarj=0;varsheet_cols=19;//GAEで取得したデータの1行当たりの列数が19なので、19としてる。/*
  (arr.length -1) の意味:GAE上で改行コードを入れたが、改行コードをうまく認識できないので、全部で
          配列がいくつ有るか数えて、最後に1引く(1引くのは、GAE上最後のデータの後にカンマを入れたから)
  (arr.length -1)は、19の倍数になってるので、19(sheet_cols)で割って、何行あるかを取得する。
  */varcols_arr=(arr.length-1)/sheet_cols;for(vari=0;i<cols_arr;i++){arrData.push([arr[j],arr[j+1],arr[j+2],arr[j+3],arr[j+4],arr[j+5],arr[j+6],arr[j+7],arr[j+8],arr[j+9],arr[j+10],arr[j+11],arr[j+12],arr[j+13],arr[j+14],arr[j+15],arr[j+16],arr[j+17],arr[j+18]]);j=j+sheet_cols;};/*
  もともと入っていた配列データを消して、上のpushで追加した行だけのデータにする。
  */for(vari=0;i<len_arrData;i++){arrData.shift();//配列の上からデータを削除}varrows=arrData.length;//行数の確認varcols=arrData[0].length;//列数の確認/*
  (len_arrData + 1) :今取得したデータを書き込む行番号を決める。元々スプレッドシートに入ってるデータの行数+1で最終行の次の行に書き込む。
  1                 :1列目(A列)に書く
  rows              :今回追加する行数
  cols              :今回追加する列数(19)
  arrData           :今回追加するデータの配列
  */sheet.getRange((len_arrData+1),1,rows,cols).setValues(arrData);// 結果を返す  varoutput=ContentService.createTextOutput();output.setMimeType(ContentService.MimeType.JSON);output.setContent(JSON.stringify({message:"success!"}));returnoutput;}

Google Apps Scriptの公開方法
  • メニューバー => 公開 => ウェブアプリケーションとして導入
  • Current web app URL: このURLを、GAE側で利用する。 => const spreadsheet_url = '';//GASで発行したURL
  • Project version: 更新の都度、上げていくと、何回デプロイしたか分かって楽しい
  • Execute the app as: Meにする
  • Who has access to the app: Anyone,even anonymousにする
  • 「更新」ボタンをクリック

スプレッドシートの状態

GAE用 app.yaml
  • app.yamlは、最初に作成したディレクトリ内に作成します。
app.yaml
runtime:nodejs10instance_class:F4_1G#puppeteer利用のためhandlers:-url :/script :autosecure :always#このあたりに、adminと書けば、自分しか使えないサイトになったと思うが、エラーになる。

GAE用 cron.yaml
  • cron.yamlは、最初に作成したディレクトリ内に作成します。
cron.yaml
cron:-description:"PJ1"url:/schedule:every  5 minutes from 08:50 to 12:00timezone:Asia/Tokyo

GAE用 デプロイ方法
  • 最初に作成したディレクトリ内で、実行します。
  • あなたのプロジェクトIDが、PJ1の場合。
  • --quiet を入れない場合、確認があります。
  • シェルで実行する場合は、--quietが便利
  • デプロイが失敗しまくる時は、以下2つのファイルを消しに行くべし
  • 1. AppEngineのバージョン画面の最新情報以外を全部消す
  • 2. Strageの「staging.〜」「us.artifacts.〜」をクリックして出てきたファイルを全部消す。 CloudBuildのファイルが溜まってるから?
gcloud app deploy --quiet--project PJ1
gcloud app deploy cron.yaml --quiet--project PJ1

GAEを無料で使うテクニック(ってほどでもないか?)
  • Google App Engine は、もともと無料枠がけっこう大きい

    • 課金しない状態で、以下のように運用可能です。
    • 28CPU時間を毎日利用しても、無料です。
    • 28CPU時間を使い切ると、システムが勝手に止まります。
    • システムがリセットされて再び動き出すのは、16時。
  • Google App Engine は、課金してなくても動くが、デプロイができない。

    • デプロイには、Cloud Buildが必要で、Cloud Buildに課金が必要
    • だから、デプロイするときだけ、Cloud Buildを課金状態にする。 デプロイ終わったら忘れず課金を消す。
    • この操作をすれば、無料で運用可能。
    • 課金状態で、1日に28CPU時間以上動かしてしまうと、課金されます。
  • もっとGAEを使いたい場合

    • app.yamlの設定で、instance_class: F4_1G と設定したので、CPU時間をかなり消費する。
    • 5分に1回の実行を、3時間回すと、12x3=36回程度で、28CPU時間を消費します。
    • 9時から12時で終わります。 足りませんよね。
    • そこで、プロジェクトIDを複数作成します。例PJ1 PJ2等。そして以下のように設定します。
    • cron.yamlのPJ1用には、schedule: every 5 minutes from 08:50 to 12:00
    • cron.yamlのPJ2用には、schedule: every 5 minutes from 12:05 to 15:20
    • 注意点:cron.yamlをデプロイするだけの場合は、Cloud Buildの課金が不要なので、課金作業無しで、デプロイ可能です。
    • しかし、PJ1 PJ2 を課金対象にした上でデプロイした後、課金をやめないと、誤って課金される場合があるので注意要です。

参考にさせて頂いた資料


Viewing all articles
Browse latest Browse all 9134

Trending Articles