Python - จะตรวจสอบ url ใน python ได้อย่างไร? (ผิดรูปแบบหรือไม่)


117

ฉันได้รับurlจากผู้ใช้และฉันต้องตอบกลับด้วย HTML ที่ดึงมา

ฉันจะตรวจสอบว่า URL ผิดรูปแบบหรือไม่?

ตัวอย่างเช่น :

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

เราจะบรรลุสิ่งนี้ได้อย่างไร?



1
เพียงแค่ลองอ่านหากตัวอย่างเช่น HTTplib มีข้อยกเว้นคุณจะรู้ว่ามันไม่ถูกต้อง URL ที่มีรูปแบบไม่ถูกต้องทั้งหมดจะถูกต้อง !
carlpett

1
สิ่งนี้จะช่วยคุณได้: stackoverflow.com/questions/827557/…
DhruvPathak

10
url='http://google' ไม่ผิดรูปแบบ Schema + ชื่อโฮสต์ถูกต้องเสมอ
Viktor Joras

สิ่งนี้ตอบคำถามของคุณหรือไม่? คุณตรวจสอบ URL ด้วยนิพจน์ทั่วไปใน Python ได้อย่างไร
AMC

คำตอบ:


90

django url validation regex (ที่มา ):

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หรือไม่? หรือฉันมี django เวอร์ชั่นเก่า?
Ruggero Turra

2
@ yugal-jindle sitedomainไม่ใช่ URL ที่ถูกต้อง พิพิธภัณฑ์เป็นเพราะ. พิพิธภัณฑ์เป็นโดเมนระดับบนสุด (ICANN [1] กำหนดสิ่งเหล่านี้) ไม่ใช่อภิมหาอาณาจักร [1] icann.org
glarrain

1
ดูเหมือนว่าจะใช้ไม่ได้กับชื่อผู้ใช้: password@example.com style URLs
Adam Baxter


2
สิ่งนี้จะใช้ไม่ได้กับ URL IPv6 ซึ่งมีรูปแบบ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 หรือไม่


46
แต่สิ่งนี้จะใช้ได้เฉพาะในสภาพแวดล้อม django เท่านั้นไม่ใช่อย่างอื่น
Yugal Jindle

19
verify_existsเลิกใช้แล้ว -1
g33kz0r

2
เพิ่ม: จาก django.conf import settings settings.configure (DEBUG = False) และลบ valid_exists ออกเพื่อให้ทำงานกับ django 1.5
Dukeatcoding

1
@YugalJindle ถูกต้อง แต่การลอกออกจาก Django นั้นแทบจะไม่สำคัญเลย: D เลยใช้วิธีนี้
swdev

7
หมายเหตุด้วย django> = 1.5 ไม่มีverify_existsอีกต่อไป นอกจากนี้valคุณสามารถเรียกมันว่าตัวแปรแทนURLValidator()('http://www.google.com')
luckydonald

122

ใช้แพ็คเกจvalidators :

>>> 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
>>>

ติดตั้งจาก PyPIด้วย pip ( pip install validators)


5
มันจะส่งข้อผิดพลาดสำหรับ URL ของไฟล์ ชอบ "file: ///users/file.txt"
Devavrata

2
URL ของ localhost ล้มเหลว validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal Zada ​​ก่อนที่คุณจะอ้างสิทธิ์อะไรแบบนี้ลองใช้ความพยายามและตรวจสอบโค้ด regexp ค่อนข้างดีจริงๆ: validators.readthedocs.io/en/latest/_modules/validators/…
Drachenfels

1
การตรวจสอบ fn ของแพ็กเกจมีข้อ จำกัด หลายประการดังนั้นจึงเป็นคำแนะนำที่แย่มากที่จะแนะนำให้ใช้เป็นวิธีแก้ปัญหาทั่วไป
ivan_pozdeev

2
@ivan_pozdeev: ถ้ามันแย่มากแนะนำวิธีแก้ปัญหาที่ดีกว่า
Jabba

62

เวอร์ชันจริงหรือเท็จขึ้นอยู่กับคำตอบ @DMfll:

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
ฉันไม่ทราบว่าคุณสามารถทดสอบคำสั่ง if ด้วยรายการองค์ประกอบที่ไม่ใช่ไม่มีได้ นั่นเป็นประโยชน์ +1 สำหรับการใช้โมดูลในตัว
Marc Maxmeister

9
สิ่งนี้ช่วยให้ทุกอย่าง มันส่งกลับTrueสำหรับสตริงfakeหรือแม้กระทั่งสำหรับสตริงว่าง จะไม่มีข้อผิดพลาดใด ๆ เนื่องจากแอตทริบิวต์เหล่านั้นอยู่ที่นั่นเสมอและรายการจะมีค่าบูลีนเป็น True เสมอเนื่องจากมีแอตทริบิวต์เหล่านั้น แม้ว่าแอตทริบิวต์ทั้งหมดจะไม่มี แต่รายการจะยังคงไม่ว่างเปล่า คุณต้องมีการตรวจสอบความถูกต้องของแอตทริบิวต์เพราะทุกอย่างผ่านไปแล้ว
zondo

3
รายการวัตถุเท็จประเมินค่าเป็น True: print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")พิมพ์ "ฉันคือความจริง" เมื่อฉันเรียกใช้ มักจะประเมิน[result.scheme, result.netloc, result.path] พิมพ์ว่า "ฉันเป็นเท็จ" รายการที่ว่างเปล่าจึงเป็นเท็จ เนื้อหาของอาร์เรย์ต้องการการประเมินผลด้วยฟังก์ชัน Trueprint("I am True") if [] else print("I am False.")all
dmmfll

3
ไม่แน่ใจว่าทำไมคุณถึงต้องการเส้นทางเช่นนั้น คุณควรลบออกresult.pathจากการทดสอบ
Jerinaw

1
ดีพอสำหรับฉันขอบคุณ ฉันเพิ่งเพิ่มการตรวจสอบง่ายๆสำหรับscheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin

20

ทุกวันนี้ฉันใช้สิ่งต่อไปนี้ตามคำตอบของ Padam:

$ 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
Björn Lindqvist

1
นี่เป็นสิ่งที่ดีในการแยกส่วนประกอบในกรณีพิเศษที่ทราบว่า URI ไม่ผิดรูปแบบ ในฐานะที่ผมตอบก่อนหน้านี้เพื่อคำตอบที่คล้ายกันอื่น ๆ ตรวจสอบนี้ผิดรูปแบบ URI https://https://https://www.foo.barที่เช่น
นี่

9

บันทึก - ไม่รองรับ lepl อีกต่อไปขออภัย (คุณสามารถใช้งานได้และฉันคิดว่าโค้ดด้านล่างใช้งานได้ แต่จะไม่ได้รับการอัปเดต)

rfc 3696 http://www.faqs.org/rfcs/rfc3696.htmlกำหนดวิธีการดำเนินการนี้ (สำหรับ http urls และอีเมล) ฉันใช้คำแนะนำใน python โดยใช้ lepl (ไลบรารี parser) ดู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 ล่ะ?
Adam Parkin

6
คุณยังไม่ได้แยกโค้ดและนำไปใช้? มันเป็นโอเพ่นซอร์ส
andrew cooke

1
lepl ถูกยกเลิกโดยผู้เขียนacooke.org/lepl/discontinued.htmlแก้ไข: เดี๋ยวก่อนเพิ่งรู้ว่าคุณเป็นผู้เขียน
Emmett Butler

1
หมายเหตุ: lepl.apps.rfc3696 ไม่ทำงานใน Python 3.7.4
Sheile

9

ฉันเข้ามาที่หน้านี้เพื่อพยายามหาวิธีที่ดีในการตรวจสอบสตริงเป็น URL "ถูกต้อง" ฉันแบ่งปันวิธีแก้ปัญหาของฉันที่นี่โดยใช้ python3 ไม่จำเป็นต้องมีห้องสมุดเพิ่มเติม

ดูhttps://docs.python.org/2/library/urlparse.htmlหากคุณใช้ python2

ดูhttps://docs.python.org/3.0/library/urllib.parse.htmlถ้าคุณใช้ python3 เหมือนฉัน

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" ไม่มีแบบแผนหรือ netloc

" 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 ชี้ไว้รหัสด้านล่างนี้จะตรวจสอบความถูกต้องของ URL แม้ว่าจะไม่มี.comหรือ.coอื่น ๆ ก็ตาม

ยังระบุโดย @Blaise URL เช่นhttps://www.googleเป็น 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()คืนค่าจริงหากตัวแปรทั้งหมดที่อยู่ในนั้นส่งคืนจริง ดังนั้นถ้าresult.schemeและresult.netlocเป็นปัจจุบันเช่นมีค่าบางอย่างแล้ว URL Trueที่ถูกต้องและด้วยเหตุนี้ผลตอบแทน


โอ้ดีจับ .. ฉันเดาว่าฉันต้องเอารหัสของฉันคืน คุณต้องการอะไรมีตัวเลือกอื่น ๆ ยกเว้น regex
Padam Sethia

https://www.googleเป็น URL ที่ถูกต้อง อาจไม่สามารถแก้ไขได้จริง แต่ถ้าคุณสนใจว่าคุณต้องทำการตรวจสอบ DNS
Blaise

ข้อยกเว้นสำหรับนกนางแอ่น
ivan_pozdeev

2

ตรวจสอบ URL ด้วยurllibและ regex ที่เหมือน Django

regex การตรวจสอบ URL Django นั้นค่อนข้างดี แต่ฉันต้องปรับแต่งเล็กน้อยสำหรับกรณีการใช้งานของฉัน อย่าลังเลที่จะปรับให้เข้ากับของคุณ!

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

คำอธิบาย

  • รหัสนี้ตรวจสอบความถูกต้องschemeและnetlocบางส่วนของ URL ที่ระบุเท่านั้น (ในการดำเนินการนี้อย่างถูกต้องฉันแบ่ง URL ออกurllib.parse.urlparse()เป็นสองส่วนตามส่วนที่จับคู่กับคำศัพท์ regex ที่เกี่ยวข้อง)
  • netlocส่วนหนึ่งหยุดก่อนที่จะเกิดขึ้นครั้งแรกของการเฉือน/ดังนั้นportตัวเลขยังคงเป็นส่วนหนึ่งของnetlocเช่น:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • ที่อยู่ IPv4 ได้รับการตรวจสอบความถูกต้องด้วย

รองรับ IPv6

หากคุณต้องการให้เครื่องมือตรวจสอบ URL ทำงานกับที่อยู่ IPv6 ให้ทำดังต่อไปนี้:

  • เพิ่มis_valid_ipv6(ip)จากคำตอบของ Markus Jarderotซึ่งมี regex ตัวตรวจสอบ IPv6 ที่ดีมาก
  • เพิ่มand not is_valid_ipv6(domain)ในรายการสุดท้ายif

ตัวอย่าง

นี่คือตัวอย่างบางส่วนของ regex สำหรับส่วนnetloc(aka domain) ในการดำเนินการ:


2

วิธีแก้ปัญหาทั้งหมดข้างต้นถือว่าสตริง " 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)

google.com/path,www.yahoo.com/path เป็นที่ถูกต้อง ดูRFC 3986 : กpathทำจากsegments ซึ่งจะถูกสร้างขึ้นจากpchars ซึ่งอาจจะเป็นหนึ่งในนั้นคือsub-delims ","
Anders Kaseorg

ใช่สัญลักษณ์ "," รวมอยู่ในรายการตัวคั่นย่อยที่ยอมรับได้ แต่เส้นจากตัวอย่างของฉันแม้จะอยู่ในความฝันอันเลวร้ายก็ไม่สามารถเป็น url ที่ถูกต้องได้ =)
СергейДорофий
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.