【问题标题】:What's the cleanest way to extract URLs from a string using Python?使用 Python 从字符串中提取 URL 的最简洁方法是什么?
【发布时间】:2010-10-05 22:09:26
【问题描述】:

虽然我知道我可以使用一些巨大的正则表达式,例如发布在here 上的正则表达式,但我想知道是否有一些非常巧妙的方法可以使用标准模块或第三方插件来做到这一点?

简单的问题,但在 Google(或 Stackoverflow)上没有跳出来。

期待看到你们如何做到这一点!

【问题讨论】:

标签: python regex url


【解决方案1】:

我知道这正是您不想要的,但这里有一个包含大量正则表达式的文件:

#!/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/?(?!@)))"""

我将该文件称为urlmarker.py,当我需要它时,我只需导入它,例如。

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

参见。 http://daringfireball.net/2010/07/improved_regex_for_matching_urls

此外,这里是 Django (1.6) 用来验证 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)

参见。 https://github.com/django/django/blob/1.6/django/core/validators.py#L43-50

Django 1.9 将该逻辑拆分为几个类

【讨论】:

  • 此 URL_REGEX 将 df.info 识别为 URL。
【解决方案2】:

还有另一种方法可以轻松地从文本中提取 URL。您可以使用 urlextract 为您完成,只需通过 pip 安装即可:

pip install urlextract

然后你可以像这样使用它:

from urlextract import URLExtract

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

您可以在我的 github 页面上找到更多信息:https://github.com/lipoja/URLExtract

注意:它会从 iana.org 下载 TLD 列表,以使您随时了解最新信息。但如果该程序无法访问互联网,那么它不适合您。

这种方法与 urlextractor(上面提到的)类似,但我的代码是最新的、维护的,我愿意接受任何建议(新功能)。

【讨论】:

  • 不是吹毛求疵,但stackoverflow.com 不是一个 URL,它是一个主机名。坦率地说,我不喜欢通过将所有可能的东西变成链接来试图看起来更智能的应用程序。如今,使用 TLD 更容易出错。
  • 我同意。在示例中,它不是 URL,只是主机名。我写了这个库,因为它是我弄清楚如何从纯文本中提取 URL(主机名)的一种方式。 TLD 是 URL 或主机名中唯一易于识别、易于匹配的纯文本部分。当周围的文字符合某些标准时,您可以说您找到了 URL
  • 早期,我们常说command.com,但今天,我更可能提到一些example.sh或我命名为example.name的文件(因为它包含名称)。有时我会忘记 period.net 之后的空间增益大致相同。 我知道,除了技术术语外,这并不常见,但仍然没有 URL,所以看到它们的软件是错误的,我没有希望 my 通讯中断。 (一些读者对笑脸的处理已经足够了——你敢用 B 结束括号)。
  • "...但是仍然没有 URL,所以看到它们的软件是错误的" - 我的看法不同。主机名是 URL 的一部分。假设这个软件默认可以找到主机名。在上面的示例中,我可以看到四个主机名:command.com、example.sh、example.name、period.net。我认为这四个是有效的主机名。还是我错了?另一方面,该软件被编写为 python 类,具有指定 URL 搜索的方法。因此,您可以根据自己的需要进行设置。
  • 我的意思是,它是 is 还是 isnot 主机名取决于上下文。比较:Jan LipovskýMon Jan 24。人类擅长在上下文中检测模式,机器在这方面很差。通过仅匹配主机名,您正在实施“幼稚”规则,这将不可避免地(只是打错字或使用足够奇怪的行话)是错误的,从而导致与人类直觉的冲突,即。你的软件看起来很傻,你失去了用户体验点。
【解决方案3】:
import re
text = '<p>Please click <a href="http://www.dr-chuck.com">here</a></p>'
aa=re.findall('href="(.+)"',text)
print(aa)

【讨论】:

  • 这不会检测 url,而是检测 HTML 链接。 HTML 链接字符串本质上是 url 字符串的特定子集。
【解决方案4】:

我迟到了,但这里有一个来自 freenode 上#python 的人向我建议的解决方案。它避免了正则表达式的麻烦。

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

【讨论】:

  • 这依赖于 url 方案的存在。这在某些结构良好的情况下是理想的,但在其他情况下完全没用。前者的示例:用户生成的字符串。
【解决方案5】:

对 13 种不同的正则表达式方法进行了很好的比较

...可以在此页面找到:In search of the perfect URL validation regex

通过了所有测试的 Diego Perini 正则表达式非常长,但可从他的 gist here 获得。
请注意,您必须将他的 PHP 版本转换为 python 正则表达式(略有不同)。

我最终使用了Imme Emosol 版本,它通过了绝大多数测试,并且是 Diego Perini 的一小部分。

这是 Imme Emosol 正则表达式的 python 兼容版本:

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]*)?$'

【讨论】:

  • 当我输入字符串 Assume that this regex will be used for a public URL shortener written in PHP, so URLs like http://localhost/, https://www.webdesignerdepot.com/2012/10/creating-a-modal-window-with-html5-and-css3/, //foo.bar/, ://foo.bar/, data:text/plain;charset=utf-8,OHAI and tel:+1234567890 shouldn’t pass (even though they’re technically valid). Also, in this case I only want to allow the HTTP, HTTPS and FTP protocols. 时,无法恢复任何有效的 url 该列表中至少有 1 个有效的 url。
【解决方案6】:

在这里查看 Django 的方法:django.utils.urlize()。正则表达式对于这项工作来说太有限了,您必须使用启发式方法来获得大部分正确的结果。

【讨论】:

    【解决方案7】:

    你可以使用我写的这个库:

    https://github.com/imranghory/urlextractor

    它非常 hacky,但它不像许多其他技术那样依赖“http://”,而是使用 Mozilla TLD 列表(通过 tldextract 库)来搜索 TLD(即“.co.uk” 、“.com”等),然后尝试围绕 TLD 构建 url。

    它的目标不是要符合 RFC,而是要准确地说明 url 在现实世界中的实际使用方式。因此,例如,它将拒绝技术上有效的域“com”(您实际上可以将 TLD 用作域;尽管在实践中很少见),并且会从 url 中删除句号或逗号。

    【讨论】:

    • 有趣。有没有办法摆脱esm?我想在 python 3 中使用它。
    【解决方案8】:

    您可以使用BeautifulSoup

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

    请注意,使用正则表达式的解决方案更快,但不会那么准确。

    【讨论】:

    • Sebastian:我知道 BeautifulSoup,但问题是它只会提取锚定的 URL。我正在尝试在纯文本中搜索任何 URL 之类的东西。不过感谢您的建议。
    【解决方案9】:

    如果您知道字符串中的空格后面有一个 URL,您可以执行以下操作:

    s 是包含 url 的字符串

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

    否则你需要检查 find 是否返回 -1。

    【讨论】:

    • ... 或 ws:, git:, ftp:, mailto:, jabber: 等等等等。
    猜你喜欢
    • 2015-04-11
    • 1970-01-01
    • 2011-05-20
    • 2019-12-27
    • 1970-01-01
    • 2016-06-22
    • 2010-12-08
    • 1970-01-01
    • 2015-11-29
    相关资源
    最近更新 更多