ElixirでWebスクレイピングのためのライブラリを作った

RubyにはMetaInspectorというwebスクレイピングのためのgemがあります。複雑なparseに使うことは出来ませんが、基本的な情報やOpenGraphなどのmeta情報を取得する際に効果を発揮します。

MetaInspectorよろしく、webスクレイピングを手軽にできるライブラリをElixirで使いたかったので自分で作りました。

github.com

MetaInvestigatorって名前が呼びづらいので、さっそく名前を変えたくなってる

目次

  • MetaInvestigatorの内部的な話
  • 開発中に直面した問題
  • おわりに

MetaInvestigatorの内部的な話

MetaInspector本家は、faradayを使ってHTTPリクエストを投げています。 それに倣って実装するため、ElixirにはどんなHTTPクライアントがあるのかを調べることにしました。 どうやらHTTPoisonとHTTPotionという2つのライブラリが有名なようです。

どちらか一方が抜きん出ていれば選びようもあるのですが、GitHubのstar数を見ても分かる通り、ほとんど差がありません。 考える(調べる)のが面倒になってしまったので、もういっそのこと利用者に好きなHTTPクライアントを使ってもらうことにしました。

そういった経緯から、MetaInvestigatorを使う時は以下のように、リクエストを投げる処理とparseする処理に分けて使います。 試しに、GitHubの僕のページにリクエストを投げます。

# HTTPクライアントでリクエストを投げて、レスポンスのHTMLを取得する
iex(1)> html = HTTPoison.get!("https://github.com/nekova").body
"<!DOCTYPE html>\n<html lang=\"en\" class=\"\">\n  <head prefix=\"og: ....... "
# その結果をparseする
iex(2)> page = MetaInvestigator.fetch(html)
%{best_image: "https://avatars1.githubusercontent.com/u/3464295?v=3&s=400",
  best_title: "nekova (ಠ_ಠ) · GitHub",
  images: ["https://avatars3.githubusercontent.com/u/3464295?v=3&s=460",
   "https://assets-cdn.github.com/images/spinners/octocat-spinner-128.gif"],
  meta: %MetaInvestigator.Meta{charset: "utf-8", keywords: nil,
   og_image: "https://avatars1.githubusercontent.com/u/3464295?v=3&s=400",
   og_title: "nekova (ಠ_ಠ)", og_type: "profile",
   og_url: "https://github.com/nekova"}, title: "nekova (ಠ_ಠ) · GitHub"}

MetaInvestigatorの主な機能の実装は簡単でしたが、少しばかり面倒な問題にぶつかりました。

開発中に直面した問題

例を見せるために、阿部寛さんのホームページにHTTPリクエストを投げてみましょう。

# HTTPリクエストを投げて、レスポンスのHTMLを取得する
iex(1)> html = HTTPoison.get!("http://homepage3.nifty.com/abe-hiroshi/").body
<<60, 104, 116, 109, 108, 62, 10, 60, 104, 101, 97 ...>>

すると、さきほどとは異なり、戻り値が<<>>で囲われたBinaryで返ってきているのが分かるでしょうか。 これらの違いが生まれるのには2つの理由があります。

  1. ElixirにおけるStringの定義はエンコーディングUTF-8である
  2. リクエスト先である阿部寛さんのホームページはShift-JISでエンコードされている

つまり、今回のようにUTF-8で宣言されていないページのhtmlはStringとして扱うことが出来ず、単なるBinaryとして扱わなくてはなりません。

MetaInvestigatorが依存しているparserは、Stringのparseを想定しています。 このままではUTF-8以外で宣言されたページをparseする度にエラーが起きてしまうので、ガードの中で引数がStringであることを確かめたいですね。

def fetch(html) when is_string(html), do: parse(html)

しかし、残念ながらElixirにis_string/1などという関数は存在しません。

この話を理解するためには知っておいて欲しいことがあります。

僕は当初、「is_stringがないなら実装すればいいじゃない」と考えていましたが、それは間違いでした。 なぜなら、Erlang VM はガードの中で呼び出せる関数をあらかじめ制限しているからです。ガードの中で使えるのは、orなどのブール表現、is_binaryなどのガード述語、==などの比較演算子と、その他のいくつかの関数だけになります。

たとえElixirの言語上にis_string/1を実装したとしても、BEAMが許してくれない限り、その関数をガードの中で呼び出すことはできません。 ちなみに、自作の関数などをガードの中で実行すると、 cannot invoke local function/1 inside guardというエラーが起きます。

さて、解決方法です。 と言っても、ガードの中では何も出来ません。is_string/1を呼ぶのは諦めて、関数内でString.valid?/1を呼んでcase文で分岐して処理してください。 最初からこうすれば良いのではないか、と思うかも知れませんが、僕はどうしてもguardで書きたかったんです........。

def fetch(html) when is_binary(html) do
  case String.valid?(html) do
    true -> parse(html)
    false -> raise some_error
  end
end

「HTTPクライアントに依存しないように開発を進めた」と書きましたが、Elixirという言語の特性上、エンコーディングをチェックする必要があるみたいです。こればかりは、HTTPリクエストのレスポンスを参照するのが楽なので、いずれかのHTTPクライアントに依存しなくてはならないようです。

おわりに

少し長くなってしまいましたが、Elixirのライブラリを作った話は終わりです。

Elixir Advent Calendarというものを作りました。あと11枠の空きがあるので、奮ってご参加ください

qiita.com

Mediumで独自ドメインを使うために必要だったこと

独自ドメインが適用されるのは、Mediumでのユーザページ(/@user_name)ではなく、Publicationsのページ(/publication_name)です。

正式な申請手順は以下の記事を参照してください

medium.com

必要だったこと

さきの記事を読めばすぐに独自ドメインを使えそうですが、NamecheapでCNAMEの登録をしようとするとINVALID ERRORが起きてしまいました。

Mediumからの返信メールには A recordsに登録するための12個のIPアドレスと、CNAMEに登録するための一対のhostとtargetが書かれています。

Here is your CNAME "domain" (sometimes referred to as "host" or "name"): ABC123DEF456GHI789JKL.essay.nekova.net Some registrars will only allow you to add the string of numbers and letters before your domain or subdomain. If adding the entire string doesn't work, try just adding the part before your domain or subdomain.

Errorを回避するためには、CNAMEのhostに入力する値をabc123def456ghi789jkl.essayのようにdowncaseする必要がありました。

他の誰もこの問題に直面していないようだけど、少なくとも僕はこうしないと反映させられませんでした。

そんなこんなでMediumのPublicationsに独自ドメインを適用することができました

essay.nekova.net

まとめ

Publicationsに投稿した記事は自分のホーム(/@user_name)にも流れる仕様です。

つまり、Medium内で日本語と英語といった具合に言語でPublicationsを分けたとしても、自分のホームには両方の記事が流れてしまいます。

挙動やUIに戸惑いつつも、複数のブログを無料で独自ドメインを使いながら1つのアカウントで扱えるのは楽で良いと思ったので使っていきたい

YAPC::Asia Tokyo 2015のスタッフ業を終えて

YAPCと人生

僕がYAPCを知ったのは, ちょうどプログラミングを始めたばかりの2012年のことでした.
家でぼーっとしている時に 'Perlと人生' というLTをYouTubeで観たのがきっかけです.

www.youtube.com

それからすぐ SixApart に「YAPCのLTの動画観ました! Perl(というか何も)書けないんですけどアルバイトさせて下さい!」というメールを送りつけたら, アルバイトをさせてもらえることになりました. 現在はSixApartのアルバイトは辞めてしまったんですが, エンジニアを募集中 だそうなので興味のあるかたはどうぞ!!!(無限コーヒーの提供はSixApartさんです)

僕がボランティアスタッフに応募した理由は, プログラマーとしての一歩目を踏み出すきっかけになってくれたYAPCの手伝いをしたかったからです.

実際のところ今回に関しては, あまりお手伝いできた自信がないので Builders Conference が開催されることになったらもっともっとお手伝い出来たらなあと思っています (勘違いしてる方もいますが, Beaconは No promises yet です)

大規模カンファレンスのスタッフ業で得られた知見

  • 開催前にスタッフmeetupをやると良い(今回のYAPCでは2回あった)
    • 気持ちがグッと入る
    • 開催前に顔を見たことがあるのでやり易くなる
  • 当日, オンラインのコミュニケーションはslackでやるのが良い
    • channelを適度に作って, 全体でノイズが増え過ぎないようにする
    • スタッフが集まった段階で, facebookのグループも使ってました
  • 全体を把握して意思決定できる人が絶対に1人必要(今回だとlestrratさん)
    • その人にタスクが集中するとパンクするので, できることなら現場で解決する
    • チームリーダーを置いて, 分隊っぽくやると良さそう
  • アイコンと名前が付けられる名札があると嬉しい
    • 顔と名前が一致しない問題を解決したい
    • コミュニケーションをいかに円滑にできるかがスタッフ業は大事っぽい
  • 会場が大きい OR 会場の構造が複雑 なら誘導のための看板があると良い
  • しつこいと思われてもいいから声に出す
    • レシーバー回収の時にしつこく何度も「返却お願いします」と大声で歩き回ったんですけど, おかげ様で全部回収できました
    • 「ブログを書くまでがYAPC」って言い続けないと, ここまで多くの人がブログを書いてくれることはないと思う

もっと詳しい話は僕なんかよりも, コアスタッフの人に聞くのが良いと思います.
僕の印象だと, イベント開催で最も頭を悩まされるのは, お金と場所の問題だと思う

謝辞

コアスタッフで10ヶ月も前から準備されてた皆さんに頭が上がらないです.
CONBUの皆さんのおかげで家より快適なインターネットができました(本当に), ありがとうございました.
ボランティアスタッフの皆さんもお疲れ様でした!

参加者の皆さんもありがとうございました! ブログを書くまでがYAPCなのでよろしくお願いします!!

見逃したトークがあったらYouTubeでご覧ください

www.youtube.com

ブログを書き終えたので, 僕のYAPCはこれで終わりです.

#tqrk09 で Elixir について話した && 読書について

Tokyu Ruby Kaigiは僕が初めて参加したプログラマーのイベントなので個人的な思い入れがあります. 2回目の参加の時は飛び入りでLTをさせてもらいました. 今回は事前にLTの登録をして, Elixir/Erlangについての話をしてきました! (LTのトリでした)

Elixirについての話をした

言いたかったのは,

  • Elixirのスゴイところとして紹介されていることの多くはErlangVM(BEAM)のおかげ
  • “The Power of Erlang, the Joy of Ruby” - Dave Thomas

Voyage GroupさんでErlangを書いてらっしゃる方から話を聞くことが出来たのが一番うれしかった.
Erlangを使ってる人達の話をもっともっと聞きたいので, そういう会があったら教えてください.お願いします

学習においてはProgramming Elixirが入門として最高の本です.
(個人的には, Erlangについての記述が比較的多いElixir in Action という本が面白かったです.)
Macrosについてもっと詳しく知りたければ, Phoenixの作者が書いたMetaprogramming Elixirって本がありますよ.

読書について

僕はいつもSafariBooksOnlineって場所で本を読んでいます.
月額$39で技術書が読み放題なので, 好きな時に好きな本を読めます. もちろんここには無い本もありますが, 発売前の本を読めたりもします.
SafariにはEnterpriseのためのplanもあるので, 会社の福利厚生としてSafariに加入するのなんかがあると嬉しいですね.
30days trial のためのリンクがあった気がするんですけど, 失くしたっぽい.......見つけたら追記します.

Elixirという言語は, ErlangのPowerに支えられていて, そこにRubyのJoyもあるんですよ〜って言えて良かった.

最後になりましたが, tqrkのスタッフの皆さん ありがとうございました!!!!

f:id:nekova:20150830155212j:plain

Matias Ergo Pro Keyboard を購入した

Macbook Pro(以下MBP)のkeyboardをとても気に入っているのですが, 長時間コードを書いているとどうしても手首が痛くなってしまいます.

若さにかまけてこういった兆候を無視していると, あとあと面倒なことになりかねないと思ったので, かねてより欲しかった Matias ergo pro を買うことにしました.

keyboardioやkinesisではなく, Matias Ergo Proを選んだ理由としては

  • MBPのレイアウトに近い
  • タイピング音がうるさくなさそう
  • デザインがクール

といったあたりです.

instagram.com

Keyboardは使い込んでから評価をしなければ, あまり意味がないとは思うのですが,
時間が経つとレビューを書くのが億劫になってしまうので, 取り急ぎ雑感を書き記すことにします.

少し不満に感じている点

  • 細かいキズがたくさん入っていた
  • バリ取りが甘いため, キーによっては引っ掛かりがあった(過去形)
  • キートップが外しづらい
  • ESCが驚くほど遠い
  • 右optionがhome/end/pg-up/pg-downになっている
  • MBPのkeyboardよりはうるさい(仕方ない)

満足している点

  • wrists restがとても良い(取り外しもできる)
  • 自由に角度をつけられるので手首が楽(ただし角度は保存できない)
  • 打鍵感が気持ち良い
  • スペースキーが短いので, ⌘/cmdが内側に配置されていて押しやすい

結論

良い買い物をした

Matiasの公式から買うと$200, Euroのお店から買うと€135+shipping, Amazon(ダイヤテック)から買うと¥25,800 となります.

ドルもユーロも高かったので, ダイヤテックから買いました. 2日で届いたことにも満足しています.

kksgさんよろしく, キーボード遍歴を書きたかったのですが HHKB lite2 しか使ったことがないので諦めました

みんなのキーボード環境を見てみたいです

[追記 2015/07/17] 人間工学に基づいてデザインされてるせいで, 上段のキー(QWERTY)と下段のキー(ZXCVB)には微妙に角度に工夫が凝らしてあるので, Dvorak配列に配置し直そうとすると触り心地が気持ち悪くなる

Matiasの公式FAQには, Dvorakに配列を変えられるよとあるんだけど ergo proのキー配列は変えられないものだと思っていたほうが良い

SHIROBAKOをもっと楽しむために読んでおきたい文献2つ

アニメを仕事に!

制作進行という仕事の酸いも甘いも知り尽くした舛本和也さんが書いた, トリガー流アニメ制作進行読本. アニメを仕事にしようとは考えていなくとも, SHIROBAKOを観て制作進行という仕事について興味を持ったならオススメ出来ます.

アニメを仕事に! トリガー流アニメ制作進行読本 (星海社新書)

体力と運転免許があればできる仕事といわれている反面, 3年後の業界滞在率が10~20%だったり, 手取りが11万だったり...... SHIROBAKOでは語られない制作進行の実態について言及されていたり, 実務の流れをより詳しく説明してくれています.

個人的に一番刺さったのは下記の言葉

pp34

アニメ制作会社に入らなければアニメが作れない、絵が描けないとアニメ作りに関われない、と思うのは早計です。 音楽が好きならば音楽会社のアニメ部門、グッズが好きならばグッズメーカーのアニメ部門に就職すればいいのです。 固定観念に囚われず、視野を広く持って、 自分にあった就職先を見つけてください。 自分の人生の中で、自分の中の「アニメを好きな心」を大切にしたいのならばしっかり考えてください。 「アニメが好き」という気持ちをずっと大切にしたいのならば、その気持ちが一生続く居場所を(就職先を)ちゃんと探してください。

アニメに限らず, 好きなことに携わる仕事をしたい全ての人に当てはまる言葉だと思います.

僕は「Webサイト(インターネット)が好き」という理由だけでプログラミングを勉強し始めて, 今に至ります. これまで, 一歩下がってWeb業界のことを見たことがありませんでしたし, ハッとさせられました.

平成24年度 若手アニメーター育成プロジェクト報告書

先日,劇場版の年内公開が発表されたリトルウィッチアカデミアや, 今期放送中のデス・パレード改めデス・ビリヤードなどのアニメミライ2013の報告書です.

リトルウィッチアカデミア展にて, アニメミライ関係者の方にプロジェクトや裏話を聞かせて頂いた時に教えてもらいました.
この報告書は文化庁に納めるために作られたものなので販売はされていませんが, 国会図書館に行けば閲覧可能です. ちなみに, 国会図書館の複写サービスはA4サイズを白黒印刷で1枚あたり25円ほどなので, 報告書は3000円ほどで手に入ります.
(※ 1度の申し込みで100ページまでしか複写できません)

Kindle for Mac を amazon\.co\.jp のアカウントで認証させる方法

対象とする読者

OSXのシステム設定で, 言語を"English"(正確には日本語以外)に設定している人.

問題

Kindle for Macの起動時, システムで使われている言語によって接続されるAmazonのストアが変わってしまう.
ex) システム言語を英語に設定していると Amazon.com に接続されてしまう.

とりあえずの解決方法

  • OSXのシステム言語を日本語にする
  • Kindle for Mac のみ 日本語で起動する

後者の解決法の手順を以下に記します.

Terminalを開いて,コマンドを入力します. 上のコマンドは下のものを単純にしただけなので, どちらも同様の働きをします.

$ defaults write com.amazon.Kindle AppleLanguages "(ja)"

$ defaults write $(mdls -name kMDItemCFBundleIdentifier -raw /Applications/Kindle.app) AppleLanguages "(ja)"

言語設定の変更を確認したい場合は, defaults find AppleLanguages というコマンドを使います.

$ defaults find AppleLanguages
=>
Found 1 keys in domain 'com.amazon.Kindle': {
    AppleLanguages =     (
        ja
    );
}
Found 1 keys in domain 'Apple Global Domain': {
    AppleLanguages =     (
        en,

この状態でKindle for Macを普通に起動しても, Amazon.com に接続されてしまうため, 一度だけTerminalから起動します.

$ /Applications/Kindle.app/Contents/MacOS/Kindle

すると, めでたく Amazon.co.jp のストアに接続され, 日本アカウントの認証が可能になります.

アカウントを認証した後であれば, Terminalからでなく通常の手順でKindleを起動してもAmazon.co.jpに接続されたまま使い続ける事ができます.

余談

この手順だと, ログアウトしてしまう度にTerminalからKindleを起動する必要があるので, 些か面倒です.

ちなみに, App Language Chooserのような, 言語を選択して起動するアプリを使用してみたのですが上手くいきませんでした.......

参考にした記事

osx - Can I change the default language of a application / program in Snow Leopard? - Super User