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

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。

运行界面

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

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

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

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

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

时间: 2024-08-02 16:10:19

jsoup_解析任意网站,做任意网站客户端的相关文章

FineReport:任意时刻只允许在一个客户端登陆账号的插件

在使用FineReport报表系统中,处于账户安全考虑,有些企业希望同一账号在任意时刻智能在统一客户端登录.那么当A用户在C1客户端登陆后,该账号又在另外一个C2客户端登陆,服务器如何取判断呢? 开发原理 当服务器在得知A在C1登陆后,在cookie里面写入一个标识ID~将浏览器标记,然后以后的访问自然就能够根据匹配用户名和对应的标记来确定这个用户是不是在换浏览器登陆了,当匹配到用户异地登陆,就要把之前已经登陆的用户先登出,再登陆新请求的用户.当然关闭页面事件里要向后台先发送一个请求,后台要记得

利用XSS注入漏洞能对网站做什么

或许大家经常看一些高手测试XSS漏洞都是alert一个窗口来.便以为XSS即是如此,当自己alert出窗口来,便说自己发现了漏洞. 其实远没这么简单.你发现的只是程序员的一个小bug而已,远谈不上XSS.它们的关系如同系统漏洞与漏洞利用的关系,不可同日而语.你的系统是否出现过“****内存不能为read”?你是否知道它是溢出漏洞的一种表现呢?但想因此做出exploit远不是那么简单的事情,你能说自己发现了溢出漏洞了么? XSS高手们的那些XSS,大家都是可以拿出来做些什么的,这样的东西才能算得上

做个人网站的一些经验

煎蛋网.月光网站.卢松松网站都获得了成功. 我把网站划分为几个等级((违规网站.专业性质网站.商业网站.作弊.垃圾站不在此列,仅仅指个人网站) 起步阶段:0-1000ip(负收益) 初级阶段:1000-8000ip(收支持平) 中级阶段:8000-20000ip(有所收获) 高级阶段:20000--10万ip(收获颇丰) 终极阶段:10万以上(发了) 0-1000ip(月预期收益0),做到7.8百个ip.用一个方法就 很容易实现:在如在几个与你网站内容相关的论坛贴吧注册个账号,在自己的网站发布一

网站做SEO优化没效果 网站优化目的到底是什么

1 对于站长朋友来说,一直以来都有对自己的网站做SEO优化,然而为什么要做网站SEO优化呢?真正说得清楚的没有几人.就因为我们不知道为什么要做网站SEO优化,所以就听取"行业前辈"的经典之语:内容为王.外链为皇:原创.高质量的内容:用户体验-- 哪里找富婆包养的徽油气父贴吧 哪里找富婆包养的徽油气父贴吧 哪里找富婆包养的吩认同捶贴吧 哪里找富婆包养的吩认同捶贴吧 哪里找富婆包养的赵经不起仄贴吧 哪里找富婆包养的赵经不起仄贴吧 哪里找富婆包养 哪里找富婆包养 哪里找富婆包养 哪里找富婆包

uip UDP 服务器广播模式(客户端可以任意端口,并且主动向客户端发送数据)

目前移植uip,发现UDP 服务器模式下,必须指定本地端口以及客户端端口,否则只能讲客户端端口设置为0,才能接收任意端口的数据,但是无法发送数据,因为此时客户端端口设置为0了,我通过将原始数据包中的客户端端口保存下来,并且在发送的时候将客户端端口替换为指定的端口,发送完成之后又设置为0,这样就实现了向任意客户端端口发送数据. uip.c if(uip_udp_conn->lport != 0 && UDPBUF->destport == uip_udp_conn->lpo

12306网站做得验证码真的好烂

12306网站做得验证码真的好烂,这种产品经理.开发人员都是吃什么长大的啊!都不用脑子想问题吗?直接上图,免得说冤枉它了. 使用一点都不方便.优秀的互联网产品经理啊,救救12306吧! 12306网站做得验证码真的好烂,布布扣,bubuko.com

Linux系统下利用wget命令把整站下载做镜像网站

Linux系统下利用wget命令把整站下载做镜像网站 2011-05-28 18:13:01 | 1次阅读 | 评论:0 条 | itokit  在linux下完整的用wget命令整站采集网站做镜像 的命令是及无视网站根目录下的robots.txt限制.并且可以模拟一个正常浏览者的信息下载该网站. C/C++ Code复制内容到剪贴板 wget -m -e robots=off -U "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.

自己想要做一个网站应该怎么做

软件开发和网站开发完全是两个不同的,两个地方侧重的是不一样的.软件开发是属于手机端的开发,主要面向是安卓,ios开发,网站开发是电脑端的开发,语言分很多种:常用的有java.php..net,每个语言都有自己的特点和风格.软件开发主要分两种安卓开发和ios开发,安卓开发学习书籍书的话建议买 精通Android2 ,或者 Android2高级编程 这两本书,讲解的细致,但是一定要结合sdk来看.看完之后就可以自己动手做做项目了.对于java基础,我现在的感觉是 如果要深入学习android平台,j

别再问我做一个网站多少钱了!

「一辆车子多少钱?一个房子多少钱?」 这问题在工程师或设计师的眼里就如「一辆车子多少钱?一个房子多少钱?」,这个问题实在空泛到一个让人无法言语的境界,这也是我最常被问到的问题「做一个网站多少钱?」.说真的我大概能了解提问人的想法,或许只要说出个大概就是他们要的答案,例如说个多少X千和多少X万之间,或许我的职业病,我实在没有办法容忍这种不明确的答案. 为何我回答不了这个问题的原因大概是,最起码,给我一个参考的范例嘛,例如你问「这一台这样的车子,二手的,T牌2005年产,你估多少?」虽然提供的资讯不