JavaScript/正则表达式用于查找不带子域名的根域名

14

我查找了很多类似的正则表达式示例,但都不完全符合我的需求。

我想要能够传入以下网址并返回结果:

  • www.google.com 返回 google.com

  • sub.domains.are.cool.google.com 返回 google.com

  • doesntmatterhowlongasubdomainis.idont.wantit.google.com 返回 google.com

  • sub.domain.google.com/no/thanks 返回 google.com

希望这样说有意义:) 提前感谢!-詹姆斯


13
sub.domain.google.co.uk 的结果将是什么? - Pekka
3
这些不是URL地址,只是域名(除了最后一个是字符串,可解释为域名加上URL路径)。 - Gumbo
@pekka 将"google.co.uk"转换为google.com,就像apps.facebook.com将返回facebook.com一样。 @gumbo 正确。这些只是可能传递的示例。大多数情况下会是完整的URL(子域,域,文件夹,文件)。 - jamesmhaley
8
请务必访问http://publicsuffix.org/查看公共后缀列表。 - janmoesen
@pekka 是的,这是困难的地方,如果可能的话,我想要包括那些域。 - jamesmhaley
如果您要包含公共后缀列表中的所有规则,您确定正则表达式是完成此任务的正确工具吗? - Bennor McCarthy
7个回答

12

使用正则表达式无法解决此问题,因为您不知道后缀中有多少块。

例如,google.com的后缀是com。要从subdomain.google.com获取google.com,您需要取最后两个块 - 一个用于后缀,一个用于google

但是如果应用这种逻辑到subdomain.google.co.uk上,您将得到co.uk

您实际上需要从类似于http://publicsuffix.org/的列表中查找后缀。


10
不要使用正则表达式,使用.split()方法并从那里开始。
var s = domain.split('.');

如果你的使用情况比较狭窄,你可以根据需要检查顶级域名(TLDs),然后适当返回最后的2或3段内容。
return s.slice(-2).join('.');

它会比任何正则表达式解决方案更少地让你的眼睛流血。

1
如果你只有有限的数据子集,我建议保持正则表达式简单,例如:
(([a-z\-]+)(?:\.com|\.fr|\.co.uk))

这将匹配:
www.google.com --> google.com
www.google.co.uk --> google.co.uk
www.foo-bar.com --> foo-bar.com

在我的情况下,我知道所有相关的URL将使用这个正则表达式进行匹配。
收集一个样本数据集并针对您的正则表达式进行测试。在原型设计阶段,您可以使用 https://regex101.com/r/aG9uT0/1 这样的工具进行测试。在开发过程中,可以使用测试脚本自动化。

1

我对此进行的测试不多,但如果我理解您所要求的内容,这应该是一个不错的起点...

([A-Za-z0-9-]+\.([A-Za-z]{3,}|[A-Za-z]{2}\.[A-Za-z]{2}|[A-za-z]{2}))\b

编辑:

澄清一下,它正在寻找:

一个或多个字母数字字符或破折号,后跟一个文字点

然后是以下三种情况之一...

  1. 三个或更多字母字符(即com/net/mil/coop等)
  2. 两个字母字符,后跟一个文字点,后跟两个以上的字母(即co.uk)
  3. 两个字母字符(即us/uk/to等)

在那之后,是单词边界(\b),意味着字符串的结尾、空格或非单词字符(在正则表达式中,单词字符通常是字母数字和下划线)。

就像我说的,我没有做太多的测试,但它似乎是一个合理的起点。你可能需要尝试并调整它,即使这样,你也不太可能得到所有测试用例的100%。有一些考虑因素,比如Unicode域名和各种技术上有效但你很可能不会遇到的东西,这些都会使一个简单的正则表达式出现问题,但这可能会让你完成90%以上的工作。


请问您能否解释一下它的作用,因为我对正则表达式的理解很少。以及它如何实现。 - jamesmhaley
1
90%的慷慨已经足够了。基本上,没有简单的方法来做到这一点。域名系统过于复杂,允许很多变化。 - hallvors
1
考虑到提供的示例是“类似正常”的域名,我认为你可能可以成功匹配大部分,但肯定不会达到90%。尽管如此,像我之前所说的那样,你不太可能对于所有测试用例都获得100%的匹配率。 - theraccoonbear

0
([A-Za-z0-9-]+\.([A-Za-z]{3,}|[A-Za-z]{2}\.[A-Za-z]{2}|[A-za-z]{2}))(?!\.([A-Za-z]{3,}|[A-Za-z]{2}\.[A-Za-z]{2}|[A-za-z]{2}))\b

这是对theracoonbear答案的改进。 我进行了一些快速测试,发现如果您提供一个子域具有子域的域,它将失败。我还想指出,“90%”绝对不慷慨。它将比您想象的更接近100%。它适用于最受欢迎的50个网站的所有子域,这占全球互联网活动的很大一部分。唯一可能失败的时候是在unicode域名等情况下。

我的解决方案开始的方式与theracoonbear的相同。它不是检查单词边界,而是使用负向先行断言来检查末尾是否没有可能是TLD的内容(只是将TLD检查部分复制到负向先行断言中)。


0
我知道这是一个较旧的帖子,但这个正则表达式很好地匹配:
([^.]+(?:(?:\.[^.]{2,3}){1,2}|\.[^.]+))$

这是它工作的一个例子: https://regex101.com/r/2F9pEt/1

-1

在不测试顶级域的有效性的情况下,我正在使用stormsweeper解决方案的改编版本:

domain = 'sub.domains.are.cool.google.com'

s = domain.split('.')

tld = s.slice(-2..-1).join('.')

编辑:注意三段式顶级域名(TLD)如domain.co.uk可能会出现的问题。


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