我正在运行一个小型网络爬虫,所以我真的需要一种方法来统一管理所有发现的网址。所以,我已经确定标准是带有punycode主机名的百分比编码的URL。如何将垃圾解析器找到的任何内容转换为正确转义的URL?到目前为止,我想出了以下代码:
import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
public class URLEscaper {
public static String toURI(String url) throws Exception {
if (url == null) return null;
// The input string might already be an escaped url, so decode
// it first, which is an idempotent operation:
URL u = new URL(URLDecoder.decode(url,"UTF-8"));
String scheme = u.getProtocol();
String usercreds = u.getUserInfo();
String host = IDN.toASCII(u.getHost());
int port = u.getPort();
String path = u.getPath();
String query = u.getQuery();
String fragment = u.getRef();
URI result = new URI(scheme, usercreds, host, port, path, query, fragment);
return result.toASCIIString();
}
}
此函数的问题是遵循RFC的java.net.URI严格,它声明主机名不能包含除[a-z0-9-]
之外的任何符号。因此,如果主机名包含,例如下划线'_',那么URI构造函数将抛出URISyntaxException。互联网上有很多完全可访问的网站,其中包含主机名中的下划线。而且我不想失去那些。我可以使用什么代替java.net.URI?
答案 0 :(得分:1)
因此它适用于Apache的commons-httpclient版本的URI实现。
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
简单测试
org.apache.commons.httpclient.URI uri = new URI("http://a_domain_with_underscores.com:8080/demo/index.hp?query=param", true);
System.out.println(uri.getHost());
您可以在控制台中打印域名
a_domain_with_underscores.com
它不适用于apache commons 4,不同的maven groupId / artifact,因为apache已经放弃了URI的内部实现,而是停留在java.net.URI
;关于它的好处是Apache提供了一个很好的org.apache.http.client.utils.URIBuilder
而不是那些丑陋的java.net.URI
构造函数:)
这是Apache的http客户端工件的最新版本......
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
所以代码看起来像这样
URIBuilder uriBuilder = new URIBuilder();
uriBuilder.setScheme("http").setHost("a_domain_with_underscores.com").setPort(8080);
java.net.URI builtURI = uriBuilder.build();
System.out.println(builtURI.getHost());
你最终会在控制台中找到一个令人讨厌的null