web-dev-qa-db-ger.com

Was ist der sauberste Weg, um URLs mit Python aus einem String zu extrahieren?

Obwohl ich weiß, dass ich einige reguläre Ausdrücke verwenden könnte, wie den, der hier gepostet wurde, frage ich mich, ob es eine verdammt einfache Möglichkeit gibt, dies entweder mit einem Standardmodul oder einem Add-On eines Drittanbieters zu tun ?

Einfache Frage, aber bei Google (oder Stackoverflow) ist nichts herausgesprungen.

Freuen Sie sich darauf zu sehen, wie Sie dies tun!

23
jkp

Verwenden Sie einen regulären Ausdruck.

Antwort auf Kommentar vom OP: Ich weiß, das ist nicht hilfreich. Ich sage Ihnen den richtigen Weg, um das Problem zu lösen, da Sie sagten, es sei ein regulärer Ausdruck.

8
ddaa

Sehen Sie sich Djangos Herangehensweise hier an: Django.utils.urlize() . Regexps sind für den Job zu begrenzt und Sie müssen Heuristiken verwenden, um Ergebnisse zu erzielen, die meistens richtig sind.

12

Ich weiß, dass es genau das ist, was Sie nicht wollen, aber hier ist eine Datei mit einem großen Regex:

#!/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`!()\[\]{};:'".,<>?«»“”‘’])|(?:(?<[email protected])[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/?([email protected])))"""

Ich nenne diese Datei urlmarker.py und wenn ich sie brauche, importiere ich sie einfach, z.

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

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

Hier ist auch, was Django (1.6) verwendet, um URLFields zu überprüfen:

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)

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

Bei Django 1.9 ist diese Logik auf einige Klassen aufgeteilt

12
dranxo

Es gibt einen hervorragenden Vergleich von 13 verschiedenen Regex-Ansätzen

... die auf dieser Seite zu finden ist: Auf der Suche nach der perfekten URL-Überprüfung regex

Der Diego Perini-Regex, der alle Tests bestanden hat, ist sehr lang, ist aber bei Gist hier erhältlich.
Beachten Sie, dass Sie seine PHP-Version in Python-Regex konvertieren müssen (es gibt geringfügige Unterschiede).

Am Ende habe ich die Imme Emosol -Version verwendet, die die große Mehrheit der Tests bestanden hat und nur einen Bruchteil der Größe von Diego Perini ausmacht.

Hier ist eine Python-kompatible Version des Imme Emosol Regex:

r'^(?:(?:https?|ftp)://)(?:\S+(?::\S*)[email protected])?(?:(?:[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

Sie können diese Bibliothek verwenden, die ich geschrieben habe:

https://github.com/imranghory/urlextractor

Es ist extrem hackig, aber es ist nicht wie bei vielen anderen Techniken auf "http: //" angewiesen. Stattdessen verwendet es die Mozilla TLD-Liste (über die Bibliothek tldextract), um nach TLDs (z. B. ".co.uk", ") zu suchen. com "usw.) im Text und versucht dann, URLs um die TLD herum zu erstellen.

Es zielt nicht darauf ab, RFC-kompatibel zu sein, sondern es ist ziemlich genau, wie URLs in der Praxis in der Praxis verwendet werden. So wird zum Beispiel die technisch gültige Domäne "com" abgelehnt (Sie können eine TLD tatsächlich als Domäne verwenden; in der Praxis ist dies jedoch selten), und die URLs werden durch Leerzeichen oder Kommas ersetzt.

6
Imran

wenn Sie wissen, dass nach einem Leerzeichen in der Zeichenfolge eine URL folgt, können Sie Folgendes tun:

s ist der String, der die URL enthält

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

andernfalls müssen Sie prüfen, ob find -1 zurückgibt oder nicht.

4
sinzi

Sie können BeautifulSoup verwenden.

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

Beachten Sie, dass die Lösung mit Regex schneller ist, jedoch nicht so genau. 

4
Seb

Ich bin spät dran, aber hier ist eine Lösung, die mir jemand von #python auf freenode vorgeschlagen hat. Es vermeidet den Ärger mit 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

Es gibt eine andere Möglichkeit, URLs auf einfache Weise aus Text zu extrahieren. Sie können urlextract verwenden, um es für Sie zu erledigen. Installieren Sie es einfach über pip:

pip install urlextract

und dann kannst du es so benutzen:

from urlextract import URLExtract

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

Weitere Informationen finden Sie auf meiner Github-Seite: https://github.com/lipoja/URLExtract

HINWEIS: Die Liste der TLDs wird von iana.org heruntergeladen, um Sie auf dem neuesten Stand zu halten. Wenn das Programm jedoch keinen Internetzugang hat, ist es nicht für Sie.

Dieser Ansatz ist ähnlich wie in urlextractor (oben erwähnt), aber mein Code ist aktuell, wird beibehalten und ich bin offen für Vorschläge (neue Funktionen).

0
Jan Lipovský