使用Python3从字符串中提取所有的URL

4

我正在寻找一种干净的方法来提取文本字符串中的所有URL。

经过广泛的搜索,我发现许多帖子建议使用正则表达式来完成这个任务,并给出了应该执行此操作的正则表达式。每个正则表达式都有一些优点和一些缺点。此外,编辑它们以更改它们的行为并不直观。无论如何,在这一点上,我对任何能够正确检测此文本中的URL的正则表达式感到满意:

输入:

Lorem ipsum dolor sit amet https://www.lorem.com/ipsum.php?q=suas, nusquam tincidunt ex per, ius modus integre no, quando utroque placerat qui no. Mea conclusionemque vituperatoribus et, omnes malorum est id, pri omnes atomorum expetenda ex. Elit pertinacia no eos, nonumy comprehensam id mei. Ei eum maiestatis quaerendum https://www.lorem.org. Pri posse constituam in, sit http://news.bbc.co.uk omnium assentior definitionem ei. Cu duo equidem meliore qualisque。

输出:

['https://www.lorem.com/ipsum.php?q=suas', 'https://www.lorem.org', 'http://news.bbc.co.uk']

但是,如果有一个Python3类/函数/库,可以在给定文本中查找所有URL并接受参数来:

  1. 选择要检测的协议
  2. 选择允许的TLD
  3. 选择允许的域名

我会非常高兴知道它。


1
我觉得你在写问题标题的时候睡着了。 - user6165050
也许吧。所以,我已经编辑了问题的标题... - Ouss
5个回答

6
除了其他人提到的之外,鉴于您要求已经存在的东西,您可能想尝试使用URLExtract。 显然,它会尝试在给定文本中找到任何出现的TLD。 如果找到TLD,则从该位置开始扩展边界搜索“停止字符”(通常是空格、逗号、单引号或双引号)。
您可以在这里看到几个示例。
from urlextract import URLExtract

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

看起来这个模块还有一个update()方法,可以让你更新TLD列表缓存文件。

然而,如果那不符合您的具体要求,您可以在使用上述模块(或任何其他解析URL的方式)处理完URL后手动进行一些检查。例如,假设您得到了一个URL列表:

result = ['https://www.lorem.com/ipsum.php?q=suas', 'https://www.lorem.org', 'http://news.bbc.co.uk'] 

接下来,您可以构建另一个列表,其中包含被排除的域名/TLD等:

allowed_protocols = ['protocol_1', 'protocol_2']
allowed_tlds = ['tld_1', 'tld_2', 'tld_3']
allowed_domains = ['domain_1']

for each_url in results:
    # here, check each url against your rules

3
import re
import string
text = """
Lorem ipsum dolor sit amet https://www.lore-m.com/ipsum.php?q=suas, 
nusquam tincidunt ex per, ftp://link.com ius modus integre no, quando utroque placerat qui no. 
Mea conclusionemque vituperatoribus et, omnes malorum est id, pri omnes atomorum expetenda ex. 
Elit ftp://link.work.in pertinacia no eos, nonumy comprehensam id mei. Ei eum maiestatis quaerendum https://www.lorem.org. 
Pri posse constituam in, sit http://news.bbc.co.uk omnium assentior definitionem ei. Cu duo equidem meliore 
qualisque.
"""

URL_REGEX = r"""((?:(?:https|ftp|http)?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|org|uk)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\-][a-z0-9]+)*[.](?:com|uk|ac)\b/?(?!@)))"""

urls = re.findall(URL_REGEX, text)
print([''.join(x for x in url if x in string.printable) for url in urls])

现在,如果您想仅保留具有有效域名的URL,则可以按照以下方式编写代码:
VALID_DOMAINS = ['lorem.org', 'bbc.co.uk', 'sample.com', 'link.net']
valid_urls = []
for url in result_url:
    for val_domain in VALID_DOMAINS:
        if val_domain in url:
            valid_urls.append(url)
print(valid_urls)

2
如果你想要一个正则表达式,你可以使用这个:
import re


string = "Lorem ipsum dolor sit amet https://www.lorem.com/ipsum.php?q=suas, nusquam tincidunt ex per, ius modus integre no, quando utroque placerat qui no. Mea conclusionemque vituperatoribus et, omnes malorum est id, pri omnes atomorum expetenda ex. Elit pertinacia no eos, nonumy comprehensam id mei. Ei eum maiestatis quaerendum https://www.lorem.org. Pri posse constituam in, sit http://news.bbc.co.uk omnium assentior definitionem ei. Cu duo equidem meliore qualisque."

result = re.findall(r"\w+://\w+\.\w+\.\w+/?[\w\.\?=#]*", string)
print(result)

输出:

['https://www.lorem.com/ipsum.php?q=suas', 
 'https://www.lorem.org', 
 'http://news.bbc.co.uk']

2
请再次检查您的结果是否与您编写的不同,它应该是['https://www.lorem.com/', 'https://www.lorem.org.', 'http://news.bbc.co.'] - Gahan
哦,抱歉,我在复制和粘贴代码时打错了一个字母,将 \w 写成了 \d。@Gahan,谢谢你发现了这个错误。 - Taku

1

使用现有的库可能是最佳的解决方案。

但这对于我的小脚本来说太多了,并且受到@piotr-wasilewicz的答案的启发,我想出了以下解决方案:

from string import ascii_letters
links = [x for x in line.split() if x.strip(str(set(x) - set(ascii_letters))).startswith(('http', 'https', 'www'))]
  • 对于每个字符串中的单词,
  • 从单词本身中去除(开头和结尾处)非 ASCII 字母,并筛选以 https、http、www 开头的单词。

这段代码有些密集,但应该可以快速检测出字符串中的大多数“健全”的 URL。


1
output = [x for x in input().split() if x.startswith('http://') or x.startswith('https://') or x.startswith('ftp://')]
print(output)

您的例子:http://ideone.com/wys57x

最后,如果列表元素的最后一个字符不是字母,您也可以将其删除。

编辑:

output = [x for x in input().split() if x.startswith('http://') or x.startswith('https://') or x.startswith('ftp://')]
newOutput = []
for link in output:
    copy = link
    while not copy[-1].isalpha():
        copy = copy[:-1]
    newOutput.append(copy)
print(newOutput)

您的示例:http://ideone.com/gHRQ8w

谢谢回复。您的方法未能排除“https://www.lorem.org”中的表情符号。 - Ouss
不,只需“如果列表元素的最后一个字符不是字母,则删除该字符。” - Piotr Wasilewicz
@Ouss,请现在检查我的答案。 - Piotr Wasilewicz
也可以尝试这个: content = input.split(' ') newOutput = [] for val in content: if val.startswith('http://') or val.startswith('https://'): newOutput.append(val) - Anubhav Singh
@AnubhavSingh 我认为使用列表推导式更好。还有 content = input().split(' ') :) - Piotr Wasilewicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接