【发布时间】:2014-02-06 01:34:11
【问题描述】:
如何使用正则表达式从 URL 中仅提取顶级域和二级域?我想跳过所有较低级别的域。有什么想法吗?
【问题讨论】:
如何使用正则表达式从 URL 中仅提取顶级域和二级域?我想跳过所有较低级别的域。有什么想法吗?
【问题讨论】:
以下正则表达式将域与来自 url 或域字符串的 root 和 tld 提取(命名的捕获组)匹配:
(?:\w+:\/{2})?(?<cs_domain>(?<cs_domain_sub>(?:[\w\-]+\.)*?)(?<cs_domain_root>[\w\-]+(?<cs_domain_tld>(?:\.\w{2})?(?:\.\w{2,3}|\.xn-+\w+|\.site|\.club))))\|
很难说它是否完美,但它适用于我反对的所有测试数据集,包括 .club、.xn-1234、.co.uk 和其他奇怪的结尾。它针对 40k 字符的日志以 5556 步完成,因此效率似乎也很合理。
【讨论】:
对于任何使用 JavaScript 并想要一种简单的方法来提取顶级域和二级域的人,我最终这样做了:
'example.aus.com'.match(/\.\w{2,3}\b/g).join('')
这匹配任何带有句点后跟两个或三个字符,然后是word boundary的内容。
以下是一些示例输出:
'example.aus.com' // .aus.com
'example.austin.com' // .austin.com
'example.aus.com/howdy' // .aus.com
'example.co.uk/howdy' // .co.uk
有些人可能需要一些更聪明的东西,但这对我的特定数据集来说已经足够了。
编辑
我意识到实际上有不少二级域的长度超过 3 个字符(并且允许)。因此,再次为简单起见,我只是删除了正则表达式的字符计数元素:
'example.aus.com'.match(/\.\w*\b/g).join('')
【讨论】:
此外,您也可以使用类似于,
^(?:https?:\/\/)(?:w{3}\.)?.*?([^.\r\n\/]+\.)([^.\r\n\/]+\.[^.\r\n\/]{2,6}(?:\.[^.\r\n\/]{2,6})?).*$
并添加尽可能多的要捕获 URL 组件的捕获组。
如果您希望简化/修改/探索表达式,在regex101.com 的右上角面板中已对此进行了说明。如果您愿意,您还可以在this link 中观看它如何与一些示例输入匹配。
jex.im 可视化正则表达式:
【讨论】:
这是一个老问题,随着我们开始添加新的虚名 TLD 和更多 ccTLD 二级域(例如.co.uk、.org.uk),这里的挑战变得更加复杂。如此之多,以至于几乎可以保证正则表达式返回误报或误报。
可靠地获取主要主机的唯一方法是调用了解它们的服务,例如Public Suffix List。
您可以使用几个开源库,例如 psl,或者您可以编写自己的。
psl 的用法非常直观。从他们的文档中:
var psl = require('psl');
// Parse domain without subdomain
var parsed = psl.parse('google.com');
console.log(parsed.tld); // 'com'
console.log(parsed.sld); // 'google'
console.log(parsed.domain); // 'google.com'
console.log(parsed.subdomain); // null
// Parse domain with subdomain
var parsed = psl.parse('www.google.com');
console.log(parsed.tld); // 'com'
console.log(parsed.sld); // 'google'
console.log(parsed.domain); // 'google.com'
console.log(parsed.subdomain); // 'www'
// Parse domain with nested subdomains
var parsed = psl.parse('a.b.c.d.foo.com');
console.log(parsed.tld); // 'com'
console.log(parsed.sld); // 'foo'
console.log(parsed.domain); // 'foo.com'
console.log(parsed.subdomain); // 'a.b.c.d'
旧答案
你可以用这个:
(\w+\.\w+)$
如果没有更多细节(示例文件、您使用的语言),很难准确判断这是否可行。
【讨论】:
如果你想要所有特定的顶级域名,那么你可以这样写正则表达式:
[RegularExpression("^(https?:\\/\\/)?(([\\w]+)?\\.?(\\w+\\.((za|zappos|zara|zero|zip|zippo|zm|zone|zuerich|zw))))\\/?$", ErrorMessage = "Is not a valid fully-qualified URL.")]
您也可以从此链接添加更多域名:
【讨论】:
通过捕获组,您可以实现一些魔法。
例如,考虑以下 javascript:
let hostname = 'test.something.else.be';
let domain = hostname.replace(/^.+\.([^\.]+\.[^\.]+)$/, '$1');
document.write(domain);
这将产生一个包含'else.com' 的字符串。这是因为正则表达式本身将匹配完整的字符串,并且捕获组将映射到$1。所以它将完整的字符串'test.something.else.com' 替换为'$1',实际上是'else.com'。
正则表达式并不漂亮,可能可以通过 {3} 之类的东西变得更加动态,用于定义要查找子域的深度,但这只是一个示例。
【讨论】:
如果您需要更具体:
/\.(?:nl|se|no|es|milru|fr|es|uk|ca|de|jp|au|us|ch|it|io|org|com|net|int|edu|mil|arpa)/
基于http://www.seobythesea.com/2006/01/googles-most-popular-and-least-popular-top-level-domains/
【讨论】:
\.[a-z]{2,3}(\.[a-z]{2,3})?)
由于 TLD 现在包含超过三个字符的内容,例如 .wang 和 .travel,因此这里有一个满足这些新 TLD 的正则表达式:
([^.\s]+\.[^.\s]+)$
策略:从字符串的末尾开始,查找一个或多个不是句点或空格的字符,然后是一个句点,然后是一个或多个不是句点或空格的字符。
【讨论】:
这是我的想法,
使用$ 锚点从行尾开始匹配任何不是点的内容,重复三次。
字符串末尾的最后一个匹配项应该是可选的,以允许.com.au 或.co.nz 类型的域。
最后一个和倒数第二个匹配都只会匹配2-3个字符,这样就不会和二级域名混淆了。
正则表达式:
[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$
演示:
【讨论】: