2010-06-07 35 views
26

URL normalization (hoặc URL canonicalization) là quá trình theo đó URL được sửa đổi và chuẩn hóa theo cách nhất quán. Mục tiêu của quá trình chuẩn hóa là chuyển đổi URL thành URL chuẩn hoặc chuẩn để có thể xác định xem hai URL khác nhau về cú pháp có tương đương hay không.Làm cách nào để chuẩn hóa URL trong Java?

Chiến lược bao gồm thêm dấu gạch chéo, https => http, v.v. Trang Wikipedia liệt kê nhiều.

Có phương pháp yêu thích để thực hiện việc này trong Java? Có lẽ một thư viện (Nutch?), Nhưng tôi đang mở. Phụ thuộc nhỏ hơn và ít hơn là tốt hơn.

Tôi sẽ mã hóa nội dung nào đó ngay bây giờ và theo dõi câu hỏi này.

EDIT: Tôi muốn tích cực chuẩn hóa để đếm các URL giống nhau nếu chúng tham chiếu đến cùng một nội dung. Ví dụ: tôi bỏ qua các thông số utm_source, utm_medium, utm_campaign. Ví dụ: tôi bỏ qua tên miền phụ nếu tiêu đề giống nhau.

Trả lời

20
+3

Tốt nhất! Tuy nhiên, nó không đủ gần với tôi. Điều đầu tiên tôi đã làm là giúp đưa ra các thông số sau: utm_source, utm_medium, utm_campaign.Chúng có nhiều URL trong tự nhiên, nhưng việc loại bỏ chúng cũng giống nhau về mặt ngữ nghĩa cho các mục đích phân tích nội dung chúng đề cập đến. – dfrankow

+1

@dfrankow Điều đó không nhất thiết phải đúng. Không có gì để ngăn trang web phân phối nội dung khác nhau dựa trên các thông số đó. –

+0

Chắc chắn, nhưng thực tế nói, chúng được sử dụng bởi một số gói tiếp thị (Google analytics?) Để theo dõi các chiến dịch, vì vậy chúng sẽ không thay đổi. – dfrankow

1

Bạn có thể làm điều này với Restlet khuôn khổ sử dụng Reference.normalize(). Bạn cũng có thể loại bỏ các yếu tố bạn không cần khá thuận tiện với lớp này.

18

Tôi đã tìm thấy câu hỏi này tối qua, nhưng không có câu trả lời tôi đang tìm kiếm vì vậy tôi đã tự làm. ai đó ở đây nó được trong trường hợp trong tương lai muốn nó:

/** 
* - Covert the scheme and host to lowercase (done by java.net.URL) 
* - Normalize the path (done by java.net.URI) 
* - Add the port number. 
* - Remove the fragment (the part after the #). 
* - Remove trailing slash. 
* - Sort the query string params. 
* - Remove some query string params like "utm_*" and "*session*". 
*/ 
public class NormalizeURL 
{ 
    public static String normalize(final String taintedURL) throws MalformedURLException 
    { 
     final URL url; 
     try 
     { 
      url = new URI(taintedURL).normalize().toURL(); 
     } 
     catch (URISyntaxException e) { 
      throw new MalformedURLException(e.getMessage()); 
     } 

     final String path = url.getPath().replace("/$", ""); 
     final SortedMap<String, String> params = createParameterMap(url.getQuery()); 
     final int port = url.getPort(); 
     final String queryString; 

     if (params != null) 
     { 
      // Some params are only relevant for user tracking, so remove the most commons ones. 
      for (Iterator<String> i = params.keySet().iterator(); i.hasNext();) 
      { 
       final String key = i.next(); 
       if (key.startsWith("utm_") || key.contains("session")) 
       { 
        i.remove(); 
       } 
      } 
      queryString = "?" + canonicalize(params); 
     } 
     else 
     { 
      queryString = ""; 
     } 

     return url.getProtocol() + "://" + url.getHost() 
      + (port != -1 && port != 80 ? ":" + port : "") 
      + path + queryString; 
    } 

    /** 
    * Takes a query string, separates the constituent name-value pairs, and 
    * stores them in a SortedMap ordered by lexicographical order. 
    * @return Null if there is no query string. 
    */ 
    private static SortedMap<String, String> createParameterMap(final String queryString) 
    { 
     if (queryString == null || queryString.isEmpty()) 
     { 
      return null; 
     } 

     final String[] pairs = queryString.split("&"); 
     final Map<String, String> params = new HashMap<String, String>(pairs.length); 

     for (final String pair : pairs) 
     { 
      if (pair.length() < 1) 
      { 
       continue; 
      } 

      String[] tokens = pair.split("=", 2); 
      for (int j = 0; j < tokens.length; j++) 
      { 
       try 
       { 
        tokens[j] = URLDecoder.decode(tokens[j], "UTF-8"); 
       } 
       catch (UnsupportedEncodingException ex) 
       { 
        ex.printStackTrace(); 
       } 
      } 
      switch (tokens.length) 
      { 
       case 1: 
       { 
        if (pair.charAt(0) == '=') 
        { 
         params.put("", tokens[0]); 
        } 
        else 
        { 
         params.put(tokens[0], ""); 
        } 
        break; 
       } 
       case 2: 
       { 
        params.put(tokens[0], tokens[1]); 
        break; 
       } 
      } 
     } 

     return new TreeMap<String, String>(params); 
    } 

    /** 
    * Canonicalize the query string. 
    * 
    * @param sortedParamMap Parameter name-value pairs in lexicographical order. 
    * @return Canonical form of query string. 
    */ 
    private static String canonicalize(final SortedMap<String, String> sortedParamMap) 
    { 
     if (sortedParamMap == null || sortedParamMap.isEmpty()) 
     { 
      return ""; 
     } 

     final StringBuffer sb = new StringBuffer(350); 
     final Iterator<Map.Entry<String, String>> iter = sortedParamMap.entrySet().iterator(); 

     while (iter.hasNext()) 
     { 
      final Map.Entry<String, String> pair = iter.next(); 
      sb.append(percentEncodeRfc3986(pair.getKey())); 
      sb.append('='); 
      sb.append(percentEncodeRfc3986(pair.getValue())); 
      if (iter.hasNext()) 
      { 
       sb.append('&'); 
      } 
     } 

     return sb.toString(); 
    } 

    /** 
    * Percent-encode values according the RFC 3986. The built-in Java URLEncoder does not encode 
    * according to the RFC, so we make the extra replacements. 
    * 
    * @param string Decoded string. 
    * @return Encoded string per RFC 3986. 
    */ 
    private static String percentEncodeRfc3986(final String string) 
    { 
     try 
     { 
      return URLEncoder.encode(string, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); 
     } 
     catch (UnsupportedEncodingException e) 
     { 
      return string; 
     } 
    } 
} 
+0

Cảm ơn vì điều này, tôi thích cách tiếp cận, nhưng tôi đã tìm thấy một vài vấn đề với việc thực hiện: 1) Ngoại lệ sửa đổi đồng thời được nâng lên trong vòng loại bỏ utm_ và khóa phiên (trừ khi đó là mục nhập cuối cùng), vì bạn xóa khỏi bộ sưu tập trong khi lặp lại. Bạn nên sử dụng một trình lặp và phương thức remove(). 2) việc tái thoát của các tham số phá vỡ một số trang web mà tôi đã thử. Đó là tốt nếu bạn chỉ sử dụng phiên bản kinh điển để so sánh các URL mặc dù, đó là những gì tôi đã kết thúc làm. Tôi tưởng tượng việc xóa mã thông báo phiên cũng có thể phá vỡ một số trang web, do đó, nó thực sự khắc phục. –

+1

Không tốt để tách dấu gạch chéo theo sau khỏi URL. Nó làm cho một URL khác trong thực tế. Ví dụ: Apache aliasing có thể không hoạt động nếu nó được thiết lập với dấu gạch chéo. – rustyx

2

Không, không có gì trong thư viện chuẩn để thực hiện việc này. Canonicalization bao gồm những thứ như giải mã các ký tự được mã hóa không cần thiết, chuyển đổi tên máy chủ thành chữ thường, v.v.

ví dụ: http://ACME.com/./foo%26bar trở thành:

http://acme.com/foo&bar

URI của normalize() làm không làm điều này.

3

Thư viện RL: https://github.com/backchatio/rl hoàn toàn nằm ngoài java.net.URL.normalize(). Đó là trong Scala, nhưng tôi tưởng tượng nó có thể sử dụng được từ Java.

1

Trong Java, bình thường hóa một URL bằng tay lớp URL

String company_website = "http://www.foo.bar.com/whatever&stuff"; 

try { 
    URL url = new URL(company_website); 
    System.out.println(url.getProtocol() + "://" + url.getHost()); 
} catch (MalformedURLException e) { 
    e.printStackTrace(); 
} 

//prints `http://www.foo.bar.com` 

Các java có tất cả các loại phương pháp để phân tích ra bất kỳ một phần của URL.

+2

Chuẩn hóa/chuẩn hóa đề cập đến một phép chuyển đổi đảm bảo dữ liệu được xác định là tương đương ngữ nghĩa trở thành giống hệt nhau. Tước dữ liệu cần thiết không phải là chuẩn hóa. – AndrewF

Các vấn đề liên quan