Android 使用 jsoup 解析 html
一、什么是jsoup
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。——摘自百度百科
1、官方文档:
jsoup Cookbook(中文版)、jsoup cookbook(英文版),总有一个版本适合你。
2、能做什么:
由于工作或自己的需要,我们可能要获得某些特定网页的数据,如果没有后台提供也没有开放的Api接口,这时就可以用jsoup自己抓取数据以解决没有数据的问题。
二、使用
1、导入
如果你使用Android Studio的话,首先在Gradle中引入jsoup,这是目前最新的版本
compile 'org.jsoup:jsoup:1.10.3'
其它引入方式见官方文档https://jsoup.org/download
2、选择你需要抓取数据的目标网站,分析目标数据
百度彩票目前已经停止运营了,可以使用 http://tubiao.17mcp.com/Ssq/Haoma.aspx?page=1 来学习
这里我以抓取百度彩票的双色球历史开奖结果为例,用Chrome打开目标网页,进入开发者工具(快捷键 Ctrl + Shift + I),找到要抓取的数据在网页源码的大概位置:
可以看到双色球的开奖结果在一个class="historyOpt"的table中。注意观察table中包含了thead和tbody,如果你了解HTML中table元素的用法,可能已经猜到了开奖结果数据就在tbody中:
图2
展开tbody标签后,其中的tr就是表格中的一行数据,td代表一个单元格。开奖日期、开奖期数、开奖结果都包含在一个td,其中的开奖结果又嵌套在了另一个table中。
3、解析数据
分析完了数据的基本结构,接下来主角jsoup就该登场解析html了。这里我们使用jsoup的使用选择器语法来查找元素功能,如果了解选择器相关概念的话做起来会容易些。关于jsoup选择器语法可以看这里:http://www.open-open.com/jsoup/selector-syntax.htm
解析数据前,总要先拿到网页源码数据吧,这个jsoup可以轻松帮我实现,并且可以直接得到Document对象:
Document doc = Jsoup.connect(URL).get();
ps:当然你也可以使用okhttp,或者retrofit等其它网络框架。
由于改操作设计网络请求,所以需要在子线程进行哦,有了Document,就可以进行解析工作了:
private List<LotteryData> parseHtml(Document document) {
Elements elements = document.select("table[class=historylist] > tbody > tr");
List<LotteryData> list = new ArrayList<>();
LotteryData data;
for (Element element : elements) {
data = new LotteryData();
data.setNum(element.select("td > a").text());//开奖期数
data.setDate(element.select("td").get(1).text());//开奖日期
Elements ems = element.select("td[class=balls] em");//开奖结果
StringBuilder sb = new StringBuilder();
for (Element em : ems) {
sb.append(em.text());
}
data.setBalls(sb.toString());
list.add(data);
}
return list;
}
首先通过Elements elements = document.select("table[class=historylist] > tbody > tr");
使用组合选择器,得到了当前页面class是historylist的table的直接子元素tbody的所有直接子元素tr,也就是当前页面table中的所有行数据,elements是一个ArrayList<Element>类型的集合。
接下来遍历elements,解析每一行数据,我们知道一个element对象对应tr,可以代表一行数据,我们先拿开奖期数,注意观察图2,开奖期数包含在tr行的第一个td单元格中的a标签,所以我们可以这样写:
element.select("td > a").text()
这样就拿到了开奖期数,如图中的2017069。
同样开奖时间是在tr行的第二个td单元格中,获取方式如下:
element.select("td").get(1).text()
至于开奖结果,在tr行中的class是balls的td单元格中,并用一个子table存储,所以只要得到子table中所有em元素就可以了:
Elements ems = element.select("td[class=balls] em");
然后再遍历ems,就可以拿到开奖结果了
for (Element em : ems) {
sb.append(em.text());
}
到这里html就解析完成了。在parseHtml()返回了一个List,可以方便列表显示。
4、展示数据
原网站可以按照开始、结束日期的方式来查询开奖结果,所以我们在demo中采用了分页的形式来加载数据,每一页显示30天的开奖数据,所以就要根据页码计算每一页数据对应的开始日期和结束日期,例如第一页[2017-05-18,2017-06-17]、第二页[2017-04-17,2017-05-17] ......,然后拼接到URL后面,一个完整的请求URL是这样的:http://baidu.lecai.com/lottery/draw/list/50?type=range_date&start=2017-05-18&end=2017-06-17
至于如何根据页码计算日期,还是很简单的,可以参考源码。
到这里我们的流程就分析完了,大致从请求到解析再到展示的代码如下:
private void fetchData() {
new Thread(new Runnable() {
@Override
public void run() {
Document doc = null;
try {
String[] date = getDate(PAGE_NUM);
//拼接URL
URL = URL + "&start=" + date[0] + "&end=" + date[1];
//请求
doc = Jsoup.connect(URL).get();
//解析
final List<LotteryData> datas = parseHtml(doc);
runOnUiThread(new Runnable() {
@Override
public void run() {
//更新列表
parseDataFinish(datas);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
说了这么多,总要有个效果图吧:
图3三、小结
用jsoup解析html还是比较简单的,虽然只是它的一小部分功能,用处还是蛮大的,对于不同的网页最大的差别就在分析出如何根据选择器查找元素。但是如果原网页修改了相关代码,可能就会影响数据抓取了,你就需要及时修改你的解析代码了。文中表达有误的地方还请指正,最后希望对你有所帮助吧!
有需要可以下载 源码