如何使用正则表达式提取字符串中的所有链接或锚点标签?

-1 投票
5 回答
1826 浏览
提问于 2025-04-17 05:26

我看到过其他问题,它们可以从字符串中提取所有普通链接或者所有锚标签,但没有看到能同时做到这两点的。

理想情况下,我希望这个正则表达式能够解析像这样的字符串(我在用Python):

>>> import re
>>> content = '
    <a href="http://www.google.com">http://www.google.com</a> Some other text.
    And even more text! http://stackoverflow.com
    '
>>> links = re.findall('some-regular-expression', content)
>>> print links
[u'http://www.google.com', u'http://stackoverflow.com']

有没有可能写出一个正则表达式,能避免返回重复的链接?有没有更好的方法来实现这个?

5 个回答

0

写一个能匹配所有有效网址的正则表达式是个挺棘手事情

如果你只是想在一个任意字符串中找出简单的http/https网址,我可以给你这个解决方案:

>>> import re
>>> content = '<a href="http://www.google.com">http://www.google.com</a> Some other text. And even more text! http://stackoverflow.com'
>>> re.findall(r"https?://[\w\-.~/?:#\[\]@!$&'()*+,;=]+", content)
['http://www.google.com', 'http://www.google.com', 'http://stackoverflow.com']

这个表达式会查找以http://或https://开头,后面跟着一个或多个有效字符的字符串。

为了避免重复的条目,可以使用set()

>>> list(set(re.findall(r"https?://[\w\-.~/?:#\[\]@!$&'()*+,;=]+", content)))
['http://www.google.com', 'http://stackoverflow.com']
1

通常情况下,你不应该用正则表达式来解析HTML,因为HTML并不是一种规则语言。在这里,看起来你只是想获取所有的http链接,无论它们是在A标签里还是在文本中。那不如先把它们都找出来,然后再去掉重复的链接呢?

试试下面的代码:

set(re.findall("(http:\/\/.*?)[\"' <]", content))

看看这样是否能满足你的需求。

1

无论你怎么做,结果都会有点乱。不过,一个大概90%有效的解决方案可能看起来像这样:

r'<a\s[^>]*>([^<]*)</a>|\b(\w+://[^<>\'"\t\r\n\xc2\xa0]*[^<>\'"\t\r\n\xc2\xa0 .,()])'

因为这个模式有两个部分,所以它会返回一个包含两个元素的列表。要把它们合并在一起,你可以用列表推导式,或者直接用一个映射函数:

map(''.join, re.findall(pattern, content))

如果你想要的是链接的src属性,而不是链接的文本,那么这个模式就会变得更加复杂:

r'<a\s[^>]*src=[\'"]([^"\']*)[\'"][^>]*>[^<]*</a>|\b(\w+://[^<>\'"\t\r\n\xc2\xa0]*[^<>\'"\t\r\n\xc2\xa0 .,()])'

另外,你也可以让模式的后半部分直接提取src属性,这样就不需要再进行字符串的合并了:

r'\b\w+://[^<>\'"\t\r\n\xc2\xa0]*[^<>\'"\t\r\n\xc2\xa0 .,()]'

一旦你有了这些基础,你就可以把找到的链接替换成看起来不像链接的东西,搜索'://',并更新模式以收集遗漏的部分。你可能还需要清理一些错误的结果,特别是那些末尾的垃圾内容。(这个模式需要找到包含空格的链接,所以它特别容易出现过度匹配的问题。)

警告:不要依赖这个方法来处理未来的用户输入,尤其是在安全性方面。最好只用它来手动收集已有数据中的链接。

撰写回答