รับชื่อโดเมนจาก URL ที่กำหนด


130

เมื่อระบุ 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


3
ลองhttp://74.125.226.70แจ้งให้เราทราบวิธีการทำงาน :)
Marvin Pinto

1
เพียงส่งคืนที่อยู่ IP 74.125.226.70
RandomQuestion

2
และคุณจะได้ชื่อโดเมนจากสิ่งนั้นได้อย่างไร? สมมติว่าเป็นสิ่งที่คุณต้องการ ..
มาวินปิ่นโต

5
ตัวอย่างเช่นhttp://www.de/หรือhttp://www.com/จะไม่ให้ผลลัพธ์ที่ต้องการ
Michael Konietzka

คำตอบ:


287

หากคุณต้องการที่จะแยก URL java.net.URIที่ใช้ java.net.URLมีปัญหามากมาย - equalsวิธีการของมันทำการค้นหา DNS ซึ่งหมายความว่าโค้ดที่ใช้อาจเสี่ยงต่อการถูกปฏิเสธการโจมตีบริการเมื่อใช้กับอินพุตที่ไม่น่าเชื่อถือ

"มิสเตอร์กอสลิง - ทำไมคุณถึงทำให้ url เท่ากับห่วย" อธิบายปัญหาดังกล่าวอย่างหนึ่ง แค่ติดนิสัยใช้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- URL httpที่เกี่ยวข้องกับองค์ประกอบเส้นทางที่เริ่มต้นด้วย
  • HTTP://example.com/ - โปรโตคอลไม่คำนึงถึงขนาดตัวพิมพ์
  • //example.com/ - URL สัมพัทธ์ของโปรโตคอลกับโฮสต์
  • www/foo - URL สัมพัทธ์ที่มีองค์ประกอบเส้นทางที่ขึ้นต้นด้วย www
  • 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

9
นอกจากนี้สำหรับ URI netUrl = URI ใหม่ ("www.google.com"); netUrl.getHost () ส่งคืนค่า NULL ฉันคิดว่าฉันยังต้องตรวจสอบ http: // หรือ https: //
RandomQuestion

2
@Jitendra, www.google.comเป็น URL www.google.comที่เกี่ยวข้องกับองค์ประกอบเส้นทางที่เป็น ตัวอย่างเช่นถ้าได้รับการแก้ไขกับคุณจะได้รับhttp://example.com/ http://example.com/www.google.com
Mike Samuel

ขอบคุณไมค์. ถ้าฉันเข้าใจถูกต้องกับไลบรารีคุณหมายถึงใช้ URI หรือ regex ด้านบน?
RandomQuestion

2
โฮสต์ URI จะเป็นโมฆะหากมีอักขระพิเศษตัวอย่างเช่น "öob.se"
inc

80
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
  }
}

อ่านเพิ่มเติม


15

นี่คือบรรทัดสั้น ๆ และเรียบง่ายที่ใช้InternetDomainName.topPrivateDomain()ใน Guava:InternetDomainName.from(new URL(url).getHost()).topPrivateDomain().toString()

ได้รับที่จะทำให้คุณhttp://www.google.com/blah google.comหรือได้รับก็จะให้คุณhttp://www.google.co.mxgoogle.co.mx

ดังที่Sa Qadaแสดงความคิดเห็นในคำตอบอื่นในโพสต์นี้คำถามนี้ถูกถามก่อนหน้านี้: แยกชื่อโดเมนหลักออกจาก URL ที่ระบุ ตอบที่ดีที่สุดกับคำถามที่มาจากสัตยาที่แสดงให้เห็นฝรั่งของInternetDomainName.topPrivateDomain ()

บูลีนสาธารณะ isTopPrivateDomain ()

ระบุว่าชื่อโดเมนนี้ประกอบด้วยส่วนประกอบโดเมนย่อยเดียวตามด้วยคำต่อท้ายสาธารณะหรือไม่ ตัวอย่างเช่นส่งคืนจริงสำหรับ google.com และ foo.co.uk แต่ไม่ใช่สำหรับ www.google.com หรือ co.uk

คำเตือน: ผลลัพธ์ที่แท้จริงจากวิธีนี้ไม่ได้หมายความว่าโดเมนอยู่ในระดับสูงสุดซึ่งสามารถกำหนดแอดเดรสเป็นโฮสต์ได้เนื่องจากส่วนต่อท้ายสาธารณะจำนวนมากเป็นโฮสต์ที่ระบุแอดเดรสได้เช่นกัน ตัวอย่างเช่นโดเมน bar.uk.com มีคำต่อท้ายสาธารณะของ uk.com ดังนั้นจึงจะคืนค่าจริงจากวิธีนี้ แต่ uk.com เป็นโฮสต์ที่ระบุแอดเดรสได้

วิธีนี้สามารถใช้เพื่อพิจารณาว่าโดเมนอาจเป็นระดับสูงสุดที่อาจตั้งค่าคุกกี้ได้หรือไม่แม้ว่าจะขึ้นอยู่กับการใช้งานการควบคุมคุกกี้ของเบราว์เซอร์แต่ละตัว ดู 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);
  }
}

6

ฉันเขียนวิธีการ (ดูด้านล่าง) ซึ่งแยกชื่อโดเมนของ url และใช้การจับคู่สตริงแบบง่าย สิ่งที่ทำได้จริงคือแยกบิตระหว่างตัวแรก"://"(หรือดัชนี0ถ้าไม่มี"://") และตัวแรกที่ตามมา"/"(หรือดัชนีString.length()ถ้าไม่มีตามมา"/") ส่วนที่เหลืออยู่ก่อนหน้า"www(_)*."จะถูกสับออก ฉันแน่ใจว่าจะมีบางกรณีที่มันไม่ดีพอ แต่ในกรณีส่วนใหญ่ก็ควรจะดีพอ!

โพสต์ของ Mike Samuel ด้านบนบอกว่าjava.net.URIชั้นเรียนทำได้ (และเป็นที่ต้องการของjava.net.URLชั้นเรียน) แต่ฉันพบปัญหากับURIชั้นเรียน โดยเฉพาะอย่างยิ่งURI.getHost()ให้ค่า null หาก url ไม่รวมโครงร่างนั่นคือ"http(s)"บิต

/**
 * 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

ขอบคุณที่ชี้ให้เห็นลี โปรดทราบว่าฉันมีคุณสมบัติตามคำตอบของฉัน "ฉันแน่ใจว่าจะมีบางกรณีที่ไม่ดีพอ ... " คำตอบของฉันจะต้องมีการปรับเปลี่ยนเล็กน้อยสำหรับกรณีเฉพาะของคุณ
Adil Hussain

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;

2

ในกรณีของฉันฉันต้องการเพียงโดเมนหลักไม่ใช่โดเมนย่อย (ไม่มี "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=th&t=8 " จะมีสำหรับโดเมน "webtoapp.io"


1

ลองอันนี้: java.net.URL;
JOptionPane.showMessageDialog (null, getDomainName (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;}


1
private static final String hostExtractorRegexString = "(?:https?://)?(?:www\\.)?(.+\\.)(com|au\\.uk|co\\.in|be|in|uk|org\\.in|org|net|edu|gov|mil)";
private static final Pattern hostExtractorRegexPattern = Pattern.compile(hostExtractorRegexString);

public static String getDomainName(String url){
    if (url == null) return null;
    url = url.trim();
    Matcher m = hostExtractorRegexPattern.matcher(url);
    if(m.find() && m.groupCount() == 2) {
        return m.group(1) + m.group(2);
    }
    return null;
}

คำอธิบาย: regex มี 4 กลุ่ม สองกลุ่มแรกเป็นกลุ่มที่ไม่ตรงกันและสองกลุ่มถัดไปเป็นกลุ่มที่ตรงกัน

กลุ่มแรกที่ไม่ตรงกันคือ "http" หรือ "https" หรือ ""

กลุ่มที่ไม่ตรงกันที่สองคือ "www." หรือ ""

กลุ่มที่สองที่ตรงกันคือโดเมนระดับบนสุด

กลุ่มแรกที่ตรงกันคืออะไรก็ได้ตามหลังกลุ่มที่ไม่ตรงกันและอะไรก็ได้ก่อนโดเมนระดับบนสุด

การต่อกันของกลุ่มที่ตรงกันทั้งสองจะทำให้เรามีชื่อโดเมน / โฮสต์

PS: โปรดทราบว่าคุณสามารถเพิ่มโดเมนที่รองรับจำนวนเท่าใดก็ได้ใน regex


0

หาก URL อินพุตเป็นอินพุตของผู้ใช้ วิธีนี้ให้ชื่อโฮสต์ที่เหมาะสมที่สุด หากไม่พบให้คืนอินพุต url

private String getHostName(String urlInput) {
        urlInput = urlInput.toLowerCase();
        String hostName=urlInput;
        if(!urlInput.equals("")){
            if(urlInput.startsWith("http") || urlInput.startsWith("https")){
                try{
                    URL netUrl = new URL(urlInput);
                    String host= netUrl.getHost();
                    if(host.startsWith("www")){
                        hostName = host.substring("www".length()+1);
                    }else{
                        hostName=host;
                    }
                }catch (MalformedURLException e){
                    hostName=urlInput;
                }
            }else if(urlInput.startsWith("www")){
                hostName=urlInput.substring("www".length()+1);
            }
            return  hostName;
        }else{
            return  "";
        }
    }

0

ทั้งหมดข้างต้นเป็นสิ่งที่ดี อันนี้ดูเหมือนง่ายสำหรับฉันและเข้าใจง่าย ขอโทษคำพูด ฉันเขียนให้ 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'
}

0

วิธีหนึ่งที่ฉันทำและได้ผลกับทุกกรณีคือการใช้ Guava Library และ regex ร่วมกัน

public static String getDomainNameWithGuava(String url) throws MalformedURLException, 
  URISyntaxException {
    String host =new URL(url).getHost();
    String domainName="";
    try{
        domainName = InternetDomainName.from(host).topPrivateDomain().toString();
    }catch (IllegalStateException | IllegalArgumentException e){
        domainName= getDomain(url,true);
    }
    return domainName;
}

getDomain () อาจเป็นวิธีการทั่วไปกับ regex


0

ในการรับชื่อโดเมนจริงโดยไม่มีโดเมนย่อยฉันใช้:

private String getDomainName(String url) throws URISyntaxException {
    String hostName = new URI(url).getHost();
    if (!hostName.contains(".")) {
        return hostName;
    }
    String[] host = hostName.split("\\.");
    return host[host.length - 2];
}

โปรดทราบว่าสิ่งนี้ใช้ไม่ได้กับโดเมนระดับที่สอง (เช่น. co.uk)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.