含有中文字符的网址404问题
用浏览器访问网址的时候,浏览器会默认编码URL,例如把中文、空格改成类似%xx的形式,服务器接收到之后再解码得到原URL,这个过程对用户来说是透明的。
用HttpClient爬取网络时,发现出现很多404,其中有很多是含有乱码/中文。抽取其中一个网址,发现在浏览器中访问正常,但是程序就会出现404,查看请求头可以发现实际请求的网址是类似
1 | http://tzb.hit.edu.cn/%E5%85%A8%E5%9B%BD%E6%94%BF%E5%8D%8F%E5%8A%9E%E5%85%AC%E5%8E%85/main.psp |
的形式,即中文已经被编码过了。
经过反复测试,发现HttpClient访问有些中文URL正常,另外一些就是404,它们之间的区别在于编码不同,UTF-8编码的可以正常访问,而采用其他编码的由于HttpClient事先并不知道对方服务器采用了什么编码,默认按照UTF-8编码URL,对方服务器在解码时就是乱码,自然找不到资源了。
为了解决这个问题,需要对URL进行预处理,把含有URL中的中文按照编码统一处理成 %xx 的形式,这样就不用再操心编码的问题了。
注意,网址中的中文并不 都 是按照相同的编码处理的,理由如下
我们知道,一个有效的网址(可以)包括 协议、主机、端口、地址、参数(即?y=x&z=x),其中,除了参数串其他的都是由web服务器处理的,而web服务器跟网页的编码并不是一致的,所以参数串需要单独区分。查看index_gbk.php,该页是由gbk编码的,点击第一个网址,检查请求头,可以发现这两部分编码结果是不一样的,前一个”中文”是用utf8编码(?),而后一个是gbk。服务器不支持rewrite,所以没法做更多演示。
解决方案
不过,虽然JAVA里有URLEncoder类似的方法,但是它并不能满足我们的需求。因为我们只需要把中文、空格编码,但是URLEncoder会把http://
也进行编码,结果已经不是有效的网址了。只好自己实现。好在,网上已经有现成的代码了Android 编码URL中文字符串,不过他没有考虑到上面说的编码问题,稍作修改。
示例代码
我采用的是第三种方案:直接遍历url,找到非字母的字符,用16进制编码。然后顺便解决一个url编码中的陈年老坑:空格被转换成”+”号。
1 | public static String encodeUrl(URL url, Charset charset){ |
没有对地址部分编码,因为我的环境是utf8,程序能自动转换,而且中文看起来比较顺眼
测试:
1 | input: http://apache.local/中文utf8.php?key=中文 |
完整代码:Chinese URL