GASによるtwitter投稿、1-legedバージョン

この辺の話の続き
miz999.hatenablog.com

ようやく認証画面のないtwitter投稿(いわゆる1-leged)が上手く行ったのでソースに起こしてみる

今まではgistにペタペタ貼ってましたが、管理が面倒なのでgithubに正式ではないがforkしました

github.com

詳細はREADMEで

Google Apps Scriptのjavascriptがスクレイピング言語として優れてる点

スクレイピングとはすなわち、他のサイトからファイルを持ってきて、それに何らかの処理を行うわけで、それを他のjavascriptで行うと、例えばnode.jsだと

function scrape_nantoka(url) {
    return new Promise((resolve, reject) => {
        var client = http.get(url_rank, function (res) {
            var data = ''
            if (res.statusCode === 200) {
                res.setEncoding('binary');
                res.on('data', function (chunk) {
                    data += chunk
                })
                res.on('end', function () {
                    resolve(url)
                })
            }
            else {
                reject();
            }
        })
        client.on('error', function (e) {
            console.log('problem with request: ' + e.message);
        })
    });
}

こんな感じになるわけで(コードは適当)、「データを取ってきて処理する」という作業の都合上、callbackで受けてそこから処理関数を呼ぶか、上記のソースにちょっと入ってるPromiseで受けて、awaitで待つとかなってるわけです。これはプレーンなjavascriptxmlHttpRequestでも同じ構造です。

「await, asyncのおかげで格段に同期処理が便利になった」というのがjavascript界の空気なのですが、あくまでasync配下の処理が同期になるだけで、例えば、5ページ分スクレイピングして、それに対して処理を行うとなると、その5ページ分をawaitしてやることになり、処理はやっぱり複雑になってしまいます。

さらに言えば、仮に並列に走っていい作業(DBにガンガン放り込むとか)だとしても、特定のサイトに対し力いっぱいアクセスすると迷惑あるいは逮捕の可能性もあるわけです。そうならないための作法としてsleepを入れてやるのですが、javascriptにはsleepがありません。で、setTimeoutを使うわけなのだが、そこでもやっぱりコールバック地獄か、await地獄になるわけです。

ここまで話してきて、そもそも論で言えば、javascriptスクレイピングをやるというのは無茶というか根本的に向いてないのです。止めてはいけないブラウザのための言語であるjavascriptであり、参考例として出したnode.jsのソースもそもそも論で言えばサーバサイド言語なわけで、何もかも間違ってるわけです。

そこで、GASの登場になるわけです。 GASの素晴らしいところは、ネットワーク越しのファイル取得関数 UrlFeftchApp.fetch() が同期関数になっているところです。 ちなみに今どきSHIFT-JISとか使ってるサイトのために文字コードを変えたかったら UrlFetchApp.fetch().getContentText("Shift_JIS") とかやります。 この「同期関数が必要」という哲学を感じるのはちゃんとsleep(Utilities.sleep(msec))が用意されてるところです。

正しい使いやすいクライアントサイドのjavascriptとしてgoogleはこのjavascriptを単体でリリースするべきだと思います。 あるいは他の実装系がもっと同期処理を考えるべきだと。

このページは大変に参考になるので必読です。 qiita.com

Google Apps Scriptからtwitter投稿するちょっとした面倒くささ その3

一応目的は達成できたのだが、認証画面の無いOauthをちょっと追ってみる。
やりたいのはこれ
Single user OAuth with examples — Twitter Developers

件のOauthのライブラリの中に .setAccessToken()という関数がある

/**
 * Sets the access token and token secret to use (optional). For use with APIs
 * that support a 1-legged flow where no user interaction is required.
 * @param {string} token The access token.
 * @param {string} secret The token secret.
 * @return {Service_} This service, for chaining.
 */
Service_.prototype.setAccessToken = function(token, secret) {
  this.saveToken_({
    public: token,
    secret: secret,
    type: 'access'
  });
  return this;
};

本来は認証画面で認証したあとに取得したtokenをセットする関数なのだが、コメント読むと'1-legged'と書いてあり、要するにやり取りが一回のパターン(tokenをもらわないパターン)であろう。

で、これでsetAccessToken(AccessToken, AccessTokenSecret)して動かしたが

Invalid or expired token. (code: 89)

が返ってきてしまう。何かがおかしいのか、そもそも間違っているのか。

Single user OAuthできているpython-twitterの方もソースを追ってみるが、結構入り組んだ構造になっていて面倒になった。

というわけで、心が折れたので誰か何かやってくれないだろうか。ライブラリ書いてるGoogleの中の人とか。

Google Apps Scriptからtwitter投稿するちょっとした面倒くささ その2

昨日に続いてGASのTwitter投稿スクリプトをいじっている

くだんの投稿スクリプトを扱ってるページを見ると、使うライブラリのバージョンを12以下に推奨してる。最新の15だと動かないようだ。
これは要するに廃止されたsetProjectKey()という関数を使ってるようで、それを使わないようにするだけである。
元のgistのソースのsetProjectKey()を使っているたった一行をコメントアウトしてもいいのだが、いちおうfork機能があるようなので使ってみる。
これで最新のバージョン15が使えるが、まだ問題がある。
callbackのURLにも使ってる「プロジェクトキー」はサポート終了で、これからは「スクリプトID」を使うようになる。
スクリプトIDはプロジェクトキー同様にプロジェクトのプロパティのページで見れる。
よって、今までtwitterのサイトのコールバックに登録していたURLもプロジェクトキーの部分をスクリプトIDを使うようにする。

https://script.google.com/macros/d/[GASのスクリプトID]/usercallback

それらの修正を入れたのがこちら
https://gist.github.com/miz999/c83b5de622fc510918b4a7c1f4ac45bf


で、バージョン15を使いたかった理由があって、バージョン14でURL encodingに関するバグフィクスが入っている。
バージョン12を使ってるときにこのバグに当たったようで、ツイート文の中に'='があるとそれをセパレータとして処理してしまう。
最新バージョンを入れるとこの障害が無くなる。

Google Apps Scriptからtwitter投稿するちょっとした面倒くささ

今まではpythontwitter投稿してて、いわゆるBOTみたいなものを作ってたのですが、その時の作法としてはtwitterの開発者ページから
ConsumerKey = "”
ConsumerSecret = ""
AccessToken = ""
AccessTokenSecret = ""
を取得して、それをソースに書き込めばOKという感じでした。

ところが今回、GASから同じことをやろうといろいろ調べてそのとおりにやってみると、と認証画面(許可しますか?みたいなアレ)が出ました。

このページのように同じグーグル謹製のライブラリを使っているようです
qiita.com

pythonでやっていた時には出なかったのに何故?」と調べてみると、どうやらAccessTokenらを開発者ページから取得する手法は"Single-user OAuth"と言うらしく、twitter以外ではあまり見ない手法のようです

Single user OAuth with examples — Twitter Developers

なんかOauthの仕組みの中にあるような無いような感じなのですが、Oauthが色々ありすぎるのとOauthがなんか複雑(上記リンクの中でも"By using a single access token, it is not necessary to implement the entire OAuth token acquisition dance."と表現されてるくらい)なので、ぼんやりした理解になりました。

pythonでやってたことをGASでもできないだろうかと、python-twitterのソースや上記のライブラリを見てたのですが、python-twitterの方はOath側のソースも相当追わなければならない感じだし、GASの方はその構造がない感じなので面倒になりました。
GitHub - bear/python-twitter: A Python wrapper around the Twitter API.

そもそも論で言えば、上記のTwitterのSingle-user OAuthのページ
に呼び出し側のサンプルコードはあるのに、どういう認証なのかさっぱりわかりません。

というわけで、GASで認証画面でピコピコやることにしました。

私はOauthが苦手です。できれば関わり合わずに生きていきたいです。

sshログインから見たGoogle cloud shellとGoogle Compute Engine(GCE)の違い

Google cloud platform には Google cloud shellというサービスがある
cloud.google.com
説明は上記公式サイトに頼るが、要はsshログインできるLinux環境をgoogleが無料で提供してくれるというサービスである。ブラウザ上で動くエディタも提供されるので、これひとつでリモートで開発できるという便利なものである。

さて、これとは似たようなものであるが「Google Compute Engine(GCE)のVMインスタンへのsshログイン」というサービスもある。同じような環境であるが、これは有料である。課金はVMインスタンスへのものと同様(あるいはプラス)で通信にもCPUにもかかる。
分かりづらいのは、GCEの最低環境が実質無料になり、24時間ログインしっぱなしでも無料の設定ができることである。
結局の所もどちらもIaaS(VPS?)へのログインであはるのだが、この「どっちも無料」のせいで「どっちでもいいんじゃね?」あるいは「どっちを使ってるか意識しない」事情が発生していると思う。

ちなみに起動方法も違うので画像で説明するとこうなる
f:id:miz999:20180426194813p:plain

Google cloud shellのメリット

  • 完全無料
  • google cloud platform関連のツール(gcloucdとか)がプリインストール済み
  • GUIのエディタも使える(viやemacsに慣れてない人には朗報)
  • ちなみにuname -aは Linux cs-6000-devshell-vm-3f06ec54-2b97-418f-a525-f45bc6eac544 3.16.0-5-amd64 #1 SMP Debian 3.16.51-3+deb8u1 (2018-01-08) x86_64 GNU/Linux

開発イメージ
f:id:miz999:20180426203320p:plain
上半分はエディタのゾーン

Google Compute Engine(GCE)のメリット

  • OSを選べる(debianやらFreeBSDも)
  • メモリ、ディスク等の環境構築が柔軟(追加課金がいるが)
  • ssh鍵が分かり、ログイン先のIPアドレスが分かれば、teratermやJuiceSSH(Androidsshアプリ)を使ってのログインも可能
  • Google製のAndroidアプリのCloud Consoleからもターミナルが開いてログインできる(この場合、鍵やIPアドレスはいらない)

開発イメージ
f:id:miz999:20180426203410p:plain
厳密にはこのキャプチャはgloudからのputty起動なのだが、こんな感じということで

これだけ見るとGCE使えばいいじゃんと思われるだろうが、本当にそうなのだ。
あえてGCEのデメリットは

  • 下手な操作をして大量の通信(中国への大量の送信など)を発生させてしまうと青天井課金もありえる
  • 上記も踏まえて、ハッキングされた時のリスクが面倒
  • OSからの管理なので、aptのアップグレードが結構ある。Ubuntuは半年に一度の大型アップグレードも。逆にこれはガチガチに環境を固められてるGoogle cloud shellにとってデメリットとも言える。面倒な自由と管理された不自由問題。

課金のことを考えるとgoogle cloud shellの方がいい気もするが、古くからのsshでターミナルログインの人なら、使い慣れてるターミナルソフト使えたほうがいいし、とにかくブラウザ上でのターミナル操作は重い。
他のGCE上のインスタンスにデプロイする場合どうなるかとか見通しの問題もあるのでcloud shellを頂点としてぶらさがるGCEという環境のほうが見通しがいい気もするが。

とにかくどちらがどうとか説明しづらい。cloud shellが鍵とIPアドレスでログインできるようになってくれると嬉しいのだが。

Google Cloud Platform関連はとにかく日本語の情報が少ないのでなにか大きく間違ってる気もするが、それが間違ってるかどうかも気づかずに流れていきそうな

ヤフオクのAPIがイマイチ信用できないものだったことに今更気づく

ヤフオクAPIサービスが終了することを以前に知ったのだが
miz999.hatenablog.com

で、ロウソクの炎がどうたらみたいに今更しっかり使っていると、APIがあまりうまく動いてないことに気づく。

たとえば、あるカテゴリーの全件取得(関係ないが入札順)のURLはこんな感じ

http://auctions.yahooapis.jp/AuctionWebService/V2/categoryLeaf?sort=bids&output=xml&appid=(隠す)&category=2084053583

で、xmlの一部がこれ

<ResultSet xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:jp:auc:categoryLeaf" xsi:schemaLocation="urn:yahoo:jp:auc:categoryLeaf https://auctions.yahooapis.jp/AuctionWebService/V2/categoryLeaf.xsd" totalResultsAvailable="290" totalResultsReturned="20" firstResultPosition="1">

totalResultsAvailableの結果から、このカテゴリーに290件あるように見える

が、これを本来のwebページで見ると
https://screenshotscdn.firefoxusercontent.com/images/1f2f81c5-d062-4efc-b6f7-d7c705c5d4ea.png

2165件あることが分かり、APIだと10分の1程度しか取得できてないような気がする。
(ちなみに初めて、firefoxスクリーンショットサービスを使ってみた。はてなスクリーンショットアドオンがほぼ死んでるので乗り換えは決定的かな)

今更ではあるが、こういう不具合があったからサービスを停止するのかもしれない。

ヤフオクは10年以上に渡るサービスの建て増しの結果、増築を続けた温泉旅館のようにグチャグチャになっていて、ソースに手を入れられなくなってるという噂を聞いたことがあるので、その辺もあるのかなと思ったり思わなかったり。

まぁ単なる勘違い、仕様書の読み込みミスだったりもするかもしれないが、全てはあと数日で終わりである。

次は一覧はRSSと個別商品はスクレイピングでやる予定