Python - Як перевірити URL-адресу в python? (Неправильне формування чи ні)


116

Я маю 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' не деформується. Схема + ім'я хоста завжди діє.
Віктор Джорас

Відповіді:


90

регекс перевірки URL-адреси django ( джерело ):

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 sitedomain не є дійсною URL-адресою. Музей є тому, що .museum є доменом верхнього рівня (ICANN [1] визначає їх), а не сайтоміном. [1] icann.org
glarrain

1
Цей, здається, не працює з іменами користувача: password@example.com URL-адреси стилів
Адам Бакстер


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
Але це буде працювати лише в середовищі джанго не інакше.
Yugal Jindle

19
verify_existsзастаріло. -1
g33kz0r

2
Додати: з django.conf import settings settings.configure (DEBUG = False) і видаліть verify_exists, щоб він працював з django 1.5
Dukeatcoding

1
@YugalJindle Правильно, але зняти його з Django майже тривіально: D. Отже, я використовую цей метод
swdev

7
Зауважте, що при django> = 1,5 більше verify_existsнемає. Також замість valзмінної можна назвати її такURLValidator()('http://www.google.com')
luckydonald

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

Встановіть його з PyPI з допомогою pip ( pip install validators).


5
Це призведе до помилки для URL-адрес файлів. Як і "файл: ///users/file.txt"
Деваврата

2
Помилка для URL-адрес localhost validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Том

5
@Lal Zada, перед тим, як заявити щось подібне, докладіть певних зусиль і перевірте код, насправді regexp дуже хороший: validators.readthedocs.io/en/latest/_modules/validators/…
Drachenfels

1
Перевірка fn пакета має безліч довільних обмежень, тож це жахлива порада запропонувати його як загальне рішення.
ivan_pozdeev

2
@ivan_pozdeev: якщо це жахливо, то запропонувати краще рішення
Джабб

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 зі списком елементів, що не містять None. Це корисно. Також +1 за використання вбудованого модуля
Marc Maxmeister

9
Це дозволяє все. Він повертається Trueдля рядка fakeабо навіть для порожнього рядка. Ніколи не буде помилок, оскільки ці атрибути завжди є, і список завжди матиме булеве значення True, оскільки він містить ці атрибути. Навіть якщо всі атрибути є None, список все одно буде не порожнім. Вам потрібна певна перевірка атрибутів, оскільки все проходить так, як у вас це є зараз.
zondo

3
Списки помилкових об'єктів оцінюють на Істинне: 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.")друкує "Я неправдивий". тому порожні списки є помилковими. Вміст масиву потребує оцінки за допомогою allфункції, подібної до функції.
dmmfll

3
Не впевнений, чому вам потрібен такий шлях. Вам слід зняти result.pathз тесту.
Джеріна

1
Це досить добре для мене, дякую. Я щойно додав просту перевірку для scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Олександр Фортін

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

1
Це тільки добре , щоб розділити компоненти в окремому випадку, коли URI , як відомо, НЕ бути неправильно сформований. Як я раніше відповів на іншу подібну відповідь, це підтверджує неправильні URI, наприклад https://https://https://www.foo.bar.
ingyhere

9

Примітка - lepl більше не підтримується, вибачте (ви можете використовувати його, і я думаю, що код нижче працює, але оновлення не збирається).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html визначає, як це зробити (для URL-адрес http та електронної пошти). Я реалізував свої рекомендації в python, використовуючи lepl (бібліотеку аналізатора). див. 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
ЮЛППИ тепер зняті з автором 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.

Дивіться 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 (схема = '', netloc = '', path = 'dkakasdkjdjakdjadjfalskdjfalk', params = '', query = '', fragment = '')

ParseResult (схема = '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

EDIT

Як вказував @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()повертає true, якщо всі змінні всередині нього повертають true. Отже, якщо result.schemeі result.netlocприсутній, тобто має деяке значення, то URL-адреса є дійсною і, отже, повертається True.


О, приємний улов .. Я, мабуть, повинен повернути свій код назад. Що ви віддаєте перевагу, чи є інші варіанти, крім регулярного вираження.
Padam Sethia

https://www.googleє дійсною URL-адресою. Насправді це може не вирішитись, але якщо ви переймаєтесь цим, вам потрібно зробити перевірку DNS.
Блейз

ластівки винятку
ivan_pozdeev

2

Підтвердьте URL-адресу за допомогою urllibі Django-подібного регулярного виразу

Регекс перевірки URL-адрес Джанго насправді був досить гарним, але мені потрібно було трохи підправити його для мого використання. Сміливо адаптуйте це до вашого!

Пітон 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()на дві частини, які потім узгоджуються з відповідними термінами регулярного вираження.)
  • 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)з відповіді Маркуса Джардеро , який має дійсно хороший регекс валідатора 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.