web-dev-qa-db-fra.com

Comment normaliser une URL en Java?

Normalisation d'URL (ou canonisation d'URL) est le processus par lequel les URL sont modifiées et normalisées de manière cohérente. Le processus de normalisation a pour objectif de transformer une URL en une URL normalisée ou canonique afin de déterminer si deux URL syntaxiquement différentes sont équivalentes.

Les stratégies incluent l’ajout de barres obliques de fin, https => http, etc. La page Wikipedia en répertorie de nombreuses.

Vous avez une méthode préférée pour le faire en Java? Peut-être une bibliothèque ( Nutch ?), Mais je suis ouvert. Des dépendances plus petites et moins c'est mieux.

Je vais coder quelque chose pour le moment et garder un œil sur cette question.

EDIT: je veux normaliser de manière agressive pour compter les URL de la même manière si elles se réfèrent au même contenu. Par exemple, j'ignore les paramètres utm_source, utm_medium, utm_campaign. Par exemple, j'ignore le sous-domaine si le titre est identique.

28
dfrankow
22
Nitrodist

J'ai trouvé cette question hier soir, mais comme je ne cherchais pas de réponse, je me suis fait la mienne. Ici, au cas où quelqu'un à l'avenir le voudrait:

/**
 * - 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;
        }
    }
}
19
Amy B

La bibliothèque RL: https://github.com/backchatio/rl Va bien au-delà de Java.net.URL.normalize () . C'est dans Scala , mais j’imagine qu’il devrait être utilisable à partir de Java.

3
pdxleif

Etant donné que vous souhaitez également identifier les URL faisant référence au même contenu, j'ai trouvé ce document du WWW2007 assez intéressant: Ne pas explorer dans la DUST: Différentes URL avec un texte similaire . Il vous fournit une belle approche théorique.

2
H6.

Non, il n'y a rien dans les bibliothèques standard pour le faire. La Canonicalisation inclut des tâches telles que le décodage de caractères inutilement codés, la conversion de noms d'hôte en minuscules, etc.

par exemple. http://ACME.com/./foo%26bar devient:

http://acme.com/foo&bar

La fonction normalize() de l'URI fait pas fait ceci.

2
Randy Hudson

En Java, normaliser manuellement une 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`

La classe d'URL Java a toutes sortes de méthodes pour analyser une partie de l'URL.

1
Eric Leschinski

Vous pouvez le faire avec Restlet framework en utilisant Reference.normalize() . Vous devriez également être en mesure de supprimer les éléments dont vous n’avez pas besoin très facilement avec cette classe.

1
Bruno

J'ai un moyen simple de le résoudre. Voici mon code 

public static String normalizeURL(String oldLink)
{
    int pos=oldLink.indexOf("://");
    String newLink="http"+oldLink.substring(pos);
    return newLink;
}
0
Thanh Duy Phan