用nutch2.0+cassandra1.0爬取和存储网页时发现gbk编码的网页解析提取文本时中文全都变成乱码,非常奇怪,之前nutch1.x从来就不会出现中文乱码的问题的,因为nutch1.x和nutch2.x用的爬虫代码差不多是一样的,所以我猜可能是保存到cassandra时有问题。看了下它保存网页到cassandra的源码,所有需要保存的值它都转换成二进制封装成ByteBuffer对象传到gora中持久化。看下gora-cassandra的源码中操作cassandra的部分

CassandraClient.java中,addColumn添加数据,值为ByteBuffer的则转换成字符串

public void addColumn(String key, String fieldName, Object value) {
    if (value == null) {
      return;
    }
    if (value instanceof ByteBuffer) {
      value = toString((ByteBuffer) value);
    }
    
    String columnFamily = this.cassandraMapping.getFamily(fieldName);
    String columnName = this.cassandraMapping.getColumn(fieldName);
    
    this.mutator.insert(key, columnFamily, HFactory.createStringColumn(columnName, value.toString()));
  }

ByteUtils.java中把byte转换成字符串的代码

public static String toString(final byte [] b, int off, int len) {
    if(b == null) {
      return null;
    }
    if(len == 0) {
      return "";
    }
    String result = null;
    try {
      result = new String(b, off, len, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return result;
  }

坑爹了,直接就转换成UTF-8保存的,也就是说爬下gbk编码的网页,它把gbk编码转成了UTF-8的字符串,保存到cassandra中,本来这样转换成utf-8也没问题的,不过后来到nutch执行解析的时候,由于nutch的提取页面编码算法比较偏向于请求头的编码(如果请求头没有就提取文件进行计算猜测),而此时的charset=gbk,也就gbk编码。原来utf-8存储到cassandra的数据去出来时转换成了gbk编码,不乱码才怪,知道了原因,要解决就好办了。

我在想为什么它不直接就用二进制的格式存储,这样感觉效率要高点,然后就又看到了CassandraClient.java中的toString方法,还有个TODO注释在那,说不要把二进制的字段从字节转换成字符串存储,原来是还没有完善的。

/**
   * TODO do no convert bytes to string to store a binary field
   * @param value
   * @return
   */
  private static String toString(ByteBuffer value) {
    ByteBuffer byteBuffer = (ByteBuffer) value;
    return ByteUtils.toString(byteBuffer.array(), 0, byteBuffer.limit());
  }

于是就到git上看了下gora0.3版的代码,果然改了,不直接转换成字符串存储了,原来还想自己解决,看来又省了,最简单的解决方法就是把nutch2.0的gora依赖库从0.2,改成0.3