Ruby
正規表現

RubyでHTMLのタグからURLを抽出する

最初に

webスクレイピングの勉強をしている時に、タグの中に入ったURLだけを抽出したいことが、多々あります。備忘録のため、色々調べたことを記録します。

※異なる方法や、間違いなどに気がついた方は、コメントを下さると助かります。

tag.html
//こんな感じのやつ
<a href="http://www.aaa.bbb.ccc">aaabbbcccc</a> 

正規表現

今回はRubyを使って正規表現で、抽出したいと思います。

まず、正規表現の構文ですが、URLに使用できる文字は以下の通りとなっています。

使用できる文字
1~9の数字
a-Z,A-Z
-
_
.
!
*
'
(
)
~

参照サイト

URLに使用できる文字を確認したので、これを元に正規表現を書いてみたいと思います。
まず、httpとhttpsをマッチさせるには"http(s|)"と記述します。
これにより、httpの場合とhttpsの場合のみ、マッチさせることができます。

sample.rb
url = "<a href=\"http://www.aaa.bbb.ccc\">aaabbbcccc</a>"

if url =~ /http(s|)/
   puts "match" #文字にマッチング
else
   puts "no match" #
end

次に共通の"://"の部分を記述します。"//"はエスケープしてあげないとエラーが起こるので、"\"でエスケープしてあげます。
結果は"http(s):\/\/"となります。

sample.rb
url = "<a href=\"http://www.aaa.bbb.ccc\">aaabbbcccc</a>"

if url =~ /http(s|):\/\//
   puts "match" #文字にマッチング
else
   puts "no match" #
end

次にFQDNの部分について考えてみたいと思います。
上記のシートにあるURLが使用できる文字列を[]でグループ化してみたいと思います。
少し長くなりますが、結果は"[\w-_.!*\')(]"となります。これがURLで使用できる文字となるので、それを"の位置の前まで一致させると考えると、構文は"[\w-_.!*\')(]+"となります。

sample.rb
url = "<a href=\"http://www.aaa.bbb.ccc\">aaabbbcccc</a>"

if url =~ /http(s|):\/\/[\w\-\_\.\!\*\'\)\(]+/
   puts "match" #文字にマッチング
else
   puts "no match" #
end

Atomのエディタで見るとこんな感じになります。

simple_module_rb_—___working_directory_Ruby_Working_Directory_Thor.jpg

これで、URLの抽出する正規表現は出来たので、次はrubyでURLだけ、タグの中から取得したいと思います。
一致した文字を取り出すには、matchメソッドを使います。

sample.rb
url = "<a href=\"http://www.aaa.bbb.ccc\">aaabbbcccc</a>"

puts url.match(/http(s|):\/\/[\w\-\_\.\!\*\'\)\(]+/)

#=> http://www.aaa.bbb.ccc

httpsにしても同じ結果を得ることが出来ます。

修正

コメントから、%記法で記述した方が、可読性が上がると、ご指摘がありました。
ご指摘いただきありがとうございます。
%rを使用すると、出現頻度が高い"/"をエスケープしてくれます。

sample.rb
url = '<a href="http://www.aaa.bbb.ccc">aaabbbcccc</a>' #シングルクォートで式展開を行わない

#以前
puts url.match(/http(s|):\/\/[\w\-\_\.\!\*\'\)\(]+/) 

#修正(%記法)
puts url.match(%r{https?://[\w_.!*\/')(-]+})

エスケープする数が減り、可読性が上がりましたね

【補足】Nokogiriを使用した方法。

RubyのHTML構文解析で有名なライブラリとして「Nokogiri」があります。
NokogiriはHTMLなどの構文を解析し、簡素な記述で目的の要素を取り出すことが出来ます。
試しに「 https://example.com/ 」のページをサンプルに書いてみたいと思います。

sample.rb
require "nokogiri"
require "open-uri"

doc = Nokogiri::HTML(open("https://example.com ")) #ページの内容を解析

#CSSセレクタを使用して、<a のタグにある、href属性を全て取得する
doc.css("a").each do |el|
  puts el[:href]
end

# => http://www.iana.org/domains/example

詳しい書き方や、サンプルなどはチュートリアルの公式サイト
にあります。

最後に

URLを取り出せましたが、何か欠点があると思いますので、気がついたら、随時更新していきます。