这是我参与「第四届青训营 」笔记创作活动的第3天
本文章续接上一章juejin.cn/post/713509… ,就爬虫系统的解析部分进行讲解。爬虫系统主要用来爬取数据、解析并存储,该模块较为独立,可以扩展到不同的计算框架下进行调度使用。
首先介绍几个比较有用的工具。
HtmlCleaner
HtmlCleaner是一个开源的Java语言的Html文档解析器。HtmlCleaner能够重新整理HTML文档的每个元素并生成结构良好(Well-Formed)的 HTML 文档。默认它遵循的规则是类似于大部份web浏览器为创文档对象模型所使用的规则。然而,用户可以提供自定义tag和规则组来进行过滤和匹配。其中比较常用的应该是evaluateXPath函数,根据XPath寻找相应的值。
XPath概览
XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式,另外它还提供了超过 100 个内建函数用于字符串、数值、时间的匹配以及节点、序列的处理等等,几乎所有我们想要定位的节点都可以用XPath来选择。
具体来说,XPath为需要读取的目录,可以是
/body/table[2]/tbody/tr/td[2]/table[4]/tbody/tr/td/table/tbody 。
代码实现
使用evaluateXPath获取网页中某种属性的方法如下。
public static String getAttrByXpath(TagNode tagNode, String attr, String xpath) {
try {
Object[] objs = tagNode.evaluateXPath(xpath);
if (objs != null && objs.length > 0) {
TagNode node = (TagNode) objs[0];
return node.getAttributeByName(attr);
}
} catch (XPatherException e) {
e.printStackTrace();
}
return null;
}
此外,还可以根据当前网页的基本情况,定义针对某一个属性的个性化的解析函数。下图为获取京东商品基础属性的代码。
public static JSONObject getParams(TagNode tagNode, String xpath, String paramTitleXpath, String paramValueXpath) {
Object[] objs;
JSONObject paramJSONObj = new JSONObject();
try {
// 获取规格参数中所有的[规格类目]
objs = tagNode.evaluateXPath(xpath);
if (objs != null && objs.length > 0) {
// 遍历每一个[规格类目] div,其中每一个都包含[左侧大标题 + 右侧细节信息]
for (Object obj : objs) {
TagNode paramNode = (TagNode) obj;
// 左侧大的标题
objs = paramNode.evaluateXPath(paramTitleXpath);
String paramTitle = null;
if (objs != null && objs.length > 0) {
TagNode paramTitleNode = (TagNode) objs[0];
paramTitle = paramTitleNode.getText().toString();
}
// 右侧细节信息
objs = paramNode.evaluateXPath(paramValueXpath);
JSONObject dlJsonObject = null;
if (objs != null && objs.length > 0) {
dlJsonObject = new JSONObject();
TagNode dlNode = (TagNode) objs[0];
List<TagNode> childTagList = dlNode.getChildTagList();
for (int i = 0; i < childTagList.size()-2; i = i + 2) {
TagNode childTagTitle = childTagList.get(i); // i = 0
TagNode chileTagValue = childTagList.get(i + 1); // 1
if (chileTagValue.getAttributeByName("class") != null) {
i++;
chileTagValue = childTagList.get(i + 1); // 3
}
dlJsonObject.put(childTagTitle.getText().toString().trim(), chileTagValue.getText().toString().trim());
}
}
paramJSONObj.put(paramTitle, dlJsonObject);
}
}
return paramJSONObj;
} catch (XPatherException e) {
e.printStackTrace();
}
return null;
}
对于某些特殊的属性,例如:商品价格,商品评论数,网页将其封装在函数中,可以通过观察html内容的规律,获得其存放的地址。例如京东的商品价格存放在https://p.3.cn/prices/mgets?pduid=1504781656858214892980&skuIds=J_" + id,则其解析方法为:
String priceUrl = "https://p.3.cn/prices/mgets?pduid=1504781656858214892980&skuIds=J_" + id;
String priceJson = HttpUtil.getHttpContent(priceUrl);
if (priceJson != null) {
if (priceJson.contains("error")) {
logger.info("价格url已经不可用,请及时更换pduid--->" + priceJson);
} else {
JSONArray priceJsonArray = new JSONArray(priceJson);
JSONObject priceJsonObj = priceJsonArray.getJSONObject(0);
String priceStr = priceJsonObj.getString("p").trim();
Float price = Float.valueOf(priceStr);
page.setPrice(price);
}
}
参考
zhuanlan.zhihu.com/p/29436838 blog.csdn.net/qq_41943867… www.cnblogs.com/xiehaoyu/p/… blog.51cto.com/xpleaf/2093…