近日在开发爬虫程序时发现,如果事先不指定正确的字符集编码,在得到InputStream字节流实例后使用程序自身去判断,相关代码如下:
[
java]
if(charset == null || "".equals(charset)) {
reader = new InputStreamReader(inputStream);
charset = reader.getEncoding();
}else {
reader = new InputStreamReader(inputStream, charset);
}
[
java]
if(charset == null || "".equals(charset)) {
reader = new InputStreamReader(inputStream);
charset = reader.getEncoding();
}else {
reader = new InputStreamReader(inputStream, charset);
}
在if块语句中,往往会得到错误的charset,原因是创建了一个使用系统平台字符集的 InputStreamReader实例,同时很多专业网站在制作时使用了一个小技巧,就是在文件开头敲空格等,这样就会造成JDK的相关类在判断抓取到 的输入流是什么编码出现错误,继而抓取下来的都是包含乱码的网页。例如抓取http://
www.atcpu.com/bbs首页代码,根据if中的程序判 断,charset=”UTF8”,而页面实际设置了charset=”gb2312”。从这里也可看出JDK在底层的字节流,字符流的实现上仍然是不够 成熟,容易出现错误。
因为在构成InputStreamReader实例时的字符集出错,所以即使对抓取到的乱码的网页字符串重新转码也得不到正确的结果。
由于inputStream字节流只允许读取一遍,往往还不支持mark(int),reset()等方法,所以根据这个特性,思考了若干解决方 案,其中比较接近的一个是,先使用缺省字符集将字节流inputStream转换为字符流InputStreamReader,再使用 BufferedReader类包装一层,在BufferedReader读取到包含charset的时候,对charset进行判断后,重新实例化 InputStreamReader,然后接着逐行读取。代码如下:
[
java]
//原始的BufferedReader实例,reader即为上面代码产生的实例
bufferedReader = new BufferedReader(reader);
boolean mark = false;
StringBuffer buffer = new StringBuffer();
String str = "";
int count = 0;
while ((str = bufferedReader.readLine()) != null) {
if(mark ;; count > 0) {
bufferedReader.reset();
count = 0;
}
buffer.append(str).append("/n");
if(!mark){
count ++;
String tempStr = str.toLowerCase();
if(tempStr.indexOf(DetectorConstants.HtmlTagProperty.HTTP_EQUIV) != -1
;; tempStr.indexOf(DetectorConstants.HtmlTagProperty.CHARSET) != -1){
//此处略过了实际分析过程,直接给出结果
String anotherCharset = "gb2312";
if(anotherCharset != null ;; !"".equals(anotherCharset) ;; !anotherCharset.equals(charset)){
charset = anotherCharset;
reader = new InputStreamReader(urlStream, anotherCharset);
bufferedReader = new BufferedReader(reader);
int av = urlStream.available();
bufferedReader.mark(av + 1);//也可以使用count试试
mark = true;
}
}
}
}
[
java]
//原始的BufferedReader实例,reader即为上面代码产生的实例
bufferedReader = new BufferedReader(reader);
boolean mark = false;
StringBuffer buffer = new StringBuffer();
String str = "";
int count = 0;
while ((str = bufferedReader.readLine()) != null) {
if(mark ;; count > 0) {
bufferedReader.reset();
count = 0;
}
buffer.append(str).append("/n");
if(!mark){
count ++;
String tempStr = str.toLowerCase();
if(tempStr.indexOf(DetectorConstants.HtmlTagProperty.HTTP_EQUIV) != -1
;; tempStr.indexOf(DetectorConstants.HtmlTagProperty.CHARSET) != -1){
//此处略过了实际分析过程,直接给出结果
String anotherCharset = "gb2312";
if(anotherCharset != null ;; !"".equals(anotherCharset) ;; !anotherCharset.equals(charset)){
charset = anotherCharset;
reader = new InputStreamReader(urlStream, anotherCharset);
bufferedReader = new BufferedReader(reader);
int av = urlStream.available();
bufferedReader.mark(av + 1);//也可以使用count试试
mark = true;
}
}
}
}
这种方法可以得到正确的编码格式的page页面,然而由于使用不同的字符集实例化InputStreamReader,造成inputStream流在使用新的字符集重新实例化后,之前的定位发生变化,前后的位置不一致,中间往往会漏掉大约400多行字符。
研究了一些相关开源项目,例如HtmlParser,发现也是无法提供一个正确的,好用的方法来判断字节流的编码格式。