我正在尝试使用JSoup
获取此URLhttp://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());
}
}
答案 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());
}
}