[Android] - Jsoup实现网络爬虫,获取糗事百科数据

目录

前言

本文是在看了鸿洋大神微信公众号里的文章后,结合自己的实践写下的一些心得,所以会有很多相同的地方,请别介意仅当做笔记使用。Jsoup的简介什么的就不介绍了,百度一下就可以了。直接从配置和使用开始说起。
文章地址: Android实战——jsoup实现网络爬虫,糗事百科项目的起步

Jsoup配置

在Gradle中添加依赖:

  compile 'org.jsoup:jsoup:1.10.2'

并添加网络权限:

  <uses-permission android:name="android.permission.INTERNET" />

Jsoup使用

Jsoup提供两种网络请求,get和post,使用代码也及其简单,我们首先爬取糗事百科首页的HTML。注意:由于是网络请求操作,必须放在子线程中运行,否则4.4以上的版本会报错。(额,Post我没试成功,不晓得问题在哪,先说Get吧,0.0)

获取HTML网页

  • Get方式
    new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Document defaultDoc = Jsoup.connect("http://www.qiushibaike.com").get();
                    L.d("一、HTML內容 : " + defaultDoc .toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
  • Post方式
      new Thread() {
          @Override
          public void run() {
              super.run();
                  try {
                      Document doc = Jsoup.connect("http://www.qiushibaike.com/8hr/page/1/")
                                    .data("query", "Java")
                                    .userAgent("Mozilla")
                                    .cookie("auth", "token")
                                    .timeout(3000)
                                    .post();
                            L.d("一、HTML內容 : " + doc.toString());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();

这里对post的参数介绍一下

  • connect:设置连接的Url
  • data:设置post的键值对数据
  • userAgent:设置用户代理(请求头的东西,可以判断你是PC还是Mobile端)
  • cookie:设置缓存
  • timeout:设置请求超时
  • post:发送post请求

获取HTML元素

这里直接说Android端。

1.获取主页数据。
2.封装Bean对象。
3.使用ListView显示数据。
4.利用获取数据中的页码数据做下拉刷新和上拉加载更多操作。
....
剩下的还要做什么,请尽情的发挥你的脑洞吧。

1 获取主页数据

爬取数据利用Jsoup的属性选择器select来实现,具体详细介绍请点击Jsoup中文文档。,这里就不多做介绍。标签的id、class等都可以用作于选择。

这里写图片描述

通过上图我们可以发现,我们想要获取的数据都有一个共同的class(article)

    Document defaultDoc= Jsoup.connect("http://www.qiushibaike.com/").get();
  Elements articleEls = defaultDoc.select(".article");
        for (int j = 0; j < articleEls.size(); j++) {
            Elements authorEls = articleEls.get(j).select(".author");
            Elements authorAEls = authorEls.select("a");
            Elements authorImgEls = authorAEls.select("img");
            L.d("头像" + authorImgEls.attr("src"));
            Elements authorH2Els = authorAEls.select("h2");
            L.d("作者" + authorH2Els.text());
            Elements authorMaleAgeEls = articleEls.get(j).select(".manIcon");
            L.d("性别及年龄" + authorMaleAgeEls.text());
            Elements authorFemaleAgeEls = articleEls.get(j).select(".womenIcon");
            L.d("性别及年龄" + authorFemaleAgeEls.text());
            Elements contentEls = articleEls.get(j).select("a.contentHerf");
            L.d("内容" + contentEls.text());
            L.d("详情Url" + "http://www.qiushibaike.com" + contentEls.attr("href"));
            Elements thumbEls = articleEls.get(j).select(".thumb img[src$=jpg]");
            L.d("图片内容" + thumbEls.attr("src"));
        }

这里使用到的对象

  • Document:相当于一个Html文件
  • Elements:相当于一个标签的集合
  • Element:相当于一个标签

注意Elements与Element的toString()方法和text()方法

  • toString():打印出来的是标签的Html内容
  • text():打印出来的是标签对应的文本内容

css选择器

  • select():获取符合属性选择器要求的标签内容
  • getElementById:获取符合ID选择器要求的标签内容
  • getElementsByTag:获取符合Tag选择器要求的标签内容

小提示

 这里加一点小提示,是在我使用过程中遇到的问题,就当记个笔记。
    <div class="articleGender womenIcon">26</div>
    <div class="articleGender manIcon">47</div>

如果是这样一个标签有多个class的时候,当我们使用select(“.articleGender”)时是获取不到数据的,只有通过后一个class才能获取数据。(有可能出现这个问题的,只有我一个!0.0原谅我是个渣渣)

2 封装Bean对象

public class JokeBook implements Serializable {
    private static final String TAG = "JokeBook";
    private String author; // 作者
    private String avatar; // 作者头像
    private String age; // 作者年龄
    private int gender; // 作者性别 0:male(男)  1:female(女)
    private String content; // 文字内容
    private String thumb; // 图片内容,图片路径
    private String godCommentAuthor; // 神评论作者
    private String godCommentContent; // 神评论内容
    private String godCommentLikeNum; // 点赞数量
    private String funnyDegree; // 好笑度
    private String commentNum; // 评论次数
    private String detailUrl; // 详情Url
}

3 使用ListView显示数据

    public class JsoupActivity extends BaseAty implements CommonAdapter.RefreshMoreListener {

    @Bind(R.id.jokeLst)
    PullToRefreshRecyclerView jokeLst;
    private ArrayList<JokeBook> jokes;
    private CommonAdapter adapter;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
             switch (msg.what) {
                case 0:
                    adapter.notifyDataSetChanged();
                    break;
                default:
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_jsoup);
        ButterKnife.bind(this);
        setTitle("Jsoup");
        jokes = new ArrayList<>();
        RecyclerView mRecyclerView = jokeLst.getRefreshableView();
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//        mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
        adapter = new CommonAdapter<JokeBook>(this, R.layout.item_joke_book_layout, jokes) {
            @Override
            protected void convert(ViewHolder viewHolder, final JokeBook item, int position) {
                ...
            }
        };
        mRecyclerView.setAdapter(adapter);
        initData();
    }


    private void initData() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Document defaultDoc = Jsoup.connect(defaultUrl).get();
                    setData(defaultDoc);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private void setData(Document defaultDoc) {
        Elements articleEls = defaultDoc.select(".article");
        ArrayList<JokeBook> jokeBooksTemp = new ArrayList<>();
        for (int j = 0; j < articleEls.size(); j++) {
            JokeBook joke = new JokeBook();
            Elements authorEls = articleEls.get(j).select(".author");
            Elements authorAEls = authorEls.select("a");
            Elements authorImgEls = authorAEls.select("img");
            if (!authorImgEls.isEmpty()) {
                joke.setAvatar(authorImgEls.attr("src"));
            }
            ...
            jokes.add(joke);
            jokeBooksTemp.add(joke);
        }
        handler.sendEmptyMessage(0);
    }
}

4 利用获取数据中的页码数据做下拉刷新和上拉加载更多操作

由于下拉刷新上拉加载操作都是封装到适配器Adapter中,这里就不多做讲解了,有兴趣了解的朋友可以去看看鸿洋大神的为RecyclerView打造通用Adapter 让RecyclerView更加好用这篇文章,自己学习吧。另外详情页面和主页爬取数据的原理也差不多。

最后上一张成果图吧

这里写图片描述

这是首页,下图是详情

这里写图片描述

我觉得该说的都说了,这篇博客也算是写完了,写完后,我一看,我去,发现跟原文没啥区别啊,唉,算了,算了,只是当做笔记使用。另外上面有可能说的不是很详细,可以去看看原文,也可以去看看我的源码,我觉得有的时候看代码比别人给你讲更容易理解。

项目地址:https://github.com/Crazy-L/NdkJniDemo额,由于是我闲着没事儿瞎弄的项目,所以有点乱,具体位置见下图:

这里写图片描述

写的丑,见笑啦 0.0

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值