正则表达式提取域名.tld

4
我需要一个正则表达式,在Java中使用,可以用来从任何URL中检索出域名部分。因此,https://foo.com/barhttp://www.foo.com#barhttp://bar.foo.com都将返回foo.com。
我编写了这个正则表达式,但它匹配整个URL。
Pattern.compile("[.]?.*[.x][a-z]{2,3}");

我不确定我是否正确匹配了“.”字符。我尝试使用“.”,但是Netbeans会报错。
更新:
tld不仅限于2或3个字符,并且http://www.foo.co.uk/bar应返回foo.co.uk。

请返回已翻译的文本: 如何从URL中获取域名? - Gumbo
1
其实这并不是一个完全相同的问题,因为另一个问题试图删除顶级域名部分以及像".co.uk"这样的某些二级部分。但唯一的区别在于您是否捕获了该部分。我猜他希望http://www.foo.co.uk/会返回foo.co.uk。 - MSalters
你知道有四个字母的顶级域名,比如"info"和"name"吗?我觉得你可能忽略了这一点,因为在你的正则表达式中有"{2,3}"。另外,如果你想匹配句点,你需要像这样转义它 "\."。 - Tim Büthe
刚刚看到甚至有“.museum”和“.travel”顶级域名。 - Tim Büthe
很好的发现。我希望foo.co.uk/bar返回foo.co.uk。 - sjobe
1
我发现这个答案非常有用:https://dev59.com/eFPTa4cB1Zd3GeqPmclA#4820675。 - Philipp
8个回答

10

这比你想象的要难。你的例子 https://foo.com/bar 中有一个逗号,而逗号是一个有效的URL字符。这是一篇关于一些麻烦的重要帖子:

https://blog.codinghorror.com/the-problem-with-urls/

https?://([-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|])

这是一个很好的起点。

"Mastering Regular Expressions" 中关于此主题的一些列表:

http://regex.info/listing.cgi?ed=3&p=207

@sjobe

>>> import re
>>> pattern = r'https?://([-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|])'
>>> url = re.compile(pattern)
>>> url.match('http://news.google.com/').groups()
('news.google.com/',)
>>> url.match('not a url').groups()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'groups'
>>> url.match('http://google.com/').groups()
('google.com/',)
>>> url.match('http://google.com').groups()
('google.com',)

很抱歉,例子是用Python而不是Java编写的,它更加简洁。在Java中,需要对正则表达式进行一些额外的转义。


我认为他并不是想让逗号成为URL的一部分,他只是在分隔一个列表。 - RC1140
2
这就是我的观点,它是有歧义的。正则表达式应该如何确定逗号是 URL 的一部分还是不是? - jsamsa
1
无论如何,他对http URL中的“domain.tld”部分感兴趣。该部分中没有逗号。 - MSalters
我尝试了这个正则表达式[在末尾添加了一个')']https?://([-A-Za-z0-9+&@#/%?=_()|!:,.;]*[-A-Za-z0-9+&@#/%=_()|])但它没有匹配到任何URL。我正在尝试"http://news.google.com"和"http://www.google.com"。 - sjobe
你的codinghorror链接已经失效了。我猜这个是新的链接:http://blog.codinghorror.com/the-problem-with-urls/ - John

8
我会使用java.net.URI类来提取主机名,并使用正则表达式提取主机URI的最后两部分。
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RunIt {

    public static void main(String[] args) throws URISyntaxException {
        Pattern p = Pattern.compile(".*?([^.]+\\.[^.]+)");

        String[] urls = new String[] {
                "https://foo.com/bar",
                "http://www.foo.com#bar",
                "http://bar.foo.com"
        };

        for (String url:urls) {
            URI uri = new URI(url);
            //eg: uri.getHost() will return "www.foo.com"
            Matcher m = p.matcher(uri.getHost());
            if (m.matches()) {
                System.out.println(m.group(1));
            }
        }
    }
}

输出:

foo.com
foo.com
foo.com

那实际上就是我最终所做的。 - sjobe
3
那么像foobar.co.uk这样的域名呢? - Gumbo

6
如果字符串包含有效的URL,则可以使用类似于以下正则表达式(Perl引用)的表达式:
/^
(?:\w+:\/\/)?
[^:?#\/\s]*?

(
[^.\s]+
\.(?:[a-z]{2,}|co\.uk|org\.uk|ac\.uk|org\.au|com\.au|___etc___)
)

(?:[:?#\/]|$)
/xi;

结果:

url: https://foo.com/bar
matched: foo.com
url: http://www.foo.com#bar
matched: foo.com
url: http://bar.foo.com
matched: foo.com
url: ftp://foo.com
matched: foo.com
url: ftp://www.foo.co.uk?bar
matched: foo.co.uk
url: ftp://www.foo.co.uk:8080/bar
matched: foo.co.uk

对于Java,引用可能类似于:

"^(?:\\w+://)?[^:?#/\\s]*?([^.\\s]+\\.(?:[a-z]{2,}|co\\.uk|org\\.uk|ac\\.uk|org\\.au|com\\.au|___etc___))(?:[:?#/]|$)"

当然,您需要替换etc部分。
示例Perl脚本:
use strict;

my @test = qw(
    https://foo.com/bar
    http://www.foo.com#bar
    http://bar.foo.com
    ftp://foo.com
    ftp://www.foo.co.uk?bar
    ftp://www.foo.co.uk:8080/bar
);

for(@test){
    print "url: $_\n";

    /^
    (?:\w+:\/\/)?
    [^:?#\/\s]*?

    (
    [^.\s]+
    \.(?:[a-z]{2,}|co\.uk|org\.uk|ac\.uk|org\.au|com\.au|___etc___)
    )

    (?:[:?#\/]|$)
    /xi;

    print "matched: $1\n";
}

我忘记在字符串开头双重转义第一个\w,应该是"\w"。如果你看到任何其他单个反斜杠,请进行转义。 - Qtax
我在谷歌上搜索了大约一个小时,发现您的答案最适合我的情况。谢谢。但是在Java正则表达式字符串中似乎有一个小问题,它应该像这样 "^(?:\w+://)?[^:?#/\s]?([^.\s]+\.(?:[a-z]{2,}|co\.uk|org\.uk|ac\.uk|org\.au|com\.au|com.cn|etc))(?:[:?#/].|$)" - SalutonMondo

4

new URL(url).getHost()

不需要正则表达式。


不错,但在高吞吐量循环内无法工作 :) - Ravindranath Akila

3
你需要获取所有可能的顶级域名和国家顶级域名列表,并进行匹配。如果不这样做,你将无法区分子域名.dom.com和hello.co.uk。
因此,先获取这样的列表。我建议你将其反转,以便存储例如uk.co。
然后,你可以通过获取//和/或行末之间的所有内容来从URL中提取域名。向后拆分.,匹配TLD,然后再匹配1个附加级别以获取域名。

0
这对我有效:
public static String getDomain(String url){
    if(TextUtils.isEmpty(url)) return null;
    String domain = null;
    if(url.startsWith("http://")) {
        url = url.replace("http://", "").trim();
    } else if(url.startsWith("https://")) {
        url = url.replace("https://", "").trim();
    }
    String[] temp = url.split("/");
    if(temp != null && temp.length > 0) {
        domain = temp[0];
    }  
    return domain;
}

0
    /[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$/

就快完成了,但是当二级域名有3个字符时不匹配,例如:www.foo.com这里测试一下。


0

代码:

public class DomainUrlUtils {
    private static String[] TLD = {"com", "net"}; // top-level domain
    private static String[] SLD = {"co\\.kr"}; // second-level domain

    public static String getDomainName(String url) {
        Pattern pattern = Pattern.compile("(?<=)[^(\\.|\\/)]\\w+\\.(" + joinTldAndSld("|") + ")$");
        Matcher match = pattern.matcher(url);
        String domain = null;

        if (match.find()) {
            domain = match.group();
        }

        return domain;
    }

    private static String joinTldAndSld(String delimiter) {
        String t = String.join(delimiter, TLD);
        String s = String.join(delimiter, SLD);

        return new StringBuilder(t).append(s.isEmpty() ? "" : "|" + s).toString();
    }
}

测试:

public class DomainUrlUtilsTest {

    @Test
    public void getDomainName() throws Exception {
        // given
        String[][] domainUrls = {
            {
                "test.com",
                "sub1.test.com",
                "sub1.sub2.test.com",
                "https://sub1.test.com",
                "http://sub1.sub2.test.com"
            },
            {
                "https://domain.com",
                "https://sub.domain.com"
            },
            {
                "http://domain.co.kr",
                "http://sub.domain.co.kr",
                "http://local.sub.domain.co.kr",
                "http://local-test.sub.domain.co.kr",
                "sub.domain.co.kr",
                "domain.co.kr",
                "test.sub.domain.co.kr"
            }
        };

        String[] expectedUrls = {
            "test.com",
            "domain.com",
            "domain.co.kr"
        };

        // when
        // then
        for (int domainIndex = 0; domainIndex < domainUrls.length; domainIndex++) {
            for (String url : domainUrls[domainIndex]) {
                String convertedUrl = DomainUrlUtils.getDomainName(url);

                if (expectedUrls[domainIndex].equals(convertedUrl)) {
                    System.out.println(url + " -> " + convertedUrl);
                } else {
                    Assert.fail("origin Url: " + url + " / converted Url: " + convertedUrl);
                }
            }
        }
    }
}

结果:

test.com -> test.com
sub1.test.com -> test.com
sub1.sub2.test.com -> test.com
https://sub1.test.com -> test.com
http://sub1.sub2.test.com -> test.com
https://domain.com -> domain.com
https://sub.domain.com -> domain.com
http://domain.co.kr -> domain.co.kr
http://sub.domain.co.kr -> domain.co.kr
http://local.sub.domain.co.kr -> domain.co.kr
http://local-test.sub.domain.co.kr -> domain.co.kr
sub.domain.co.kr -> domain.co.kr

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