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>はマッチに含めない)」となります。
そんなわけで、欲しかったタイトルの部分だけを抜き出すことが出来ます。
まとめ
正規表現はいいぞ。