首页 > 解决方案 > 正则表达式匹配顶级域不是 .com、.net、.org、.info、.edu、.gov 或 .ca 的 url

问题描述

我正在寻找正则表达式来匹配不是来自 .com、.net、.org、.info、.edu、.gov 或 .ca 域的整个 url。TLD 列表可能会随着时间的推移而增长,但这是一个好的开始。

这些将匹配:

这些不匹配:

对于一些背景知识,我希望使用 Exchange Online 的表达式来过滤包含异常/国际链接的入站电子邮件,在我们的例子中,这些链接几乎 100% 是网络钓鱼或垃圾邮件。我们是一家小型企业,只为本地客户提供服务,而且通常所有供应商都是北美人。

标签: regex

解决方案


要匹配整个 URL...

请注意,此实现尝试根据匹配异常 URL 的用法覆盖其他元素:

  • 可能的未知安全向量的任何模式(例如ftp, ldap
  • 包含基本身份验证用户名和密码
  • IPv6 IP 地址
  • 指定的端口号(例如https://www.example.com:8080/
  • 没有路径,即只有主机名/IP 地址
  • 请求参数
  • 分段

我不知道“Exchange Online”使用的确切正则表达式引擎,所以在这里我使用 C# 和 PowerShell 的 RegEx 功能,假设这些功能可用。

正则表达式

[a-z][a-z0-9+.-]*://(?>(?:[a-z0-9!$%&'()*+,.:;=_~-]+@)?(?:[a-z0-9%._~-]+|\[[a-z0-9!$%&'()*+,.:;=_~-]+\]))(?<!\.(?:com|net|org|info|edu|gov|ca)(?::\d+)?)[a-z0-9!#$%&'()*+,./:;=?@_~-]*

分解

  • 架构(//http等):httpsftp[a-z][a-z0-9+\-.]*
  • 原子团开始:(?>
  • 用户名密码:(?:[a-z0-9!$%&'()*+,.:;=_~-]+@)?
  • 主机名:(?:[a-z0-9%._~-]+|\[[a-z0-9!$%&'()*+,.:;=_~-]+\]))
    • IPv4 或普通域:[a-z0-9%._~-]+
    • 或 IPv6:\[[a-z0-9!$%&'()*+,.:;=_~-]+\]
  • 主机名(负面回顾):(?<!\.(?:com|net|org|info|edu|gov|ca)(?::\d+)?)
    • 可选地允许端口号:(?::\d+)?
  • 原子团末端:)
  • 查询字符串和片段:[a-z0-9!#$%&'()*+,./:;=?@_~-]*

原子组是为了防止表达式的“用户名/密码”和“查询字符串和片段”部分在未经我们验证的情况下匹配为字符串的“主机名”部分。

使用正则表达式匹配文本中的 URL

如果您使用此正则表达式来匹配文本文档中的 URL,您可能会发现“引用”的 URL 或降价链接存在一些问题。

例如

[an example](http://example.cox/)
'http://www.example.cox/'
http://www.example.cox/index.html, something interesting in a sentence
You can get it here http://www.example.cox/download.html.

此 RegEx 原样将匹配末尾的其他字符,因为它们是有效的 URL 字符,即:

http://example.cox/)
http://www.example.cox/'
http://www.example.cox/index.html,
http://www.example.cox/download.html.

为避免这种情况,您可以以这样的模式重复上面的 RegEx(显然您将删除空格/新行):

(?:
(?<=['])
# RegEx here
(?=['])
|
(?<=["])
# RegEx here
(?=["])
|
(?<=\()
# RegEx here
(?=\))
|
# RegEx here
(?<![.,])
)

所以在这里我们说它在 URL 之前有一个引号'/"或括号(,假设 URL 的结尾可以被忽略等。

如果匹配在开头没有括号(、引号'等,最后一部分(?<![.,])基本上是说不匹配 URL 末尾的最后一个句号.或逗号,字符,即使它们是完全有效的字符。在完全了解的情况下执行此操作可能会破坏返回的 URL。


推荐阅读