linuxのシェルコマンドでWebページのタイトルを取得する

あるWebサイトのURLがずらずらと書かれたcsvファイルがあって、それにページタイトルの列を付けたいという案件がありました。 100行ちょっとあって面倒くさかったので、シェルでぱぱっと解決することに。

少し考えた結果、以下のようなコマンドでタイトルを取得するようにしてみました。

$ curl -s "${URL}" | grep -oP '(?<=<title>).*(?=</title>)'

各行について処理したかったので、forコマンドと組み合わせて以下のようにして使いました。

for URL in `cat ./url-list`; do
    curl -s "${URL}" | grep -oP '(?<=<title>).*(?=</title>)' >> title-list
done

curlもgrepもだいたいのディストリビューションに標準で入っているので、手軽で良い感じ。

中身の解説

curl -s

curlに渡している-sオプションは--silentオプションの短縮です。 これを使うことで、ダウンロードの進捗なんかが表示されなくなります。

渡さなくても問題無いのだけれど、沢山出てくるとウザいので付けてあります。

grep -o

grepの-oオプションは、「マッチした部分だけを表示する」という意味のオプションです。 シェル芸ではよく使う便利オプションです。

今回は、ダウンロードしてきたHTMLからtitleタグの中身だけを抜き出すために使っています。

grep -P

grepにもう一つ渡している-Pオプションは、Perlの正規表現を使うために付けているオプションです。 これを付けておくと、何も付けていない状態のgrepよりも高度な正規表現の機能を使えるようになります。

(?<=<title>).*(?=</title>)

単純にタイトルタグを抜き出す正規表現を考えると <title>.*</title> のようになるのですが、これだけだと <title>ここにページタイトル</title> という形で余計なタグが付いた結果しか得られません。

このタグをマッチ結果に含めないようにするために、先ほどの-Pオプションで有効にしたPerlの機能を使っていきます。

まず、開きタグ(<title>の方)を除去するために、肯定後読みという機能を使います。 書き方は(?<=<title>)みたいな感じ。(?<=)で囲ってあげます。

これをすると、「<title>の後に続く文字列しかマッチしない(ただし<title>自体はマッチに含めない)」ということが出来ます。 まさに今欲しい機能。

次は閉じタグ(</title>の方)。こちらは肯定先読みを使用します。 書き方は(?=<title>)という感じ。(?=)ですね。

機能はご想像の通り。 後読みと同じ感じで、「</title>の前にある文字列にしかマッチしない(ただし</title>はマッチに含めない)」となります。

そんなわけで、欲しかったタイトルの部分だけを抜き出すことが出来ます。

まとめ

正規表現はいいぞ。