jsoup_解析任意网站,做任意网站客户端
jsoup是一个解析网页源码的开源库,他能按照给定的规则提取出一个网页中的任意元素,和其他网页解析库不同的是,他提取网页内容的方式和css、jquery的选择器非常相似。因此如果你懂得前端的知识,只需根据以下的代码样例就可以在3分钟之内学会jsoup的用法:
1
2
3
4
5
|
Document doc = Jsoup.connect(href).timeout(10000).get(); Element masthead = doc.select( "div.archive-list" ).first(); Elements titleElements = masthead.select( "div.archive-list-item h4 a" ); Elements summaryElements = masthead.select( "div.archive-list-item div.post-intro p" ); Elements imgElements = masthead.select( "div.archive-list-item img" ); |
没错就是3分钟,我第一次接触jsoup完全没有看官方文档,直接在网上下载了jsoup的jar包,然后找了一段jsoup的例子程序就开始用他来解析网页了,最开始我想做一个cnbeta的客户端,非常顺利,从cnbeta网页上解析得到的数据就跟是cnbeta专门为我提供的一样。
不过需要明白的是使用jsoup开发客户端并不是一个客户端开发的首选,一般是针对那些没有为你提供客户端接口的网站,一个标准的客户端接口应该解析的数据形式是json或者xml,有些网站都提供了rss的功能,rss其实就是xml格式的,所以开发一个网站的客户端可以基于一个网站的rss数据。
那么为什么还要用jsoup呢,原因有两点:1、不是所有网站都有rss;2、有的网站rss功能比较全,能够覆盖网站的大部分内容,但有的网站rss很简单,基本就是摆设。
而使用jsoup,你在网站上能看到的任何东西都可以解析出来。
但是jsoup开发网站客户端其实有个弊端,因此给自己的网站做客户端绝对不会用jsoup,而是专门写接口。
那就是一旦网站改版,原来的解析规则就失效了,客户端上可能显示不出任何数据,而rss一般很难得改一次。
因此使用jsoup开发网站客户端最好针对那些版面比较固定的网站。
回到我们的话题,我们将针对jcodecraeer的《综合资讯》栏目的文章列表 (http://jcodecraeer.com/plus/list.php?tid=4) 做解析来得到文章列表并显示在一个ListView中。如果你学会了这点,解析一个网站就不成问题了,当然有些网站的解析要复杂一些,你必须先对这个网站做一些数据分析,提炼出一些数据模型。
提取数据模型
我们假设 http://jcodecraeer.com/plus/list.php?tid=4 就是一个网站(事实上它只是一个栏目),来看看我们能够提炼出什么数据模型。
这个网页上有导航栏,还有右边的一些相关文章之类的。但是我只关心文章列表,仔细观察文章列表,其实每一项都是固定的:标题、缩略图、摘要、发表时间、标签、作者、阅览数,这就是我们的数据模型,是一篇文章的模型。为了在ListView中显示文章列表,我们新建一个用于表示一篇文章的Article类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.jcodecraeer.newsapp; public class Article { private String title; private String summary; private String url; private String imageUrl; private String postTime; public void setTitle(String title) { this .title = title; } public String getTitle() { return title; } public void setSummary(String summary) { this .summary = summary; } public String getSummary() { return summary; } public void setUrl(String url){ this .url=url; } public String getUrl(){ return url; } public void setImageUrl(String imageUrl){ this .imageUrl = imageUrl; } public String getImageUrl(){ return imageUrl; } public void setPostTime(String postTime){ this .postTime = postTime; } public String getPostTime(){ return postTime; } } |
接下来的任务就是解析网页
一个网页是由html标签组成的,要查看这些代码可以直接在网页的任何位置右键,在弹出的菜单中点击查看网页源代码。
网页的源码很多,有很多是我们不关心的,你的任务是迅速找到目标代码块,这篇文章的目的是要提取文章列表,所以我找到了文章列表相关的代码块(要熟练的找到需要一点点前端的知识):
从上面的代码我可以肯定,文章列表位于
<div class="archive-list">
所包含的div中,而每一篇文章又是包含在
<div class="archive-list-item">
div中。因此我们先找出class="archive-list"的节点,确保后续的解析是文章相关的代码,然后再在这个节点之下解析每篇文章以及文章的数据项。
找出archive-list节点:
Jsoup连接网页:
1
|
|
找出archive-list节点:
1
|
Element masthead = doc.select( "div.archive-list" ).first(); |
注意最后必须加上first()方法,不然得到的不是单个数据而是一组数据。select("div.archive-list")中select顾名思义是选择的意思,其中“div.archive-list”是css 选择器的写法,意思是class=“archive-list”的div,所以
doc.select("div.archive-list")的意思就是找出class=“archive-list”的所有div,doc.select("div.archive-list").first()的意思就是找出第一个class=“archive-list”的div。
获取每篇文章直接相关的html元素,比如与文章标题相关的直接元素为class="archive-list-item"的div 下面的h4 标签下面的超链接中,用Jsoup表示就是:
1
|
Elements titleElements = masthead.select( "div.archive-list-item h4 a" ); |
上面获取了所有文章的标题相关的html元素(这里是个超链接),返回的结果是Elements类型,他是一个List类型,按照同样的道理我们再获取所有缩略图,所有摘要,所有发表时间,他们的顺序应该是一一对应的,然后根据其中任意一个的Elements数量来遍历,将这些数据一条一条的赋予上面我们定义的Article模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
Document doc = Jsoup.connect(href).timeout(10000).get(); Element masthead = doc.select( "div.archive-list" ).first(); Elements titleElements = masthead.select( "div.archive-list-item h4 a" ); Elements summaryElements = masthead.select( "div.archive-list-item div.post-intro p" ); Elements imgElements = masthead.select( "div.archive-list-item img" ); Elements postTimeElements = masthead.select( "div.archive-list-item div.post-intro span.date" ); int count=titleElements.size(); Log.i( "count" , "count = " + count); for (int i = 0;i < count;i++) { Article article = new Article(); Element titleElement = titleElements.get(i); Element summaryElement = summaryElements.get(i); Element imgElement = imgElements.get(i); String url = titleElement.attr( "href" ); String title = titleElement.text(); String summary = summaryElement.text(); article.setTitle(title); article.setSummary(summary); article.setImageUrl(imgsrc); articleList.add(article); } |
以标题为例,遍历的时候我们取出一篇文章的title节点:
1
|
Element titleElement = titleElements.get(i); |
然后用 String title = titleElement.text();获取标题的文字,以
1
2
|
String url = titleElement.attr( "href" ); |
获取文章的超链接,注意html源码中这个超链接是相对路径,因此我们增加了网站的网址来组成完整路径。之所以可以使用
1
|
titleElement.attr( "href" ); |
是因为这是一个a标签,a标签是有href属性的。
好了,上面的这种方式在后来我发现是有问题的,因为文章标题的列表和缩略图的列表并不是一一对应的,有些文章可能没有缩略图,所以我们换一种方式,获得包含一篇文章所有信息的节点,然后针对每一个节点在for循环内部再解析出文章的每一个数据项。如果没有缩略图,该数据项为空就是了,这就不会有任何问题。
下面是经过改进后的代码,这是一个解析的过程因此我将方法命名为parseArticleList:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public ArrayList<Article> parseArticleList(String href, final int page){ ArrayList<Article> articleList = new ArrayList<Article>(); try { href = _MakeURL(href, new HashMap<String, Object>(){{ put( "PageNo" , page); }}); Log.i( "url" , "url = " + href); Document doc = Jsoup.connect(href).timeout(10000).get(); Element masthead = doc.select( "div.archive-list" ).first(); Elements articleElements = masthead.select( "div.archive-list-item" ); for (int i = 0; i < articleElements.size(); i++) { Article article = new Article(); Element articleElement = articleElements.get(i); Element titleElement = articleElement.select( "h4 a" ).first(); Element summaryElement = articleElement.select( "div.post-intro p" ).first(); Element imgElement = null ; if (articleElement.select( "img" ).size() != 0){ imgElement = articleElement.select( "img" ).first(); } Element timeElement = articleElement.select( ".date" ).first(); String title = titleElement.text(); String summary = summaryElement.text(); String imgsrc = "" ; if (imgElement != null ){ } String postTime = timeElement.text(); article.setTitle(title); article.setSummary(summary); article.setImageUrl(imgsrc); article.setPostTime(postTime); article.setUrl(url); articleList.add(article); } } catch (Exception e) { e.printStackTrace(); } return articleList; } |
parseArticleList()方法返回了 ArrayList<Article>的集合,有了它你应该知道如何在ListView中使用了吧。
分页问题
上面所讨论的仅仅是单个网页,在这个栏目下有很多页数据,因此我们还需要考虑页码的问题,如果还有更多的页,当ListView滑动到最底下自动加载下一页的数据。
实现自动加载下一页ListView我已经放在了文末给出的完整源码中,这里就不讨论了,这里要继续讨论的是如何处理好分页的问题。
首先我们要先分析这个网站文章列表的url地址,第一页的url地址为:http://jcodecraeer.com/plus/list.php?tid=4 第二页为http://jcodecraeer.com/plus/list.php?tid=4&TotalResult=454&PageNo=2,第三页为:
http://jcodecraeer.com/plus/list.php?tid=4&TotalResult=454&PageNo=3,显然,不同页之间url上的区别是仅仅PageNo参数。那么当我们加载更多页面的时候只需把请求的url的PageNo换一下就行了,然而问题是如何确定是第几页呢?
我们再看,发现每一页文章数为10,所以我们判断加载页数的依据是:
1
|
int pageIndex = mArticleList.size() / 10 + 1; |
还有一个问题,如何判断是否能够加载更多页,我们怎么确定第九页之后还有第10页呢?
1
2
3
4
5
|
if (articleList.size() < 10) { //已经加载完了 } else if (articleList.size() == 10) { //还有更多页 } |
当然这个判断方法有缺陷,如果第9页刚刚有10条数据,而么有第10页,这种情况是可能的,但是这不是什么大问题。
这种几率很小
即便这种情况存在,当加载10页的数据什么也没有,articleList = 0 小于10 ,进入第一个判断条件。也达到了目的。
图片的异步加载
图片的缩略图加载是需要异步的,你可以使用 universal image loader ,但在我们给出的demo中使用的是自己实现的一个ImageLoader。
运行界面:
在这篇文章中我们只是简单的完成了文章列表的展示,你还可以根据我所提供的方法实现更多的功能,除了Jsoup的使用之外,更重要的是分析一个网站,我已经采用这种方法把一个专门介绍日本爱情动作片的网站全部解析做成了客户端。事实证明这种方式是完全可行的。其实即便是网站改版,也可以很快写出新的解析规则,我们甚至可以将解析规则放到网络上,这样到网站改版的时候,就不需要变更客户端的代码使客户端照常正常运行。
Jsoup的应用远远不止于此,我们可以用Jsoup解析出整个网站,将网站的所有数据提炼出来从未到达采集网站的目的。
读完这篇文章你可能会产生一个疑问,为什么不直接把整个列表取出来放到webview中呢,其实这个问题就好比是使用html5还是原生app的问题。webview的效率和体验在短时间内难以超越原生应用。
代码下载:http://pan.baidu.com/s/1c0s5MiG
转:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1226/2218.html
jsoup_解析任意网站,做任意网站客户端的更多相关文章
- PublicCMS 网站漏洞 任意文件写入并可提权服务器权限
PublicCMS是目前网站系统中第一个采用JAVA架构 TOMCAT+Apcche+Mysql数据库架构的CMS网站,开源,数据承载量大,可以承载到上千万的数据量,以及用户的网站并发可达到上千万的P ...
- FineReport:任意时刻只允许在一个客户端登陆账号的插件
在使用FineReport报表系统中,处于账户安全考虑,有些企业希望同一账号在任意时刻智能在统一客户端登录.那么当A用户在C1客户端登陆后,该账号又在另外一个C2客户端登陆,服务器如何取判断呢? 开发 ...
- 介绍一款非常适合做微网站并且免费的CMS系统
在微网站火热的今天,寻找一款具备 web app功能的CMS系统能够大大提高我们的工作效率,eBSite升级到3.0后,开始支持web app 皮肤,也就是创建一个站点,会同时绑定一个PC版皮肤与一个 ...
- Linux系统下利用wget命令把整站下载做镜像网站
Linux系统下利用wget命令把整站下载做镜像网站 2011-05-28 18:13:01 | 1次阅读 | 评论:0 条 | itokit 在linux下完整的用wget命令整站采集网站做镜像 ...
- DNS解析服务使用的系统对网站的安全起着很重要的作用
1 采用独享的DNS服务器解析系统 DNS解析服务使用的系统对网站的安全极其重要.现在国内的DNS服务器普遍是公用的,即是你的企业网站DNS解析服务和其他许多网站都是由同一个DNS服务器提供.一旦某个 ...
- 海蜘蛛网络科技官方网站 :: 做最好的中文软路由 :: 软件路由器 :: 软路由 :: 软件路由 :: RouterOs
海蜘蛛网络科技官方网站 :: 做最好的中文软路由 :: 软件路由器 :: 软路由 :: 软件路由 :: RouterOs 企业简介 武汉海蜘蛛网络科技有限公司成立于2005年,是一家专注于网络新技术研 ...
- ASP.NET CORE做的网站运行在docker实践
用VS2017 建立了 DotNet Core 2.2 的网站后,如何转移到 Docker 下运行? 下面分两种方式来实践: 1.直接手动命今行,将本机目录映射进Docker,运行网站.2.制作 Im ...
- 文章如何做伪原创 SEO大神教你几招做"原创"网站文章的心得
想要创作出好的文章并被百度所喜欢,就非常需要SEO的优化能力,以及要对文章进行塬创或伪塬创,那么,如何做伪塬创文章?以及如何做好塬创网站文章呢?对此,本文小编就为大家带来了几招做"塬创&qu ...
- ASP.NET MVC 做的网站项目
感谢博客园团队日夜为广大需要获取知识人们所做的奉献 博客园团队您们辛苦了 ASP.NET MVC 实现有论坛功能的网站(有iis发布网站 这是之前写的... www.lazyfitness.cn 经过 ...
随机推荐
- ContextLoaderListener和Spring MVC中的DispatcherServlet加载内容的区别
一:ContextLoaderListener加载内容 二:DispatcherServlt加载内容 ContextLoaderListener和DispatcherServlet都会在Web容器启动 ...
- AdapterView及其子类之四:基于ListView及SimpleAdapter实现列表
代码请见SimpleAdapterDemo.zip. 步骤如下: 1.创建主布局文件 <RelativeLayout xmlns:android="http://schemas.and ...
- 学php之翻译wordpress(2)
wp-load.php <?php /** * Bootstrap file for setting the ABSPATH constant * and loading the wp-conf ...
- php number_format()保留小数点后几位有效数的函数 千位分组来格式化数字(转)
PHP保留小数点后2位的函数number_format number_format(带小数点的书,小数点后保留的位数) number_format(8.3486,2); //取得小数点后2位有效数/ ...
- python成长之路第二篇(4)_collections系列
一.分别取出大于66的数字和小于66的数字 小练习:需求要求有一个列表列表中存着一组数字,要求将大于66的数字和小于66的数字分别取出来 aa = [11,22,33,44,55,66,77,88,9 ...
- UVA 1839 Alignment
还是最长上升子序列... 本题是求队列中任一士兵都能从左边或者右边看到队伍外: 即某一士兵左边为上升子序列,右边为下降子序列.求两个序列和,再用总数减去: #include <iostream& ...
- 中国大学MOOC-翁恺-C语言程序设计习题集
今年网易出了“中国大学MOOC”,于是选了浙大翁恺老师的“C语言程序设计”学习,近期打算把自己在该课程中的PAT习题解答做一个记录,等自己编程能力提高后再来看现在写的代码哪里还有写的不好,可以改进的地 ...
- ASP.NET Request.QueryString 出现乱码问题
前台: var txing = $("#txing").combobox("getValues"); .......... &tixing=" ...
- 获取ActiveX控件本身所在的路径 和 error PRJ0050
一. CString GetCurPath() { TCHAR exeFullPath[MAX_PATH]; CString strPath; ...
- EasyUI选项卡tab页面处理示例
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...