QA@IT

urlの正規表現におけるドメインの取得(python)

10991 PV

環境
Python3.4.3

urlからドメイン部分を取得する正規表現をpythonで書いています。
取得したいドメイン部分は "http://www.amazon.co.jp" の場合 "amazon.co.jp" の様にTLDから第三レベルドメインを取得したいと考えています。
urlからドメイン部分を取得するプログラムを以下に記載します。
なお、記載されたurlはあくまでパターンの例です。

#!/usr/local/bin/anaconda3/bin/python3

import re

p = re.compile(r"[https?|ftp]://[A-Za-z0-9\-.]{0,62}?\.([A-Za-z0-9\-.]{1,255})/?[A-Za-z0-9.\-?=#%/]*")

def re(url):
    m = p.search(str(url))
    print(str(m.group(1)))

re("http://r.gnavi.co.jp/g705400/")
re("http://www.amazon.co.jp")
re("http://www.dmm.com/netgame_s/toloveru/")
re("https://totalhash.cymru.com/analysis/")
re("https://www.youtube.com")
re("http://blog.code4u.org")
re("http://futuremix.org")
re("http://sample.co.jp/hoge")

何通りかのurlで試した結果、概ね想定通りのドメインを取得できましたが、 "http://futuremix.org" や "http://sample.co.jp/hoge" といったurlではTLDのみの取得になることを確認しました。
これらのURL全てに対応する有効な正規表現はどのように記載すればよいでしょうか。

以上よろしくお願い致します。

2015/11/06 追記
質問で伝わりづらい部分があったので補足です。
今回所得したい部分は第四レベルドメイン(wwwなど)を除いたTLDから第三レベルドメインです。
"http://www.dmm.com" ではwwwが第四、dmmが第三、comが第二とトップとなるため、dmm.comの取得をしたいと考えています。
そのため、今回の理想の実行結果は以下のとおりになります。

gnavi.co.jp
amazon.co.jp
dmm.com
cymru.com
youtube.com
code4u.org
futuremix.org
sample.co.jp

ドメイン分類参考
JPNIC https://www.nic.ad.jp/ja/dom/system.html
Networkの基礎 http://www5e.biglobe.ne.jp/~komichan/network/n1_DomainName.html

回答

何点か気づいた点があるので、試しに直してみました。

  1. [https?|ftp]だと文字クラスなので、httpのpやhttpsのsの1文字にしかマッチしない点が気になります。これは(?:https?|ftps?)に置き換えます。

  2. TLDから3レベル取りたいって話なので、素直に()を3個使って、3レベル分キャプチャすることにします。.はレベルを区切るものなので[]の外に出すことにします。ついでに文字数制限も加えます。するとこんな感じになると思います。

    p = re.compile(r"(?:https?|ftps?)://([A-Za-z0-9-]{1,63}\.)*([A-Za-z0-9-]{1,63}\.)([A-Za-z0-9-]{1,63})/?[A-Za-z0-9.\-?=#%/]*")
    
  3. p.search(str(url))のようにsearchを使っていますが、文字列先頭にマッチさせたいのでmatchに変更します。str(url)urlでよいのでは。

  4. 結果を表示するときprint(str(m.group(1)))としていますが、3レベル分キャプチャしたので、m.groups('')でタプルを取得することにします。表示するときは"".join(map(str, m.groups('')))として1個の文字列にします。

コード

#!/usr/bin/env python3

import re

p = re.compile(r"(?:https?|ftps?)://([A-Za-z0-9-]{1,63}\.)*([A-Za-z0-9-]{1,63}\.)([A-Za-z0-9-]{1,63})/?[A-Za-z0-9.\-?=#%/]*")

def re(url):
    m = p.match(url)
    if m:
        print("".join(map(str, m.groups(''))))

re("http://r.gnavi.co.jp/g705400/")
re("http://www.amazon.co.jp")
re("http://www.dmm.com/netgame_s/toloveru/")
re("https://totalhash.cymru.com/analysis/")
re("https://www.youtube.com")
re("http://blog.code4u.org")
re("http://futuremix.org")
re("http://sample.co.jp/hoge")

実行結果

$ ./domre2.py 
gnavi.co.jp
amazon.co.jp
www.dmm.com
totalhash.cymru.com
www.youtube.com
blog.code4u.org
futuremix.org
sample.co.jp

追記

私にはそのようにする意図がよく分からないのですが、comやorgの場合には2個レベルを消費する仕様にしたいと風に受け取りました。認識合ってますかね?もしそうなら、力ずくではありますが、comやorgを例外扱いすればよいと思います。comやorg以外にも例外があるなら、全部列挙する必要があります...

p = re.compile(r"(?:https?|ftps?)://([A-Za-z0-9-]{1,63}\.)*(?:(com)|(org)|([A-Za-z0-9-]{1,63}\.)([A-Za-z0-9-]{1,63}))/?[A-Za-z0-9.\-?=#%/]*")

書き直した後の実行結果

$ ./domre3.py 
gnavi.co.jp
amazon.co.jp
dmm.com
cymru.com
youtube.com
code4u.org
futuremix.org
sample.co.jp

(追記)第三レベルドメイン

「TLD」、「第二レベルドメイン」「第三レベルドメイン」の標準的な意味については下記をご覧下さい。comやorgがTLDと第二レベルドメインを含むと見なし、www.dmm.comのdmmを第三レベル、wwwを第四レベルとするのは、あくまでutusemi_f99さんが参考文献として挙げられたNetworkの基礎の独自解釈だという認識です。少なくとも僕は他では聞いたことはないです。

出典
https://newgtlds.icann.org/en/applicants/global-support/faqs/faqs-en

上記からの引用

8.3 What is a top-level domain (TLD)?

Every domain name around the world ends with a top-level domain (TLD); these are the 2 or more letters that come after the dot. There are currently two types of TLDs: generic top-level domain (gTLDs) such as .com, .mobi, and .info, and country code top-level domains (ccTLDs) such as .uk, .br, and .cn. A gTLD or a ccTLD is managed by a registry operator, an organization that maintains the registry database, including the nameserver information for names registered in the TLD.

8.4 What are second-level and third-level domain names?

The portion of the domain name that precedes the top-level domain is called the second-level domain name (for example, the "icann" in "icann.org"). There are also third-level domain names that appear before the second-level domain name and again are separated by a dot (for example, events.icann.org). Third-level domain names are also called sub-domains and are often used to categorize special sections of a website.

ですので私が最初に書いた方は「第三レベルドメイン」の標準解釈に従うものであり、後で書き直した方は「Networkの基礎」の独自解釈に合わせて、comやorgのときは二個レベルを消費するように例外を加えたものです。

ただcomやorgなんかは例外扱いでいいと思うのですが、.jpなんかは.co.jpもあるけど、ただの.jpもあるので、どうしたものかとは思います。www.example.co.jpならexample.co.jpを取得して、www.example.jpならexample.jpを取得したいとかいう話になると、何だかなあ...

追記(別解)

下のStackOverflowの議論を見て思ったのですが、正規表現ではなく、専用のライブラリを使用するのも一つの手ですね。URLをパースしてサブドメイン名、登録ドメイン名、TLDを抽出してくれるようです。試しにやってみたら、期待する結果が得られるようです。これらはTLDの一覧を得るためにMozilla Public Suffix Listをダウンロードするようです(TLDの一覧を最新に保つため時々更新した方がよさそうです)。

Python urlparse — extract domain name without subdomain

専用のライブラリ

tldextractを使う場合のコード

#!/usr/bin/env python3

import tldextract

def extract(url):
    ex = tldextract.extract(url)
    print(".".join(ex[1:]))

extract("http://r.gnavi.co.jp/g705400/")
#以下省略

tldを使う場合のコード

#!/usr/bin/env python3

from tld import get_tld

def extract(url):
    print(get_tld(url, fail_silently=True))

extract("http://r.gnavi.co.jp/g705400/")
#以下省略
編集 履歴 (9)
  • 1個修正漏れがあったので追記しました。
    -
  • すみません。"http://example.com."のように"."で終わる場合をはじくのを忘れたと思ったのですが、考えてみたら、これで正しいですよね。元に戻しました。 -
  • たびたびすみません。文字クラスの中の"."はエスケープする必要なかったですね。その部分は削除しました。
    -
  • お世話になっております。回答ありがとうございます。今回ドメインのレベル部分でわかりづらい部分があったため、追記させて頂きました。お手数ですが確認して頂ければ幸いです。 -
  • 認識が合っているか分かりませんが、変更点と結果を追記しておきました。
    -
  • 「第三レベルドメイン」の解釈について追記しておきました。
    -
  • 便利そうなライブラリがあることがわかったので追記しておきました。
    -
  • こちらの都合に合わせた回答ありがとうございます。第三レベルドメインの標準解釈のリンクもありがとうございます。ライブラリを利用する方法でやってみたいと思います。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。