河東セミナーニュース bot の開発

更新日時:

河東セミナーニュース bot @kawahigashinews を現在稼働しています。

勝手に発案、開発、運用しているものですが、早速先生に発見されました。以下のようにツイートします。

作成の目的

河東セミナーニュースは、河東教授(以下河東先生と書くかもしれない)の Twitter みたいなものである。内容は大きく次の 2 つである。

  • ご自身の居場所を呟く(講演で世界中を飛び回っているのだなぁといつも思っている)
  • ご自身の学生や、関係する研究者の動向について(例えば受賞、講演)

これはもう Twitter でやれよ と思ってしまう内容である。 河東セミナーニュースの更新があったら Twitter に自動投稿できれば、特に作用素環論の人にとっては最高なのでは? と思ったのがきっかけである。

同じようなことをやっている教員は、本学以外にも他にもいる。その中でも河東セミナーニュースに注目した理由は、 めでたいと思う内容だけお書きになりそうだから である。河東教授は最高レベルに established された方なので、人を貶めることを書く意味がないだろう。私は教授が人を推していくのは、基本的には、良いことだと思っている。この bot を作成すると、例えば「誰々さんがなんとか賞を受賞しました」というニュースが Twitter 上で関係者に ReTweet され共有されることになるだろう。それは、とても喜ばしいことだと思う。

ということで 勝手に やることにした。怒られたらやめます。

免責事項

  • 河東セミナーニュースのエンコーディングは EUC-JP であるが、 parse する段階で Ruby で UTF-8 に変換して文字列を処理している。そもそも現代の文字列の標準は UTF-8 以外ありえないし、一部の gem に文字列を渡す際に UTF-8 が要求されるからである。そのため 最終出力が文字化けすることを完全に避けることはできない 。実際 5 年分テストして 1 箇所文字化けし、どうしようもなかったものもある。もしかしたら(大変失礼であるものの)人名で発生してしまうかもしれない。そうなってら該当者の方には申し訳がない。こういうことが将来も発生する可能性はあるが、 to my best knowledge では、仕方がないと言える。あらかじめご了承いただきたい。
  • 最近の Twitter 社は spam アカウントに対して非常に厳しい。 YouTube も同じであるが、 「 spam に対処するためには、善良なユーザーが少々犠牲になるのは仕方がない」 というスタンスに移行している。特に 2018 年 8 月以降は API を利用することすら非常に厳しく、私が Twitter Developer 申請する際も非常に難儀した事実がある。さてこの bot であるが、その性質上 URL を多く貼ることになる。もしかしたら spam 認定を受け、凍結されるかもしれない。そこで 2019 年 2 月 17 日までは仮稼働という位置付けとする 。これはバグ取りのために本番環境で 1 ヶ月テストするという意味でもあるし、 Twitter 社に突然止められないか見守っているということでもある。「意義のある bot を作ったけれども凍結されました」は現在の Twitter 社の動向だと十分あり得るので、仮稼働中はそのつもりでご覧いただきたい。
    • (追記 2018/02/17) 本日まで 1 ヶ月仮稼働させましたが、ログを見る限り極めて正常に動作しておりました。そこで今後は本稼働といたします。

仕様について

私は最高レベルに日本の大学業界に向いていない人間だが(だからこそこんなものを勝手に作ろうと思えるのである)、それでも教授に迷惑をかけたくない。最大限河東先生に迷惑がかからないであろう仕様を考えることにした。

クロール後の処理

基本的には 10 分ごとに河東セミナーニュースをクロールし、ニュース部分に当たる部分を取り出し、新しいニュースがあったら Tweet する。ただしこれだけだと問題があって、 訂正を反映できない 。 Twitter は Tweet を修正ができない。河東先生が後から「これは書くべきじゃなかった、文言を訂正しよう」とニュース部分に変更を加えたとして、安直な仕様であると、その変更についていけない。これは先生にとっては懸念されるところであろうと思われる。

そこで次のようにすることにした。 10 分ごとのクロールをしたタイミングの処理を述べる。以下では集合に記号をおく。

$A = $ 河東セミナーニュースをクロールする直前の news の集合

$A’ = $ クロールした直後の news の集合

$B = $ その時点での @kawahigashinews の直近 100 件の tweet の集合1

$A, A’, B$ を取得後、以下の処理を順番に実行する。

  1. $A \setminus A’$ の元について、対応する $B$ の元が存在するならば、削除する(ツイ消しをする)。
  2. $A’ \setminus A$ の元について、対応する新規ツイートをする。

河東セミナーニュースを訂正した際は、 1. も 2. も実行されることになる。順序は担保されないものの、常に最新の記述が TL に並ぶことになる。 Twitter は文言を訂正できない以上、これが最善かと思う2

news から tweet への変換について

ソースコードをご覧いただくのが正確であるが、仕様の概略を記述する。

まず news を Nokogiri で判定する。 xpath で <p> を取り出し、0 文字目が があるかどうかでのみ判定する。 news であると判定したら、<p> の text を本文とし、冒頭の と、文中の改行コードと前後の空白を削除する。その後文末の日付を正規表現で取得し、日付を @date とする。そして文末の日付を本文から削除し、本文を @text とする。また本文に貼られたリンク先 URLs を Nokogiri で取得し、 @urls とし、別途管理する。 tweet する際は @date + @text + @urls を適宜整形しながら順に並べ、 tweet 用の本文とする。

以下、 2018 年のニュースを seed にした例を挙げる。

基本的には、日付が先に来て、文章が続き、 URL が続く。

<p><a href="http://www.icm2018.org/portal/en/home">ICM 2018</a>での講演を終えました.
講演者だけに見えるデジタルタイマーがありましたが,時間切れの3秒前に終わりました.
講演のPDFファイルは<a href="rio2018.pdf">こちら</a>にあります.
(8/3/2018)

08/03: ICM 2018での講演を終えました.講演者だけに見えるデジタルタイマーがありましたが,時間切れの3秒前に終わりました.講演のPDFファイルはこちらにあります. http://www.icm2018.org/portal/en/home http://www.ms.u-tokyo.ac.jp/~yasuyuki/rio2018.pdf

URL は href が相対 URL の場合は絶対 URL に直してあるし、 Amazon などで使われる日本語を含む URL にも(よほどエスケープに困らない限りは)対応しているはずである。

項目全体が Twitter の投稿限界文字数を超過する場合、本文を限界に収まるまで削る。

<p>・Springerの展示ブースに行ったら,展示されている本の中に<a href="https://www.springer.com/la/book/9783319143002">私の薄い本</a>がありました.Springer の知り合いにこれは私の本だと言ったら,私が来ることがわかっていたから展示商品の中に入れておいたのだと言われました.
Scholze の論文が載っている<a href="https://www.springer.com/mathematics/journal/11537">Japan. J. Math.</a>の号もたくさんありました.彼がFields賞を取る確信があったので,あらかじめ日本からたくさん送っておいたとのことです.彼がサインして明日来場者に配るそうです.
(8/1/2018)

08/01: Springerの展示ブースに行ったら,展示されている本の中に私の薄い本がありました.Springer の知り合いにこれは私の本だと言ったら,私が来ることがわかっていたから展示商品の中に入れておいたのだと言われました.Scholze の論文が載って… https://www.springer.com/la/book/9783319143002 https://www.springer.com/mathematics/journal/11537

URL は先頭から最大 4 つまで載せる。それ以上は本文を削りすぎると判断して、載せないことにした。

<p>・2018年Fields賞は
<a href="https://www.dpmms.cam.ac.uk/~cb496/">Caucher Birkar</a>,
<a href="https://people.math.ethz.ch/~afigalli/">Alessio Figalli</a>,
<a href="http://www.math.uni-bonn.de/people/scholze/">Peter Scholze</a>,
<a href="http://math.stanford.edu/~akshay/">Akshay Venkatesh</a>
に授与されました.Scholze, Venkateshは<a href="http://www.ms.u-tokyo.ac.jp/~toshi/jjm/JJMJ/JJM_JHP/contents/jjm-takagi_jp.htm">高木レクチャー</a>をしています.
また,Chern Medal は<a href="https://ja.wikipedia.org/wiki/%E6%9F%8F%E5%8E%9F%E6%AD%A3%E6%A8%B9">柏原正樹</a>氏に贈られました.
(8/1/2018)

08/01: 2018年Fields賞はCaucher Birkar,Alessio Figalli,Peter Scholze,Akshay Venkateshに授与されました.Scholze, Venkateshは高木レクチャーをしています.また,Chern Medal は柏原正樹氏に… https://www.dpmms.cam.ac.uk/~cb496/ https://people.math.ethz.ch/~afigalli/ http://www.math.uni-bonn.de/people/scholze/ http://math.stanford.edu/~akshay/

技術について

bot を作るのに使う技術は全然難しくない。 Nokogiri でスクレイピングして Clockwork で定期的にクロールし、 Twitter gemREST API を叩くだけである。私にとっては全て過去に通った道であり、自明である。それでもいくつか勉強になった箇所があるので私のために残しておく。

Ruby における正規表現による取得

Perl 由来のメソッドではなく match メソッドを使用するのが「正しい作法」とされている。これは以下の本参照。

URL の短縮について

よく知られる通り、 Twitter は t.co による短縮 URL が導入されているが、日本語の短縮ができない。調べたら以下の奥村先生のページがヒットした。

ところがこれではうまくいかない例を 一発目で 発見してしまった。

私は確かに『ご注文はうさぎですか?』は好きだけど、ぶっちゃけガチ勢ではないです。完全に偶然です。あの 5 人は全員かわいいけど、その中だとリゼちゃんが一番スキです。

わからなかったので ciel さんに質問したところ、どうやらブラウザが ? をエスケープしているらしい。

私は Addressable という gem を見つけて解決した。

ツイートの文字数のカウント

現在の Twitter は文字数のカウントが若干ややこしくなっている。大雑把に言えば「半角では 280 文字まで、全角では 140 文字まで」である。素朴にやるなら以下の方法がある。

ところがこの方法では Encoding::UndefinedConversionError が生じることがわかった3

よく調べると、公式に提供されている twitter-text gem で文字カウントができることがわかった。これを使用することにした。この際 libidn が必要であり、 brew install libidn とした。現状は v.3.0.0 なのか、きちんと動いている。

[16] pry(main)> Twitter::TwitterText::Validation.parse_tweet("「 1 歩音超え、 2 歩無間、 3 歩絶刀……!」 『無明三段突き』! 」")
=> {:weighted_length=>65,
 :valid=>true,
 :permillage=>232,
 :valid_range_start=>0,
 :valid_range_end=>37,
 :display_range_start=>0,
 :display_range_end=>37}

これで二分探索することにした。

access token の取得

私が Twitter developer 登録してあるアカウントは @sunaemonRT_univ であるから、今回はここから App を登録してある。ゆえに @kawahigashinews の access token を取得する必要がある。

実はこれは Ruby gem で非常に簡単にできる。以下の記事をみた。

補足

制作時間について

1h で書けると豪語したが、実際はきちんと仕様を切る+調べる+テストをする+ロバストにする必要があったので、およそ 12h くらいかかった。私の計算機への適正はその程度である。

安直な実装をするなら 1h でできるので、プロトタイピングをしてだんだん成長させていくという開発スタイルもあり得るかと思うが、私の場合は完成品を最初から目指した。

多分 ciel さんのようなコード書くのが速い+技術にも明るい本職のプログラマだと 1h で全部できそうではある。

難易度について

私は何個か bot を作ってきた。 10 個以上になる。

今回の件を振り返ると、 スクレイピング自体は最高レベルに簡単だった 。私はアニメと声優さんの対応を記述したサイトのスクレイピングをしたこともある。考える要素すらないレベルだった。スクレイピングしたい人は河東セミナーニュースは入門編になるだろう。

しかし現在の Twitter の仕様上、 それを整形して Twitter に投げるのは思いの外骨が折れた 。特に日本語 URL 展開と文字カウントは今までやったことがなかったので時間がかかった。

bot 部分については sunaemonRT-univ の開発経験がほぼそのまま活かせた。

テストについて

河東先生が 20 年程度同じ形でニュースを記述していたので、この開発においてテストが非常にしやすかった。そこは通常の開発より恵まれていた。 parse については直近の 5 年を使ってテストしたが、十分だと感じた。また、直近の 2019 年 1 月の記述を使って、 Twitter 側との連携のテストをするのも、自然であった。

アイコンについて

アイコンは IPAex ゴシックをわざわざ mac に入れて作成した。ヒラギノだとなんか言われるかもしれないから一応。

  1. 原理的には 200 件までいけるらしい (2018-01-17 現在) 

  2. なお、年が変更になった時点も想定して対応してある。少々 Twitter API や ms サーバが死んでも動き続けるロバストなものを目指した。 

  3. 2018 年の河東セミナーニュースをテストして判明。エラーを吐くので避けるほかあるまい。