[2013.12.27]
好吧……这篇文章与nutch有关系也没有关系。
在nutch把数据放入数据库以后,因为是非结构化的,所以我们还需要从中提取我们需要的数据,这时就需要清洗数据了。
自己写一个小程序是很有用的,Ade这里用的方法是远程jdbc链接数据库,获取webpage中的content字段,然后使用jsoup提取数据。
选用jsoup的原因是content字段中保存了整个html,是半结构化的,需要足够的鲁棒性来从中按结构提取数据,传统的DOM方法肯定会崩溃。
本来想用Java自带的XPath,但就是因为无法将html转换为DOM而放弃了。Jsoup很好用,虽然不支持xpath,但是支持类似cssQuery的语法,几乎与XPath相同,在这里学习10分钟足以。另外jsoup还提供一个在线的实验,把自己要分析的html放上去,然后调试一下jsoup select很快就能定位出想要的数据了。这个与firebugs中找xpath很像。
还需要注意的一点是mysql链接的中文问题,这个网上有很多解决方案,这里就不一一熬述了。
另外,由于content字段是blob类型的,如果直接getString的化中文是乱码的,所以这里需要将blob按照byte获取,并new String(bytes, charset)这样做。由于blob的length是long,如果直接blob.getbytes是填充不到bytes数组中去的(会截断),所以这里需要先写入byteArrayOutputStream,再生成byte数组。
好了,下面给出代码,是一个抓取appchina上游戏app数据并分析的例子,为了追求速度,程序写的粗糙,大概理解其中的含义即可。
package me.yxyz.washer;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class Main {
public static final String driver = "com.mysql.jdbc.Driver";
public static final String url = "jdbc:mysql://115.28.xxx.xxx:3306/nutch_appchina?useUnicode=true&characterEncoding=UTF-8";
public static final String user = "root";
public static final String password = "xxx";
static HashMap<String, String> map = new HashMap<String, String>();
private static BufferedWriter bw = null;
// private static FileWriter fw = null;
public static void main(
String args[]) {
File f = new File("/Users/ade/Desktop/result.csv");
try {
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "GB2312" ));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url,
user,
password);
if (!conn.isClosed())
System.out.println("Succeeded connecting to the Database!");
Statement statement = conn.createStatement();
String sql = "select id,content from webpage ";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
String id = rs.getString("id");
Blob b = rs.getBlob("content");
String content;
try {
content = blobToString(b);
map.put(id,
content);
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("done");
System.out.println("start to parsing");
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
String k = iter.next();
String html = map.get(k);
System.out.println(k + "------->");
// System.out.println( html );
if (html != null)
dealData(html);
}
rs.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( bw != null )
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String blobToString(
Blob b) throws SQLException, IOException {
InputStream is = b.getBinaryStream();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int readLen = 0;
while ((readLen = is.read(buffer)) != -1) {
bao.write(buffer,
0,
readLen);
}
byte[] byteArr = bao.toByteArray();
String s = new String(byteArr, "utf8");
return s;
}
private static void dealData(
String html) {
if (html == null)
return;
Document doc = Jsoup.parse(html);
// learn at: http://jsoup.org/cookbook/extracting-data/selector-syntax
// try at : try.jsoup.org
// name (cn and en)
Elements es = doc.select("ul.app_list > li> div.app_section>p.app_name");
// rate
Elements es2 = doc.select("ul.app_list > li> div.app_section>span");
// download count
Elements es3 = doc.select("ul.app_list > li> div.app_section>p.app_info>span:eq(0)");
// date
Elements es4 = doc.select("ul.app_list > li> div.app_section>p.app_info>span:eq(1)");
// desc
Elements es5 = doc.select("ul.app_list > li> div.app_section>p[class~=desc]");
for (int i = 0; i < es2.size(); i++) {
AppInfo info = new AppInfo();
info.setCnName(es.get(i * 2).text());
info.setEnName(es.get(i * 2 + 1).text());
if( es2.size() > i )
info.setRate(es2.get(i).text());
if( es3.size() > i )
info.setDownloadCount(es3.get(i).text());
if( es4.size() > i )
info.setDate(es4.get(i).text());
if( es5.size() > i )
info.setDescription(es5.get(i).text());
System.out.println(info);
if( bw !=null ){
try {
bw.append( info.toString() + "\n" );
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}