動機
URLをメモするときに、ページタイトルがあったら便利だなと思ったので作りました。これまでは、javascriptでpukiwiki用に出力するbookmarkletを自作していたのですが、ページごとにbookmarkletを起動させないといけないのが億劫になりました。
このごろCommon Lispにハマっているのでその練習も兼ねて。
環境
- Mac OS X El captain(10.11.3)
- SBCL 1.3.2
必要ライブラリ
- quicklisp
- drakma
- plump
- clss
- babel
- guess(インストール方法)
gistへのリンク
Gistにコードをまとめました。
httpレスポンスを文字列として取得
Drakmaで帰ってきたhttpレスポンスのbodyは、vectorの場合と文字列の場合があります。:force-binary
を使うことで、vectorで返してもらいます。
(drakma:http-request url :force-binary t)
ここで得られたvectorをguessというライブラリで文字コード判別します。
(guess:CES-GUESS-FROM-VECTOR input-vector :jp)
文字コードが判別できたら、babelというライブラリで、文字列に変換します。ここで判別された文字コード名と、babelで指定するシンボルは異なる場合があるので注意が必要です(例 EUC-JPなら:eucjp)。
(babel:octets-to-string input-vector :encoding encoding)
DOMに変換
titleタグの中に入っている文字を取得したいので、plumpというライブラリで、文字列をDOMに変換します。
(plump:parse html-body)
DOMからタイトルを取得
plumpのDOMオブジェクトから、clssというライブラリでtitleタグを抽出します。
(clss:select "title" dom)
抽出されたオブジェクトはvectorですので、map 'list
で変換しつつ、各要素にplump:text
で文字列を取得します。
(map 'list (lambda (val) (plump:text val)) (clss:select "title" dom))
実行例
上記で説明したものを組み合わせた、get-title
という関数を使ってみます。下の例はUTF-8のサイトのみに適用していますが、Shift-JIS、EUC-JPのサイトでも無事取得できました。
(get-title "https://ja.wikipedia.org/wiki/Common_Lisp")
;;=>("Common Lisp - Wikipedia")
(get-title "http://www.sbcl.org/")
;;=>("About - Steel Bank Common Lisp")
(get-title "http://tips.cddddr.org/common-lisp/")
;;=>("逆引きCommon Lisp")
複数のURLを渡す
mapcan
を使って、複数のURLを処理することができます。
(mapcan #'get-title '("https://ja.wikipedia.org/wiki/Common_Lisp" "http://tips.cddddr.org/common-lisp/")))
;;=>("Common Lisp - Wikipedia" "逆引きCommon Lisp")