用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