如何正确编码此URL

时间:2018-04-11 07:06:28

标签: java jsoup

我正在尝试使用JSoup

获取此URL

http://betatruebaonline.com/img/parte/330/CIGUEÑAL.JPG

即使使用编码,我也有例外。 我不明白为什么编码错了。它返回

  

http://betatruebaonline.com/img/parte/330/CIGUEN%C3%91AL.JPG

而不是正确的

  

http://betatruebaonline.com/img/parte/330/CIGUEN%CC%83AL.JPG

我如何解决这个问题? 感谢。

private static void GetUrl()
{
    try
    {
        String url = "http://betatruebaonline.com/img/parte/330/";
        String encoded = URLEncoder.encode("CIGUEÑAL.JPG","UTF-8");
        Response img = Jsoup
                            .connect(url + encoded)
                            .ignoreContentType(true)
                            .execute();

        System.out.println(url);
        System.out.println("PASSED");
    }
    catch(Exception e)
    {
        System.out.println("Error getting url");
        System.out.println(e.getMessage());
    }
}

4 个答案:

答案 0 :(得分:5)

编码没有错,这里的问题是复合unicode&预组合的字符“Ñ”可以用两种方式显示,它们看起来相同但真的不同

precomposed unicode: Ñ           -> %C3%91
composite unicode: N and ~       -> N%CC%83

我强调两个方面都是正确的,这取决于你想要的unicode类型:

String normalize = Normalizer.normalize("Ñ", Normalizer.Form.NFD);
System.out.println(URLEncoder.encode("Ñ", "UTF-8")); //%C3%91
System.out.println(URLEncoder.encode(normalize, "UTF-8")); //N%CC%83

答案 1 :(得分:3)

这里会发生什么?

正如@yelliver所述,网络服务器似乎在其路径名中使用了NFD编码的unicode。所以解决方案是使用相同的编码。

网络服务器是否正确?

1。对于那些好奇的人(像我一样),这篇关于Multilingual Web Addresses的文章为这个主题带来了一些启示。在section on IRI pathes(网络服务器实际处理的部分)中,它声明:

  

尽管域名注册机构都同意接受特定形式和编码的域名(基于ASCII的punycode),但多脚本路径名称标识位于多种平台上的资源,其文件系统确实并将继续使用许多不同的编码。这使得路径比域名更难处理。

2. 有关如何编码pathes的主题的更多信息,请参阅部分 5.3.2.2。在IETF国际资源标识符(IRI)的拟议标准 rfc3987。它说:

  

IRI的等效性必须依赖于IRI的假设      适当地预先字符规范化而不是应用字符      比较两个IRI时的归一化。转换例外      来自非数字形式,以及来自非基于UCS的转换      字符编码为基于UCS的字符编码。在这些情况下,      必须使用NFC或使用NFC的标准化转码器      互操作性。避免误报和问题      转码, IRI应该使用NFC 创建。使用NFKC可能      避免更多问题;例如,通过选择半宽拉丁语      字母而不是全角,而不是全宽      半角片假名。

3。 Unicode Consortium州:

  

NFKC是标识符的首选形式,特别是在存在安全问题的情况下(参见UTR#36)。 NFD和NFKD对内部处理最有用。

<强>结论

问题中提到的网络服务器不符合IRI标准或unicode联盟的建议,并使用NFD编码代替NFC或NFKC。 correctly encode an URL-String的一种方法如下

URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());

然后将该Uri转换为ASCII字符串:

String correctEncodedURL=uri.toASCIIString(); 

toASCIIString()使用NFC编码的unicode调用encode()IDN.toASCII()会将主机名转换为Punycode

答案 2 :(得分:1)

实际上,您必须在URL编码之前将URL转换为分解的表单。

这是一个使用Guava和java.text.Normalizer

的解决方案
import com.google.common.escape.Escaper;
import com.google.common.net.UrlEscapers;
import org.jsoup.Connection;
import org.jsoup.Jsoup;

import java.text.Normalizer;

public class JsoupImageDownload {

    public static void main(String[] args) {

        String url = "http://betatruebaonline.com/img/parte/330/CIGUEÑAL.JPG";
        String encodedurl = null;
        try {
            encodedurl = Normalizer.normalize(url, Normalizer.Form.NFD);
            Escaper escaper = UrlEscapers.urlFragmentEscaper();
            encodedurl = escaper.escape(encodedurl);
            Connection.Response img = Jsoup
                    .connect(encodedurl)
                    .ignoreContentType(true)
                    .execute();

            System.out.println(url);
            System.out.println("PASSED");
        } catch (Exception e) {
            System.out.println("Error getting url: " + encodedurl);
            System.out.println(e.getMessage());
        }
    }
}

这些是Maven依赖项:

<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.11.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>24.1-jre</version>
</dependency>

答案 3 :(得分:0)

非常简单的解决方案: 编码系统提供和你需要的是不同的,以下解决方案将对你有好处。

private static void GetUrl(String url)
{
    try
    {

        String encodedurl = url.replace("Ñ","N%CC%83");
        Response img = Jsoup
                            .connect(encodedurl)
                            .ignoreContentType(true)
                            .execute();

        System.out.println(url);
        System.out.println("PASSED");
    }
    catch(Exception e)
    {
        System.out.println("Error getting url");
        System.out.println(e.getMessage());
    }
}