jsoup_解析任意网站,做任意网站客户端

news/2024/5/19 14:37:37/文章来源:https://blog.csdn.net/weixin_34233421/article/details/90197045

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标签组成的,要查看这些代码可以直接在网页的任何位置右键,在弹出的菜单中点击查看网页源代码。

网页的源码很多,有很多是我们不关心的,你的任务是迅速找到目标代码块,这篇文章的目的是要提取文章列表,所以我找到了文章列表相关的代码块(要熟练的找到需要一点点前端的知识):

QQ图片20141226204637.png

从上面的代码我可以肯定,文章列表位于

<div class="archive-list">

所包含的div中,而每一篇文章又是包含在

<div class="archive-list-item">

div中。因此我们先找出class="archive-list"的节点,确保后续的解析是文章相关的代码,然后再在这个节点之下解析每篇文章以及文章的数据项。

找出archive-list节点:

Jsoup连接网页:

1
Document doc = Jsoup.connect("http://jcodecraeer.com/plus/list.php?tid=4").timeout(10000).get();

找出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"); 
                url="http://www.jcodecraeer.com/" + url;
                        
                String title = titleElement.text();
                String summary = summaryElement.text();
                String imgsrc ="http://www.jcodecraeer.com/" + imgElement.attr("src");
                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"); 
 url="http://www.jcodecraeer.com/" + url;

获取文章的超链接,注意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 url = "http://www.jcodecraeer.com" + titleElement.attr("href"); 
                String title = titleElement.text();
                String summary = summaryElement.text();
                String imgsrc = "";
                if(imgElement != null){
                    imgsrc  ="http://www.jcodecraeer.com" + imgElement.attr("src");
                }
               
                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页,这种情况是可能的,但是这不是什么大问题。

  1. 这种几率很小  

  2. 即便这种情况存在,当加载10页的数据什么也没有,articleList = 0 小于10 ,进入第一个判断条件。也达到了目的。

 

图片的异步加载

图片的缩略图加载是需要异步的,你可以使用 universal image loader ,但在我们给出的demo中使用的是自己实现的一个ImageLoader。

 

运行界面

1419606579627661.png

 

在这篇文章中我们只是简单的完成了文章列表的展示,你还可以根据我所提供的方法实现更多的功能,除了Jsoup的使用之外,更重要的是分析一个网站,我已经采用这种方法把一个专门介绍日本爱情动作片的网站全部解析做成了客户端。事实证明这种方式是完全可行的。其实即便是网站改版,也可以很快写出新的解析规则,我们甚至可以将解析规则放到网络上,这样到网站改版的时候,就不需要变更客户端的代码使客户端照常正常运行。

 

Jsoup的应用远远不止于此,我们可以用Jsoup解析出整个网站,将网站的所有数据提炼出来从未到达采集网站的目的。

 

读完这篇文章你可能会产生一个疑问,为什么不直接把整个列表取出来放到webview中呢,其实这个问题就好比是使用html5还是原生app的问题。webview的效率和体验在短时间内难以超越原生应用。

 

代码下载:http://pan.baidu.com/s/1c0s5MiG 

 

转:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1226/2218.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_751768.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记1-架构演进】从0开始 独立完成企业级Java电商网站开发(服务端)

高大上的淘宝架构 我们以淘宝架构为例&#xff0c;了解下大型的电商项目的服务端的架构是怎样&#xff0c;如图所示 上面是一些安全体系系统&#xff0c;如数据安全体系、应用安全体系、前端安全体系等。中间是业务运营服务系统&#xff0c;如会员服务、商品服务、店铺服务、…

【笔记3-用户模块】从0开始 独立完成企业级Java电商网站开发(服务端)

数据表结构设计 关系设计 为什么不用外键&#xff1f;分库分表有外键会非常麻烦&#xff0c;清洗数据也很麻烦。数据库内置触发器也不适合采用。 查业务问题的后悔药——时间戳create_time 数据创建时间update_time 数据更新时间可以用于查询业务&#xff0c;主要要存储datet…

【笔记4-商品模块】从0开始 独立完成企业级Java电商网站开发(服务端)

分类管理模块 数据表结构设计 分类表 CREATE TABLE、mmall_ category ( id int(11) NOT NULL AUTO_ INCREMENT COMMENT 类别Id&#xff0c; parent_ id int(11) DEFAULT NULL COMMENT 父类 别id当id0时说明是根节点,一级类别 , name varchar(50) DEFAULT NULL COMMENT 类别…

【笔记5-购物车及地址模块】从0开始 独立完成企业级Java电商网站开发(服务端)

购物车模块 数据库表设计 购物车表 CREATE TABLE mmall_ cart ( id int(11) NOT NULL AUTO_ INCREMENT, user_ id int(11) NOT NULL, product_ id int(11) DEFAULT NULL COMMENT 商品id, quantity int(11) DEFAULT NULL COMMENT 数量, checked int(11) DEFAULT NULL COMMEN…

【笔记6-支付及订单模块】从0开始 独立完成企业级Java电商网站开发(服务端)

支付模块 实际开发工作中经常会遇见如下场景&#xff0c;一个支付模块&#xff0c;一个订单模块&#xff0c;有一定依赖&#xff0c;一个同事负责支付模块&#xff0c;另一个同事负责订单模块&#xff0c;但是开发支付模块的时候要依赖订单模块的相关类 &#xff0c;方法&…

【笔记8-Redis分布式锁】从0开始 独立完成企业级Java电商网站开发(服务端)

Redis分布式锁 Redis分布式锁命令 setnx当且仅当 key 不存在。若给定的 key 已经存在&#xff0c;则 setnx不做任何动作。setnx 是『set if not exists』(如果不存在&#xff0c;则 set)的简写,setnx 具有原子性。 getset先 get 旧值&#xff0c;后set 新值&#xff0c;并返回…

多网站项目的 CSS 架构

复杂的 CSS 架构&#xff0c;可不是你在科班里能学到的东西。 我在互联网行业的第四份工作&#xff0c;是在我国一家领先的媒体新闻公司中任职一名 CSS/HTML 专家&#xff0c;我的主要职责就是开发可重用的、可扩展的、用于多网站的 CSS 架构。 在本文中&#xff0c;我将与大家…

部署网站(虚拟主机,MVC站点,前后台,百度编辑器)出现的问题

开发工具&#xff1a;Vs2010 数据库&#xff1a;Sql2008 部署主机&#xff1a;万网虚拟主机&#xff0c;支持.NET 4.0 / 4.5 SQL2008 等.. 出现问题1&#xff1a;无法访问页面 出现问题2:后台无法访问 出现问题3&#xff1a;使用的编辑器是百度的UM&#xff0c;有使用到的地方…

电商详情页缓存架构(一)电商网站的商品详情页架构

小型电商网站的商品详情页的页面静态化架构以及其缺陷 小型电商网站&#xff0c;一般使用页面静态化的方案&#xff0c;提前将数据渲染到模板中。 问题&#xff1a;每次模板变更&#xff0c;模板对应的所有数据需要全部重新渲染 大型电商网站的异步多级缓存构建 nginx 数据本…

云南楚雄做网站找哪家?

2019独角兽企业重金招聘Python工程师标准>>> 云南楚雄做网站哪家强&#xff1f; 点击进入&#xff1a;http://www.anline.cn 转载于:https://my.oschina.net/jiankian/blog/614142

一分钟了解阿里云产品:利用Rsync服务SLB下多台centos服务器网站文件同步更新...

易淘帮使用了SLB负载均衡&#xff0c;为了保证SLB下两台服务器下面的网站文件同步&#xff0c;易淘帮采用了rsync服务进行同步&#xff0c;每三分钟进行同步一次。 简单介绍下reync&#xff0c;rsync—remote synchronize是类unix系统下的数据镜像备份工具&#xff0c;它的特性…

从全球最大同性交友网站抄了一份不一样的2048小游戏

大家好&#xff0c;我是“前端点线面”&#xff0c;一位新生代农民工&#xff0c;欢迎关注我获取最新前端知识和《前端百题斩》pdf版(包括JS基础篇、浏览器篇、网络篇共计50个章节&#xff0c;5万多字)&#xff0c;此外有喜欢划水的老铁们&#xff0c;可以加我进“前端划水群”…

10个大佬经常逛的小网站,各个爆款!!!

欢迎和号主【前端点线面】进群盘算法&#xff0c;此外本号干货满满&#xff1a;14个门类&#xff08;100篇原创&#xff09;内容&#xff08;又干又硬&#xff09;、《前端百题斩》pdf&#xff08;助力薪资double&#xff09;、20篇思维导图&#xff08;知识系统化、记忆简单化…

一步步构建大型网站架构

之前我简单向大家介绍了各个知名大型网站的架构&#xff0c;MySpace的五个里程碑、Flickr的架构、YouTube的架构、PlentyOfFish的架构、WikiPedia的架构。这几个都很典型&#xff0c;我们可以从中获取很多有关网站架构方面的知识&#xff0c;看了之后你会发现你原来的想法很可能…

【问底】徐汉彬:大规模网站架构的缓存机制和几何分形学

【导读】徐汉彬曾在阿里巴巴和腾讯从事4年多的技术研发工作&#xff0c;负责过日请求量过亿的Web系统升级与重构&#xff0c;目前在小满科技创业&#xff0c;从事SaaS服务技术建设。 在过去的工作中&#xff0c;徐汉彬从事各类缓存建设和优化&#xff0c;遇到问题无数&#xf…

npm的gh-pages结合github发布repository网站

如果你在使用github而且创建了一个新的仓库&#xff0c;你会发现&#xff0c;在仓库设置里面有结合github pages发布网站的设置&#xff0c;如下所示&#xff1a; 用过github pages都知道它可以做为静态网站来处理比如html和markdown的文件产生预览效果&#xff0c;我之前做过类…

大型网站技术架构(二)架构模式

2019独角兽企业重金招聘Python工程师标准>>> 每一个模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的核心。这样&#xff0c;你就能一次又一次地使用该方案而不必做重复工作。 所谓网站架构模式即为了解决大型网站面临的高并发访问、海量数据、高可靠…

大型网站技术架构(四)网站的高性能架构

2019独角兽企业重金招聘Python工程师标准>>> 网站性能是客观的指标&#xff0c;可以具体体现到响应时间、吞吐量、并发数、性能计数器等技术指标。 1、性能测试指标 1.1 响应时间 指应用执行一个操作需要的时间&#xff0c;指从发出请求到最后收到响应数据所需要的时…

网站安全认证系统的设计变迁

网站在从小到大的发展历程中&#xff0c;安全认证系统是如何变迁的&#xff1f; 下面我们从其发展的几个阶段来分下&#xff1a; 阶段1&#xff1a; 起步&#xff0c;注册用户很少&#xff0c;两台服务器&#xff0c;一台应用服务器&#xff0c;一台数据库服务器。 用户登陆后在…

如何通过IP地址来访问网站

今天我们写极少量的代码&#xff0c;来实现用IP地址来访问一个网站。步骤如下&#xff1a; 打开命令提示符&#xff0c;输入以下代码&#xff1a; &#xff08;这里用CSDN官网https://www.csdn.net/为例&#xff09; 输入代码ping csdn.net /n 5 复制上面正在 Ping csdn.net […