2010-06-07 8 views
26

URL normalization (o canonicalizzazione URL) è il processo tramite il quale gli URL vengono modificati e standardizzati in modo coerente. L'obiettivo del processo di normalizzazione è trasformare un URL in un URL normalizzato o canonico in modo che sia possibile determinare se due URL sintatticamente diversi sono equivalenti.Come normalizzare un URL in Java?

Le strategie includono l'aggiunta di barre finali, https => http, ecc. La pagina di Wikipedia elenca molte.

Hai un metodo preferito per farlo in Java? Forse una biblioteca (Nutch?), Ma sono aperto. Meno e meno dipendenze è meglio.

Farò un handcode per ora e tieni d'occhio questa domanda.

MODIFICA: Voglio normalizzarmi in modo aggressivo per contare gli URL come gli stessi se si riferiscono allo stesso contenuto. Ad esempio, ignoro i parametri utm_source, utm_medium, utm_campaign. Ad esempio, ignoro il sottodominio se il titolo è lo stesso.

risposta

20
+3

Buono! Tuttavia, non è abbastanza lontano per me. La prima cosa che ho fatto è stata quella di inserire i seguenti parametri: utm_source, utm_medium, utm_campaign.Sono in molti URL in natura, ma rimuoverli lascia semanticamente gli URL allo stesso modo per analizzare i contenuti a cui si riferiscono. – dfrankow

+1

@dfrankow Questo non è necessariamente vero. Non c'è niente per impedire a un sito di pubblicare contenuti diversi in base a tali parametri. –

+0

Certo, ma in pratica, quelli sono utilizzati da un pacchetto di marketing (Google Analytics) per tenere traccia delle campagne, quindi non sono suscettibili di variazioni. – dfrankow

1

Si può fare questo con il quadro Restlet utilizzando Reference.normalize(). Dovresti anche essere in grado di rimuovere gli elementi che non ti servono abbastanza comodamente con questa classe.

18

Ho trovato questa domanda la scorsa notte, ma non c'era una risposta che stavo cercando così ho fatto il mio. qualcuno Qui si incassa in futuro lo vuole:

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

Grazie per questo, mi piace l'approccio, ma ho riscontrato alcuni problemi con l'implementazione: 1) Un'eccezione di modifica simultanea viene sollevata nel ciclo di rimozione utm_ e chiavi di sessione (a meno che non sia l'ultima voce), dal momento che sei rimuovendo dalla raccolta durante l'iterazione. Dovresti usare un iteratore e il metodo remove(). 2) la ri-fuga dei parametri rompe alcuni siti web che ho provato. Questo va bene se stai usando la versione canonica per confrontare gli URL, che è quello che ho finito per fare. Immagino che rimuovere il token di sessione potrebbe anche distruggere alcuni siti, quindi è davvero discutibile. –

+1

Non va bene togliere la barra finale da un URL. Rende infatti un URL diverso. Ad esempio l'alias di Apache potrebbe non funzionare se è impostato con una barra finale. – rustyx

2

No, non c'è nulla nelle librerie standard per farlo. La canonizzazione include cose come la decodifica di caratteri codificati inutilmente, la conversione di nomi di host in minuscolo, ecc.

ad es. http://ACME.com/./foo%26bar diventa:

http://acme.com/foo&bar

di normalize() URI fa non fare questo.

1

In Java, normalizzare un URL manualmente

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` 

Java classe URL ha tutti i tipi di metodi per analizzare qualsiasi parte dell'URL.

+2

Normalizzazione/canonicalizzazione si riferisce a una trasformazione che garantisce che i dati definiti come semanticamente equivalenti diventino identici. Strippare i dati essenziali non è la normalizzazione. – AndrewF

Problemi correlati