从给定的URL中获取域名

162

给定一个URL,我想提取域名(不应包含“www”部分)。 URL 可能包含 http/https。这是我编写的 Java 代码。虽然它似乎运行正常,但是否有更好的方法或是否存在一些可能失败的边缘情况。

public static String getDomainName(String url) throws MalformedURLException{
    if(!url.startsWith("http") && !url.startsWith("https")){
         url = "http://" + url;
    }        
    URL netUrl = new URL(url);
    String host = netUrl.getHost();
    if(host.startsWith("www")){
        host = host.substring("www".length()+1);
    }
    return host;
}

输入:http://google.com/blah

输出:google.com


6
请尝试访问 http://74.125.226.70 并告诉我结果如何 :) - Marvin Pinto
1
它只返回IP地址。74.125.226.70 - RandomQuestion
3
你希望从中获得域名,对吗?假设那是你想要的。 - Marvin Pinto
6
例如,http://www.de/http://www.com/将无法获得期望的结果。 - Michael Konietzka
17个回答

348
如果您想解析URL,请使用java.net.URIjava.net.URL存在许多问题 - 其equals方法会执行DNS查找,因此在与不受信任的输入一起使用时,可能会使代码容易受到拒绝服务攻击的影响。 “Mr. Gosling - 为什么要让url equals变得糟糕?”解释了其中一个问题。请养成使用java.net.URI的习惯。
public static String getDomainName(String url) throws URISyntaxException {
    URI uri = new URI(url);
    String domain = uri.getHost();
    return domain.startsWith("www.") ? domain.substring(4) : domain;
}

你想要的功能应该可以实现。


虽然它似乎工作良好,但是否有更好的方法或可能失败的一些边缘情况?

按照您编写的代码,以下有效的URL将失败:

  • httpfoo/bar -- 带有以http开头的路径组件的相对URL。
  • HTTP://example.com/ -- 协议是不区分大小写的。
  • //example.com/ -- 主机为协议相对URL
  • www/foo -- 带有以www开头的路径组件的相对URL
  • wwwexample.com -- 不以www.开头而以www开头的域名。

分层URL具有复杂的语法。 如果您在仔细阅读RFC 3986之前尝试自行编写解析器,则可能会出错。只需使用内置在核心库中的解析器。

如果您确实需要处理java.net.URI拒绝的混乱输入,请参见RFC 3986附录B:

附录B.使用正则表达式解析URI引用

由于“第一次匹配获胜”的算法与POSIX正则表达式使用的“贪婪”消除歧义方法相同,因此使用正则表达式将URI引用的潜在五个组件分解为其组件是自然而常见的。

以下行是分解格式良好的URI引用成其组件的正则表达式。

  ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
   12            3  4          5       6  7        8 9

上面第二行中的数字仅用于辅助阅读,它们表示每个子表达式(即每个成对括号)的参考点。


2
@Jitendra,我建议你不要费力去修复它们。Java库已经为你完成了这项工作。 - Mike Samuel
3
@Jitendra,www.google.com是一个带有路径组件的相对URL,路径组件为www.google.com。举个例子,如果相对于http://example.com/进行解析,你将得到http://example.com/www.google.com - Mike Samuel
3
如果URI主机包含特殊字符,例如:"öob.se",则主机将为null。 - inc
1
如果域名包含下划线(_),则uri.getHost();将返回null。 - user2128672
显示剩余2条评论

115
import java.net.*;
import java.io.*;

public class ParseURL {
  public static void main(String[] args) throws Exception {

    URL aURL = new URL("http://example.com:80/docs/books/tutorial"
                       + "/index.html?name=networking#DOWNLOADING");

    System.out.println("protocol = " + aURL.getProtocol()); //http
    System.out.println("authority = " + aURL.getAuthority()); //example.com:80
    System.out.println("host = " + aURL.getHost()); //example.com
    System.out.println("port = " + aURL.getPort()); //80
    System.out.println("path = " + aURL.getPath()); //  /docs/books/tutorial/index.html
    System.out.println("query = " + aURL.getQuery()); //name=networking
    System.out.println("filename = " + aURL.getFile()); ///docs/books/tutorial/index.html?name=networking
    System.out.println("ref = " + aURL.getRef()); //DOWNLOADING
  }
}

阅读更多


谢谢!我们可以在单元测试中使用Java的URL类,而不是Android的Uri。现在我看到了authorityhost之间的区别。 - CoolMind

24

以下是使用Guava的InternetDomainName.topPrivateDomain()的简短且简单的代码:InternetDomainName.from(new URL(url).getHost()).topPrivateDomain().toString()

如果给定 http://www.google.com/blah,它将返回 google.com。或者,如果给定 http://www.google.co.mx,它将返回 google.co.mx

正如Sa Qada此问题的另一个答案中所评论的那样,此问题已经被问过了:从给定的url提取主域名。该问题的最佳答案来自Satya,他建议使用Guava的InternetDomainName.topPrivateDomain()

public boolean isTopPrivateDomain()

指示此域名是否由正好一个子域组件和一个公共后缀组成。例如,对于google.com和foo.co.uk返回true,但对于www.google.com或co.uk则不返回。

警告:此方法的真实结果并不意味着该域是可寻址主机的最高级别,因为许多公共后缀也是可寻址主机。例如,域bar.uk.com具有公共后缀uk.com,因此它将从该方法返回true。但uk.com本身是一个可寻址的主机。

此方法可用于确定域是否可能是可以设置cookie的最高级别,尽管这仍取决于各个浏览器对cookie控件的实现。详见RFC 2109。

将此与原始帖子已经包含的URL.getHost()结合使用,即可得到:

import com.google.common.net.InternetDomainName;

import java.net.URL;

public class DomainNameMain {

  public static void main(final String... args) throws Exception {
    final String urlString = "http://www.google.com/blah";
    final URL url = new URL(urlString);
    final String host = url.getHost();
    final InternetDomainName name = InternetDomainName.from(host).topPrivateDomain();
    System.out.println(urlString);
    System.out.println(host);
    System.out.println(name);
  }
}

这个没预览吗? - gumuruh
@gumuruh,你是指JavaScript代码片段吗?这是Java。 - Kirby

8
我写了一个方法(见下文),用简单的字符串匹配提取URL的域名。它实际上提取的是第一个 "://"(如果没有包含则为索引0)和第一个后续 "/" (如果没有后续,则为索引String.length())之间的位。剩余的前缀 "www(_)*." 会被切除。我确定有一些情况这个方法不够好,但在大多数情况下应该足够使用!
Mike Samuel 在上面的帖子中说 java.net.URI 类可以做到这一点(并且优于 java.net.URL 类),但我遇到了 URI 类的问题。特别是,如果URL不包括方案(即 "http(s)" 部分),URI.getHost() 将返回空值。
/**
 * Extracts the domain name from {@code url}
 * by means of String manipulation
 * rather than using the {@link URI} or {@link URL} class.
 *
 * @param url is non-null.
 * @return the domain name within {@code url}.
 */
public String getUrlDomainName(String url) {
  String domainName = new String(url);

  int index = domainName.indexOf("://");

  if (index != -1) {
    // keep everything after the "://"
    domainName = domainName.substring(index + 3);
  }

  index = domainName.indexOf('/');

  if (index != -1) {
    // keep everything before the '/'
    domainName = domainName.substring(0, index);
  }

  // check for and remove a preceding 'www'
  // followed by any sequence of characters (non-greedy)
  // followed by a '.'
  // from the beginning of the string
  domainName = domainName.replaceFirst("^www.*?\\.", "");

  return domainName;
}

我认为这个可能不正确,适用于 http://bob.com:8080/service/read?name=robert - Lee Meador
感谢指出,Lee。请注意,我在回答中已经加上了“我相信会有一些情况下这样做可能不够好...”的限定语。我的回答需要根据您的具体情况进行一些微调。 - Adil Hussain

4

以上都很好。这个对我来说似乎非常简单易懂。请原谅引号。我是为Groovy编写的,类名为DataCenter。

最初的回答:

以上方法都不错。以下方法在我看来非常简单易懂。请忽略引号,该段代码是为Groovy中的DataCenter类编写的。

static String extractDomainName(String url) {
    int start = url.indexOf('://')
    if (start < 0) {
        start = 0
    } else {
        start += 3
    }
    int end = url.indexOf('/', start)
    if (end < 0) {
        end = url.length()
    }
    String domainName = url.substring(start, end)

    int port = domainName.indexOf(':')
    if (port >= 0) {
        domainName = domainName.substring(0, port)
    }
    domainName
}

以下是一些junit4测试:

最初的回答:

@Test
void shouldFindDomainName() {
    assert DataCenter.extractDomainName('http://example.com/path/') == 'example.com'
    assert DataCenter.extractDomainName('http://subpart.example.com/path/') == 'subpart.example.com'
    assert DataCenter.extractDomainName('http://example.com') == 'example.com'
    assert DataCenter.extractDomainName('http://example.com:18445/path/') == 'example.com'
    assert DataCenter.extractDomainName('example.com/path/') == 'example.com'
    assert DataCenter.extractDomainName('example.com') == 'example.com'
}

非常感谢简洁明了!:D - gumuruh

4
在我的情况下,我只需要主域名而不是子域名(没有“www”或其他子域名):
public static String getUrlDomain(String url) throws URISyntaxException {
    URI uri = new URI(url);
    String domain = uri.getHost();
    String[] domainArray = domain.split("\\.");
    if (domainArray.length == 1) {
        return domainArray[0];
    }
    return domainArray[domainArray.length - 2] + "." + domainArray[domainArray.length - 1];
}

使用这种方法,URL "https://rest.webtoapp.io/llSlider?lg=en&t=8" 的域名将变为 "webtoapp.io"。请注意保留HTML标记。

3

val host = url.split("/")[2]


3

在URI对象创建后,我进行了一些小的处理。

 if (url.startsWith("http:/")) {
        if (!url.contains("http://")) {
            url = url.replaceAll("http:/", "http://");
        }
    } else {
        url = "http://" + url;
    }
    URI uri = new URI(url);
    String domain = uri.getHost();
    return domain.startsWith("www.") ? domain.substring(4) : domain;

1

1
尝试这个:java.net.URL; JOptionPane.showMessageDialog(null,getDomainName(new URL("https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains")));
public String getDomainName(URL url){
String strDomain;
String[] strhost = url.getHost().split(Pattern.quote("."));
String[] strTLD = {"com","org","net","int","edu","gov","mil","arpa"};

if(Arrays.asList(strTLD).indexOf(strhost[strhost.length-1])>=0)
    strDomain = strhost[strhost.length-2]+"."+strhost[strhost.length-1];
else if(strhost.length>2)
    strDomain = strhost[strhost.length-3]+"."+strhost[strhost.length-2]+"."+strhost[strhost.length-1];
else
    strDomain = strhost[strhost.length-2]+"."+strhost[strhost.length-1];
return strDomain;}

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