common-lisp
CommonLisp

Common LispでURLからページタイトルを取得する

More than 1 year has passed since last update.

動機

URLをメモするときに、ページタイトルがあったら便利だなと思ったので作りました。これまでは、javascriptでpukiwiki用に出力するbookmarkletを自作していたのですが、ページごとにbookmarkletを起動させないといけないのが億劫になりました。

このごろCommon Lispにハマっているのでその練習も兼ねて。

環境

  • Mac OS X El captain(10.11.3)
  • SBCL 1.3.2

必要ライブラリ

gistへのリンク

Gistにコードをまとめました。

https://gist.github.com/tamanobi/746f550c60f890960d40#file-take-title-lisp

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.lisp
(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を処理することができます。

get-titles.lisp
(mapcan #'get-title '("https://ja.wikipedia.org/wiki/Common_Lisp" "http://tips.cddddr.org/common-lisp/")))
;;=>("Common Lisp - Wikipedia" "逆引きCommon Lisp")