web-dev-qa-db-fra.com

Quel est le moyen le plus propre d'extraire les URL d'une chaîne à l'aide de Python?

Bien que je sache que je pourrais utiliser certaines expressions rationnelles hugeass telles que celle publiée ici je me demande s’il existe une façon très fictive de le faire avec un module standard ou peut-être un complément tiers?

Question simple, mais rien n'a sauté sur Google (ou Stackoverflow).

J'ai hâte de voir comment vous allez faire ça!

23
jkp

Utilisez une expression régulière.

Réponse au commentaire de l'OP: Je sais que ce n'est pas utile. Je vous dis la bonne façon de résoudre le problème car vous avez dit que c'était d'utiliser une expression régulière.

8
ddaa

Regardez l'approche de Django ici: Django.utils.urlize() . Les expressions régulières sont trop limitées pour le travail et vous devez utiliser des méthodes heuristiques pour obtenir des résultats généralement corrects.

12
Björn Lindqvist

Je sais que c'est exactement ce que vous ne voulez pas mais voici un fichier avec un regex énorme:

#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
the web url matching regex used by markdown
http://daringfireball.net/2010/07/improved_regex_for_matching_urls
https://Gist.github.com/gruber/8891611
"""
URL_REGEX = r"""(?i)\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|Gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\-][a-z0-9]+)*[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|Gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)\b/?(?!@)))"""

J'appelle ce fichier urlmarker.py et quand j'en ai besoin, je l'importe simplement, par exemple.

import urlmarker
import re
re.findall(urlmarker.URL_REGEX,'some text news.yahoo.com more text')

cf. http://daringfireball.net/2010/07/improved_regex_for_matching_urls

Aussi, voici ce que Django (1.6) utilise pour valider URLFields:

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 ipv4
    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
    r'(?::\d+)?'  # optional port
    r'(?:/?|[/?]\S+)$', re.IGNORECASE)

cf. https://github.com/Django/django/blob/1.6/Django/core/validators.py#L43-50

Django 1.9 a cette logique divisée en quelques classes

12
dranxo

Il y a une excellente comparaison de 13 approches de regex différentes

... qui peut être trouvé à cette page: À la recherche de la regex de validation d'URL parfaite

La regex de Diego Perini, qui a passé tous les tests, est very long mais est disponible sur son Gist ici .
Notez que vous devrez convertir sa version PHP en regex python (il existe de légères différences).

J'ai fini par utiliser la version Imme Emosol qui passe la grande majorité des tests et représente une fraction de la taille de celle de Diego Perini.

Voici une version compatible avec Python de l'expression régulière Imme Emosol:

r'^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$'
12
tohster

Vous pouvez utiliser cette bibliothèque, j'ai écrit:

https://github.com/imranghory/urlextractor

C'est extrêmement difficile, mais il ne s'appuie pas sur "http: //" comme beaucoup d'autres techniques, mais utilise la liste des TLD de Mozilla (via la bibliothèque tldextract) pour rechercher des TLD (c'est-à-dire ".co.uk", "." com ", etc.) dans le texte et tente ensuite de construire des URL autour du TLD.

Il ne vise pas à être conforme aux RFC, mais à préciser la manière dont les URL sont utilisées dans la pratique. Ainsi, par exemple, il rejettera le domaine techniquement valide "com" (vous pouvez en fait utiliser un TLD comme domaine; bien que ce soit rare dans la pratique) et effacera les balises de fond ou les virgules.

6
Imran

si vous savez qu'il y a une URL après un espace dans la chaîne, vous pouvez faire quelque chose comme ceci:

s est la chaîne contenant l'url

>>> t = s[s.find("http://"):]
>>> t = t[:t.find(" ")]

sinon, vous devez vérifier si find renvoie -1 ou non.

4
sinzi

Vous pouvez utiliser BeautifulSoup .

def extractlinks(html):
    soup = BeautifulSoup(html)
    anchors = soup.findAll('a')
    links = []
    for a in anchors:
        links.append(a['href'])
    return links

Notez que la solution avec les expressions rationnelles est plus rapide, mais ne sera pas aussi précise. 

4
Seb

Je suis en retard à la soirée, mais voici une solution qui m'a été suggérée par quelqu'un de #python on freenode. Cela évite les tracas de regex.

from urlparse import urlparse

def extract_urls(text):
    """Return a list of urls from a text string."""
    out = []
    for Word in text.split(' '):
        thing = urlparse(Word.strip())
        if thing.scheme:
            out.append(Word)
    return out
3
Shatnerz
import re
text = '<p>Please click <a href="http://www.dr-chuck.com">here</a></p>'
aa=re.findall('href="(.+)"',text)
print(aa)
0
zlin

Il existe un autre moyen d'extraire facilement les URL du texte. Vous pouvez utiliser urlextract pour le faire pour vous, installez-le simplement via pip:

pip install urlextract

et ensuite vous pouvez l'utiliser comme ceci:

from urlextract import URLExtract

extractor = URLExtract()
urls = extractor.find_urls("Let's have URL stackoverflow.com as an example.")
print(urls) # prints: ['stackoverflow.com']

Vous pouvez trouver plus d’informations sur ma page github: https://github.com/lipoja/URLExtract

NOTE: Il télécharge la liste des TLD de iana.org pour vous tenir au courant. Mais si le programme n'a pas accès à Internet, alors ce n'est pas pour vous.

Cette approche est similaire à celle utilisée dans urlextractor (mentionné ci-dessus), mais mon code est récent, maintenu et je suis ouvert à toute suggestion (nouvelles fonctionnalités).

0
Jan Lipovský