Python-PythonでURLを検証する方法は?(不正かどうか)


116

私はurlユーザーから受け取ったもので、取得したHTMLで返信する必要があります。

URLの形式が正しくないかどうかを確認するにはどうすればよいですか?

例えば ​​:

url='google'  // Malformed
url='google.com'  // Malformed
url='http://google.com'  // Valid
url='http://google'   // Malformed

どうすればこれを達成できますか?



1
それを読んでみてください。たとえば、httplibが例外をスローした場合、それが無効であることがわかります。正しい形式のURLがすべて有効であるとは限りません
carlpett、2011

1
これはあなたを助ける:stackoverflow.com/questions/827557/...
DhruvPathak

10
url='http://google' 奇形ではありません。スキーマ+ホスト名は常に有効です。
Viktor

回答:


90

django url検証正規表現(ソース):

import re
regex = re.compile(
        r'^(?:http|ftp)s?://' # http:// or https://
        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
        r'localhost|' #localhost...
        r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
        r'(?::\d+)?' # optional port
        r'(?:/?|[/?]\S+)$', re.IGNORECASE)

print(re.match(regex, "http://www.example.com") is not None) # True
print(re.match(regex, "example.com") is not None)            # False

好奇心...追加しましたftpか?または私は古いジャンゴバージョンを持っていますか?
Ruggero Turra

2
@ yugal-jindleサイトドメインは有効なURLではありません。Museumは、.museumがトップレベルドメイン(ICANN [1]が定義)であり、サイトドメインではないためです。[1] icann.org
glarrain

1
これは、username:password@example.comスタイルのURL では機能しないようです
Adam Baxter


2
これは次の形式のIPv6のURLでは機能しませんhttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine

124

実はこれが一番いい方法だと思います。

from django.core.validators import URLValidator
from django.core.exceptions import ValidationError

val = URLValidator(verify_exists=False)
try:
    val('http://www.google.com')
except ValidationError, e:
    print e

に設定verify_existsした場合True、実際にはURLが存在することを確認します。それ以外の場合は、URLが正しく形成されているかどうかを確認します。

編集:ああそうです、この質問はこれの複製です:DjangoのバリデータでURLが存在するかどうかを確認するにはどうすればよいですか?


46
ただし、これはdjango環境でのみ機能し、それ以外の場合は機能しません。
Yugal Jindle、2011

19
verify_exists廃止予定です。-1
g33kz0r 2013

2
追加:django.confからインポート設定settings.configure(DEBUG = False)を確認し、verify_existsを削除して、django 1.5で動作し続けるようにします
Dukeatcoding

1
@YugalJindle正解ですが、Djangoからそれを取り除くことはほとんど簡単です:D。だから、私はこの方法を使います
swdev 2014

7
注意:django> = 1.5では、verify_existsもうありません。また、val変数の代わりに次のように呼び出すこともできますURLValidator()('http://www.google.com')
ラッキードナルド

122

バリデーターパッケージを使用してください:

>>> import validators
>>> validators.url("http://google.com")
True
>>> validators.url("http://google")
ValidationFailure(func=url, args={'value': 'http://google', 'require_tld': True})
>>> if not validators.url("http://google"):
...     print "not valid"
... 
not valid
>>>

Pippip install validators)を使用てPyPIからインストールします。


5
ファイルのURLに対してエラーがスローされます。「file:///users/file.txt」のように
Devavrata

2
localhostのURLが失敗するvalidators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal Zada、あなたはこのような何かを主張する前に、いくつかの努力を入れて、コードを確認し、正規表現は、実際には非常に良いです: validators.readthedocs.io/en/latest/_modules/validators/...
Drachenfels

1
パッケージのfnの検証には多くの恣意的な制限があるため、一般的な解決策として提案するのはひどいアドバイスです。
ivan_pozdeev

2
@ivan_pozdeev:それがひどい場合は、より良い解決策を提案してください
Jabba

62

@DMfllの回答に基づくTrueまたはFalseバージョン:

try:
    # python2
    from urlparse import urlparse
except:
    # python3
    from urllib.parse import urlparse

a = 'http://www.cwi.nl:80/%7Eguido/Python.html'
b = '/data/Python.html'
c = 532
d = u'dkakasdkjdjakdjadjfalskdjfalk'

def uri_validator(x):
    try:
        result = urlparse(x)
        return all([result.scheme, result.netloc, result.path])
    except:
        return False

print(uri_validator(a))
print(uri_validator(b))
print(uri_validator(c))
print(uri_validator(d))

与える:

True
False
False
False

8
Non-None以外の要素のリストを使用してifステートメントをテストできることを知りませんでした。それは役に立ちます。また、組み込みモジュールを使用するための+1
Marc Maxmeister

9
これはすべてを可能にします。True文字列fakeまたは空の文字列に対しても戻ります。これらの属性は常に存在するため、エラーは発生しません。また、リストにはこれらの属性が含まれているため、ブール値は常にTrueになります。すべての属性がNoneであっても、リストは空ではありません。すべてが現在の方法を通過するため、属性の検証が必要です。
zondo 2016年

3
falseオブジェクトのリストはTrueと評価され、print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")「私は本当です」と出力します。実行すると [result.scheme, result.netloc, result.path]は常にに評価されTrueます。print("I am True") if [] else print("I am False.")「私は偽りです」と印刷します。空のリストはFalseです。配列の内容は、all関数のようなもので評価する必要があります。
dmmfll 2016

3
なぜそのようなパスが必要になるのかわからない。result.pathテストから削除する必要があります。
Jerinaw

1
これで十分です。簡単な検証を追加しましたschemeif not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin

20

今日、私はパダムの答えに基づいて、以下を使用します:

$ python --version
Python 3.6.5

そして、これはどのように見えるかです:

from urllib.parse import urlparse

def is_url(url):
  try:
    result = urlparse(url)
    return all([result.scheme, result.netloc])
  except ValueError:
    return False

使うだけ is_url("http://www.asdf.com")

それが役に立てば幸い!


ドメイン名がダッシュで始まる場合は失敗しますが、これは無効です。tools.ietf.org/html/rfc952
ビョルンリンド

1
これは、URIの形式が正しくないことがわかっている特殊な場合にのみコンポーネントを分割するのに適しています。以前に他の同様の答えに返信したように、これはのような不正な形式のURIを検証しますhttps://https://https://www.foo.bar
ingyhere

9

注意 -leplはサポートされなくなりました。申し訳ありません(これを使用することは歓迎されます。以下のコードは機能すると思いますが、更新は取得されません)。

rfc 3696 http://www.faqs.org/rfcs/rfc3696.htmlは、これを行う方法を定義します(http URLと電子メールの場合)。lepl(パーサーライブラリ)を使用して、Pythonで推奨事項を実装しました。http://acooke.org/lepl/rfc3696.htmlを参照してください

使用する:

> easy_install lepl
...
> python
...
>>> from lepl.apps.rfc3696 import HttpUrl
>>> validator = HttpUrl()
>>> validator('google')
False
>>> validator('http://google')
False
>>> validator('http://google.com')
True

2
端正ですが、FTPやHTTPSはどうでしょうか?
アダムパーキン

6
あなたはコードをフォークして実装していませんか?オープンソースです。
Andrew Cookeは

1
leplは、現在作者によって中止されacooke.org/lepl/discontinued.html EDIT:あわや、ちょうどあなたが気づいている著者
エメット・バトラー

1
注:lepl.apps.rfc3696はPython 3.7.4では機能しません
Sheile

9

文字列を「有効な」URLとして検証する正気な方法を見つけようとして、このページにアクセスしました。ここでは、python3を使用して私のソリューションを共有します。追加のライブラリは必要ありません。

https://docs.python.org/2/library/urlparse.htmlを参照してくださいpython2を使用している場合は、 //docs.python.org/2/library/urlparse.htmlを。

私と同じようにpython3を使用している場合は、https: //docs.python.org/3.0/library/urllib.parse.htmlを参照してください

import urllib
from pprint import pprint

invalid_url = 'dkakasdkjdjakdjadjfalskdjfalk'
valid_url = 'https://stackoverflow.com'
tokens = [urllib.parse.urlparse(url) for url in (invalid_url, valid_url)]

for token in tokens:
    pprint(token)

min_attributes = ('scheme', 'netloc')  # add attrs to your liking
for token in tokens:
    if not all([getattr(token, attr) for attr in min_attributes]):
        error = "'{url}' string has no scheme or netloc.".format(url=token.geturl())
        print(error)
    else:
        print("'{url}' is probably a valid url.".format(url=token.geturl()))

ParseResult(scheme = ''、netloc = ''、path = 'dkakasdkjdjakdjadjfalskdjfalk'、params = ''、query = ''、fragment = '')

ParseResult(scheme = 'https'、netloc = 'stackoverflow.com'、path = ''、params = ''、query = ''、fragment = '')

'dkakasdkjdjakdjadjfalskdjfalk'文字列にはスキームもネットロックもありません。

' https://stackoverflow.com 'はおそらく有効なURLです。

次に、より簡潔な関数を示します。

from urllib.parse import urlparse

min_attributes = ('scheme', 'netloc')


def is_valid(url, qualifying=min_attributes):
    tokens = urlparse(url)
    return all([getattr(tokens, qualifying_attr)
                for qualifying_attr in qualifying])

4

編集

@Kwameで指摘されているように、以下のコードは、.comor .coなどが存在しない場合でもURLを検証します。

@Blaiseでも指摘されているように、https://www.googleのようなURLは有効なURLであり、それが解決するかどうかを確認するために、個別にDNSチェックを行う必要があります。

これはシンプルで機能します:

そのmin_attrため、URLの有効性を定義するために存在する必要がある基本的な文字列のセット、つまりhttp://部分とgoogle.com部分が含まれています。

urlparse.scheme店舗http://

urlparse.netloc ドメイン名を保存する google.com

from urlparse import urlparse
def url_check(url):

    min_attr = ('scheme' , 'netloc')
    try:
        result = urlparse(url)
        if all([result.scheme, result.netloc]):
            return True
        else:
            return False
    except:
        return False

all()内部のすべての変数がtrueを返す場合、trueを返します。つまり、result.schemeand result.netlocが存在する場合、つまり何らかの値がある場合、URLは有効であるためを返しますTrue


ああ、ナイスキャッチ..私は自分のコードを取り戻す必要があると思います。何が好きですか、正規表現以外のオプションはありますか?
パダムSethia 2017

https://www.googleは有効なURLです。実際には解決されない可能性がありますが、気になる場合はDNSチェックを行う必要があります。
ブレイズ

例外を飲み込む
ivan_pozdeev

2

urllibDjangoのような正規表現でURLを検証する

DjangoのURL検証の正規表現は実際にはかなり良かったのですが、使用例に合わせて少し調整する必要がありました。お気軽にご利用ください!

Python 3.7

import re
import urllib

# Check https://regex101.com/r/A326u1/5 for reference
DOMAIN_FORMAT = re.compile(
    r"(?:^(\w{1,255}):(.{1,255})@|^)" # http basic authentication [optional]
    r"(?:(?:(?=\S{0,253}(?:$|:))" # check full domain length to be less than or equal to 253 (starting after http basic auth, stopping before port)
    r"((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+" # check for at least one subdomain (maximum length per subdomain: 63 characters), dashes in between allowed
    r"(?:[a-z0-9]{1,63})))" # check for top level domain, no dashes allowed
    r"|localhost)" # accept also "localhost" only
    r"(:\d{1,5})?", # port [optional]
    re.IGNORECASE
)
SCHEME_FORMAT = re.compile(
    r"^(http|hxxp|ftp|fxp)s?$", # scheme: http(s) or ftp(s)
    re.IGNORECASE
)

def validate_url(url: str):
    url = url.strip()

    if not url:
        raise Exception("No URL specified")

    if len(url) > 2048:
        raise Exception("URL exceeds its maximum length of 2048 characters (given length={})".format(len(url)))

    result = urllib.parse.urlparse(url)
    scheme = result.scheme
    domain = result.netloc

    if not scheme:
        raise Exception("No URL scheme specified")

    if not re.fullmatch(SCHEME_FORMAT, scheme):
        raise Exception("URL scheme must either be http(s) or ftp(s) (given scheme={})".format(scheme))

    if not domain:
        raise Exception("No URL domain specified")

    if not re.fullmatch(DOMAIN_FORMAT, domain):
        raise Exception("URL domain malformed (domain={})".format(domain))

    return url

説明

  • コードは、指定されたURLのschemeおよびのnetloc一部のみを検証します。(これを適切に行うために、URLをurllib.parse.urlparse()2つの対応する部分に分割し、対応する正規表現の用語と照合します。)
  • netloc一部には、スラッシュが最初に出現する前に停止し/、そのport数字はまだの一部でありnetloc、例えば:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • IPv4アドレスも検証されます

IPv6サポート

URLバリデーターがIPv6アドレスでも機能するようにするには、次のようにします。

  • Markus Jarderotの回答is_valid_ipv6(ip)から追加。これには、非常に優れたIPv6バリデータ正規表現があります
  • and not is_valid_ipv6(domain)最後に追加if

動作中のnetloc(別名domain)部分の正規表現の例をいくつか示します。


1

上記のソリューションはすべて、「http://www.google.com/path,www.yahoo.com/path」のような文字列を有効であると認識します。このソリューションは常に正常に機能します

import re

# URL-link validation
ip_middle_octet = u"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))"
ip_last_octet = u"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))"

URL_PATTERN = re.compile(
                        u"^"
                        # protocol identifier
                        u"(?:(?:https?|ftp|rtsp|rtp|mmp)://)"
                        # user:pass authentication
                        u"(?:\S+(?::\S*)?@)?"
                        u"(?:"
                        u"(?P<private_ip>"
                        # IP address exclusion
                        # private & local networks
                        u"(?:localhost)|"
                        u"(?:(?:10|127)" + ip_middle_octet + u"{2}" + ip_last_octet + u")|"
                        u"(?:(?:169\.254|192\.168)" + ip_middle_octet + ip_last_octet + u")|"
                        u"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_octet + ip_last_octet + u"))"
                        u"|"
                        # IP address dotted notation octets
                        # excludes loopback network 0.0.0.0
                        # excludes reserved space >= 224.0.0.0
                        # excludes network & broadcast addresses
                        # (first & last IP address of each class)
                        u"(?P<public_ip>"
                        u"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])"
                        u"" + ip_middle_octet + u"{2}"
                        u"" + ip_last_octet + u")"
                        u"|"
                        # host name
                        u"(?:(?:[a-z\u00a1-\uffff0-9_-]-?)*[a-z\u00a1-\uffff0-9_-]+)"
                        # domain name
                        u"(?:\.(?:[a-z\u00a1-\uffff0-9_-]-?)*[a-z\u00a1-\uffff0-9_-]+)*"
                        # TLD identifier
                        u"(?:\.(?:[a-z\u00a1-\uffff]{2,}))"
                        u")"
                        # port number
                        u"(?::\d{2,5})?"
                        # resource path
                        u"(?:/\S*)?"
                        # query string
                        u"(?:\?\S*)?"
                        u"$",
                        re.UNICODE | re.IGNORECASE
                       )
def url_validate(url):   
    """ URL string validation
    """                                                                                                                                                      
    return re.compile(URL_PATTERN).match(url)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.