由于博主前一段时间已经自学过了Python网络爬虫,因此在自学Java网络爬虫时进展还是蛮快的。据我目前所学习的Jsoup来看,可以与Python中的request库作为参照进行学习。因此在昨天刚学完Jsoup获取网页后,今天博主便花了一上午时间对Jsoup解析网页进行了学习和分析。
首先,我们先来看一下要爬取和解析的HTML页面。因为刚入门Java网络爬虫,并且据我所知现在大多网站都具备反爬虫手段,因此先用w3school作为学习练手(https://www.w3school.com.cn/python/python_ml_getting_started.asp)。
其中,上图箭头所指的列表便是我们今天要爬取和解析的目标。要想解析图中所示每个课程表的标题及每个课程表对应的URL,则需要遍历选中的元素。
遍历代码模块如下所示:
// 遍历每一个li节点
for(Element ele : elements) {
// .text()为解析标签中的文本内容
String title = ele.select("a").text();
// .attr(String)表示获取标签内某属性内容
String course_url = ele.select("a").attr("href");
System.out.println("标题为:" + title + "\t\tURL为:" + course_url);
}
关于Jsoup解析URL加载的Document,可以先指定URL(可先使用Jsoup请求URL,获取对应的Document)。之后,再使用select(String cssQuery)方法定位要解析的内容。
Jsoup请求及解析URL代码块如下所示:
// 获取URL对应的Document
Document doc = Jsoup.connect("https://www.w3school.com.cn/python/"
+ "python_ml_getting_started.asp").timeout(5000).get();
// 层层定位到要解析的内容,可以发现包含多个li元素
Elements elements = doc.select("div#course").select("li");
首先,利用谷歌自带的抓包工具定位到需要解析的HTML片段,分析发现需要解析的每个课程表的标题及URL在元素a标签中,元素a标签在元素li标签中,元素li标签在div[id=course]中。针对这种层层嵌套(禁止套娃x)的结构,可以使用Jsoup中的遍历解析出每部分内容。
程序运行结果:
说完了Jsoup解析,我们再来说说支持Xpath语法的JsoupXpath解析。JsoupXpath是在Jsoup的基础上扩展支持了Xpath语法的HTML文件解析器,使用时需要先在Maven工程的pom.xml中添加JsoupXpath对应的dependency。
<dependency>
<groupId>cn.wanghaomiao</groupId>
<artifactId>JsoupXpath</artifactId>
<version>2.2</version>
</dependency>
接下来我将直接展示通过JsoupXpath编写的解析程序的源代码,并对里面新学的部分内容进行分析。
程序源代码:
package com.test.study;
import java.util.List;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;
public class JsoupXpathTset {
public static void main(String[] args) {
// 基于URL创建JXDocument
JXDocument jxd = JXDocument.createByUrl("https://www.w3school.com.cn/"
+ "python/python_ml_getting_started.asp");
// Xpath语句
String str = "//*[@id='course']/ul/li/a";
// 获取节点集合
List<JXNode> list = jxd.selN(str);
// 遍历节点
for(int i = 0; i < list.size(); i++) {
JXNode node = list.get(i);
System.out.println("标题为:" + node.asElement().text() +
"\t\tURL为:" + node.asElement().attr("href"));
}
}
}
使用JsoupXpath编写的程序,总体来说和Jsoup编写的程序是不一样的,但是输出结果是相同的,因此不进行展示。
其中:
public List<JXNode> selN(String xpath) throws XpathSyntaxErrorException{
List<JXNode> finalRes = new LinkedList<>();
List<org.seimicrawler.xpath.JXNode> jxNodeList = jxDoc.selN(xpath);
for (org.seimicrawler.xpath.JXNode n:jxNodeList){
if (n.isString()){
finalRes.add(JXNode.t(n.asString()));
}else {
finalRes.add(JXNode.e(n.asElement()));
}
}
return finalRes;
}
根据查询解析Xpath语句JXDocument.selN(String Xpath)方法可以发现.asElement()方法用于非字符串(本程序中为JXNode类型的node节点),而.asString()方法用于字符串。
最后,补充一些JsoupXpath的函数及用法。
String id = element.attr("id"); //获取id的属性值
Elements children = element.children(); //获取元素下的所有子对象元素
String tag = element.tagName(); //获取元素的标签名
String text = element.text(); //获取元素的标签体内容
- text() :提取节点的自有文本;
- node() :提取所有节点;
- position(): 返回当前节点所处在同胞中的位置;
- last() :返回同级节点中的最后那个节点;
- first() :返回同级节点中的第一个节点;
- allText():提取节点下全部文本,取代类似 //div/h3//text()这种递归取文本用法;
- html():获取全部节点的内部的html;
- outerHtml():获取全部节点的 包含节点本身在内的全部html;
- num():抽取节点自有文本中全部数字,如果知道节点的自有文本(即非子代节点所包含的文本)中只存在一个数字,如阅读数,评论数,价格等那么直接可以直接提取此数字出来。如果有多个数字将提取第一个匹配的连续数字。