• Jsoup抓取网页数据完成一个简易的Android新闻APP


    前言:作为一个篮球迷,每天必刷NBA新闻。用了那么多新闻APP,就想自己能不能也做个简易的新闻APP。于是便使用Jsoup抓取了虎扑NBA新闻的数据,完成了一个简易的新闻APP。虽然没什么技术含量,但还是写一下过程,满足一下菜鸟小小的成就感。

    关于Jsoup

    • jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。
    • Jsoup的中文文档:点击这里
    • Jsoup的jar包下载地址:点击这里

    分析与思路

    虎扑NBA新闻网页的新闻列表如图所示:
    我们所要做的便是获取图中每条新闻的新闻标题、新闻概要、新闻时间与来源以及新闻的链接地址这四个信息,而后用一个实体类News封装上述四个数据,再布局到ListView上。点击ListView的每个子项,便将该子项所显示的新闻的链接地址用一个WebView显示出来,便大功告成。效果如图:

    具体实现过程

    1.在AndroidStudio新建工程JsoupTest,而后将Jsoup的jar包【下载地址】拷到项目的libs下,然后右键Add As Library...

    2.修改activity_main.xml的布局,简单加入一个ListView,并设置一下Listview的每两个子项间的间隔的距离和颜色

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
       <ListView
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:id="@+id/news_lv"
           android:dividerHeight="7dp"
           android:divider="#dcdcdc">
       </ListView>  
            
    </LinearLayout>
    

    3.建立一个实体类News来封装我们等会要从网页中获取的新闻的标题、概要、时间与来源、链接地址这四个数据。很简单,用四个变量分别代表上述四个数据,并建立相应的构造方法以及四个变量的get与set方法。

    public class News {
        private String newsTitle;   //新闻标题
        private String newsUrl;     //新闻链接地址
        private String desc;        //新闻概要
        private String newsTime;    //新闻时间与来源
    
        public News(String newsTitle, String newsUrl, String desc, String newsTime) {
            this.newsTitle = newsTitle;
            this.newsUrl = newsUrl;
            this.desc = desc;
            this.newsTime = newsTime;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        public String getNewsTime() {
            return newsTime;
        }
    
        public void setNewsTime(String newsTime) {
            this.newsTime = newsTime;
        }
    
        public String getNewsTitle() {
            return newsTitle;
        }
    
        public void setNewsTitle(String newsTitle) {
            this.newsTitle = newsTitle;
        }
    
        public String getNewsUrl() {
            return newsUrl;
        }
    
        public void setNewsUrl(String newsUrl) {
            this.newsUrl = newsUrl;
        }
    }
    

    4.最重要的一步:利用Jsoup获取虎扑NBA新闻网页的数据,并封装到News实体类中。就简单概述下实现方法

    • 查看虎扑NBA新闻的网页源代码。谷歌浏览器,右键-查看网页源代码。其他浏览器应该都差不多。
      我们直接查看显示新闻列表的这部分代码,我们截图下两条新闻的代码来进行分析

      • 新闻1源代码:
        新闻1
      • 新闻2源代码:
        新闻2
    • 分析上图两条新闻的源代码,找到我们所打算要获取的新闻的标题、概要、时间与来源、链接地址这四个数据。我们可以发现在每条新闻的[div class="list-hd"][/div]这个标签下,存在新闻的链接地址与新闻的标题这两个数据。而我们所要做的便是利用Jsoup将这两个数据解析出来:
      这里写图片描述

      首先用Jsoup.connect(“所要抓取数据的网址”).get()获取到一个Document对象

    Document doc = Jsoup.connect("https://voice.hupu.com/nba/").get();
    

    用doc.select("div.list-hd")这个方法,返回一个Elements对象,封装了每条新闻[div class="list-hd"][/div]标签中的内容,数据格式为:[{新闻1},{新闻2},{新闻3},{新闻4}......]
    用for循环遍历titleLinks,对于每个Element对象:
    用e.select("a").text()便获取到[a][/a]间的内容,即新闻标题;
    用e.select("a").attr("href")便获取到每个标签中的href的值,即新闻的链接地址

    Elements titleLinks = doc.select("div.list-hd");
      for(Element e:titleLinks){	
      String title = e.select("a").text();
    String uri = e.select("a").attr("href");
     }
    
    • 同理对于另外两个数据:新闻简介和新闻时间及来源,我们分析源代码并进行解析
    • 新闻简介源代码
      这里写图片描述
      用如下代码获得新闻简介
        Elements descLinks = doc.select("div.list-content");
     for(Element e:titleLinks){	
    String desc = e.select("span").text();
    }
    
    - 新闻时间及来源源代码
    ![这里写图片描述](http://img.blog.csdn.net/20170122221602676?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU0NQcm9ncmFtbWVy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
    用如下代码获得新闻时间与来源
    
     ```   
     Elements timeLinks = doc.select("div.otherInfo");
    

    for(Element e:timeLinks){
    String time = e.select("span.other-left").select("a").text();
    }
    ```

    • 综上,我们就获取到了我们所需要的数据了,为此我们在MainActivity中声明一个getNews()方法,在方法中,我们开启一个线程来进行数据的获取。完整代码如下:
    	private void getNews(){
    	    new Thread(new Runnable() {
    	            @Override
    	            public void run() {
    	                try{
    	                
    	                    //获取虎扑新闻20页的数据,网址格式为:https://voice.hupu.com/nba/第几页   
    	                    for(int i = 1;i<=20;i++) {
    	                        Document doc = Jsoup.connect("https://voice.hupu.com/nba/" + Integer.toString(i)).get();
    	                        Elements titleLinks = doc.select("div.list-hd");    //解析来获取每条新闻的标题与链接地址
    	                        Elements descLinks = doc.select("div.list-content");//解析来获取每条新闻的简介
    	                        Elements timeLinks = doc.select("div.otherInfo");   //解析来获取每条新闻的时间与来源
    	                        
    	                        //for循环遍历获取到每条新闻的四个数据并封装到News实体类中
    	                        for(int j = 0;j < titleLinks.size();j++){
    	                            String title = titleLinks.get(j).select("a").text();
    	                            String uri = titleLinks.get(j).select("a").attr("href");
    	                            String desc = descLinks.get(j).select("span").text();
    	                            String time = timeLinks.get(j).select("span.other-left").select("a").text();
    	                            News news = new News(title,uri,desc,time);
    	                            newsList.add(news);
    	                        }
    	                    }
    	                    Message msg = new Message();
    	                    msg.what = 1;
    	                    handler.sendMessage(msg);
    	
    	                }catch (Exception e){
    	                    e.printStackTrace();
    	                }
    	            }
    	        }).start();	            }
    

    上一段代码相信通过备注就可以理解了,通过解析虎扑NBA新闻20页的每一页的网址,获取到每条新闻所需的数据,封装到实体类News中,再加入到MainActivity中声明的泛型为News的List即newsList中。等全部20页数据都获取到之后,用Message.what=1来作为数据加载完成的标志,并用Handler.sendMessage()将子线程的消息发送到主线程,通知主线程数据已加载完成,可以将数据加载到ListView上显示出来。

    5.剩下的就简单了 ,为ListView做相关的配置

    • news_item.xml:为ListView的item指定布局。放置三个TextView用来显示新闻标题,新闻概要,新闻时间及来源;
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            android:layout_marginRight="5dp"
            android:layout_marginBottom="5dp"
            android:id="@+id/news_title"
            android:gravity="center_horizontal"
            android:textColor="#000"
            android:textSize="18sp"/>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:id="@+id/news_desc"
            android:layout_marginBottom="6dp"
            android:layout_marginLeft="10dp"
            android:gravity="center_horizontal" />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/news_time"
            android:layout_marginBottom="6dp"
            android:textSize="10sp"
            android:textColor="#708090"
            android:gravity="center_horizontal"
            android:layout_marginRight="30dp" />
    
    </LinearLayout>
    
    
    • NewsAdapter.java:为ListView加载数据
    public class NewsAdapter extends BaseAdapter {
    
        private List<News> newsList;
        private View view;
        private Context mContext;
        private ViewHolder viewHolder;
    
        public NewsAdapter(Context mContext, List<News> newsList) {
            this.newsList = newsList;
            this.mContext= mContext;
        }
    
        @Override
        public int getCount() {
            return newsList.size();
        }
    
        @Override
        public Object getItem(int position) {
            return newsList.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                view = LayoutInflater.from(mContext).inflate(R.layout.news_item,
                        null);
                viewHolder = new ViewHolder();
                viewHolder.newsTitle = (TextView) view
                        .findViewById(R.id.news_title);
                viewHolder.newsDesc = (TextView)view.findViewById(R.id.news_desc);
                viewHolder.newsTime = (TextView)view.findViewById(R.id.news_time);
                view.setTag(viewHolder);
            } else {
                view = convertView;
                viewHolder = (ViewHolder) view.getTag();
            }
            viewHolder.newsTitle.setText(newsList.get(position).getNewsTitle());
            viewHolder.newsDesc.setText(newsList.get(position).getDesc());
            viewHolder.newsTime.setText("来自 : "+newsList.get(position).getNewsTime());
            return view;
        }
    
        class ViewHolder{
            TextView newsTitle;
            TextView newsDesc;
            TextView newsTime;
        }
    
    }
    

    6.建立NewsDisplayActivity用于显示新闻的具体内容。

    布局activity_display_news.xml:简单放入一个WebView,用于显示新闻

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <WebView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/web_view"></WebView>
    
    </LinearLayout>
    

    NewsDisplayActivity.java

    public class NewsDisplayActvivity extends AppCompatActivity {
    
        private String newsUrl;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_news_display);
            newsUrl = getIntent().getStringExtra("news_url");
            WebView webView = (WebView) findViewById(R.id.web_view);
            webView.getSettings().setJavaScriptEnabled(true);
            webView.setWebViewClient(new WebViewClient());
            webView.loadUrl(newsUrl);
    
    
        }
    }
    

    7.MainActivity用getNews()获取到数据后,在handlerMessage()方法里接受到子线程获取完数据的消息后,开始为ListView加载数据。并为ListView的item设置点击事件,当点击item时,将该item所对应的新闻的网址传递给NewsDisplayActivity,NewsDisplayActivity得到网址后用WebView加载该网址,便可看到新闻

    public class MainActivity extends AppCompatActivity {
    
        private List<News> newsList;
        private NewsAdapter adapter;
        private Handler handler;
        private ListView lv;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            newsList = new ArrayList<>();
            lv = (ListView) findViewById(R.id.news_lv);
            getNews();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    if(msg.what == 1){
                        adapter = new NewsAdapter(MainActivity.this,newsList);
                        lv.setAdapter(adapter);
                        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                            @Override
                            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                                News news = newsList.get(position);
                                Intent intent = new Intent(MainActivity.this,NewsDisplayActvivity.class);
                                intent.putExtra("news_url",news.getNewsUrl());
                                startActivity(intent);
                            }
                        });
                    }
                }
            };
            
        }
    
    
    
        private void getNews(){
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        //获取虎扑新闻20页的数据,网址格式为:https://voice.hupu.com/nba/第几页
                        for(int i = 1;i<=20;i++) {
    
                            Document doc = Jsoup.connect("https://voice.hupu.com/nba/" + Integer.toString(i)).get();
                            Elements titleLinks = doc.select("div.list-hd");    //解析来获取每条新闻的标题与链接地址
                            Elements descLinks = doc.select("div.list-content");//解析来获取每条新闻的简介
                            Elements timeLinks = doc.select("div.otherInfo");   //解析来获取每条新闻的时间与来源
                            for(int j = 0;j < titleLinks.size();j++){
                                String title = titleLinks.get(j).select("a").text();
                                String uri = titleLinks.get(j).select("a").attr("href");
                                String desc = descLinks.get(j).select("span").text();
                                String time = timeLinks.get(j).select("span.other-left").select("a").text();
                                News news = new News(title,uri,desc,time);
                                newsList.add(news);
                            }
                        }
                        Message msg = new Message();
                        msg.what = 1;
                        handler.sendMessage(msg);
    
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();
                }
    
        }
    

    最后记得在AndroidManifest中添加网络请求的权限


    2017.05.19更新(Github同步更新)

    由于虎扑新闻网页做了调整,去除了新闻简介,所以这部分内容就获取不到了,所以相应的要修改代码,只需要getNews()方法中做三个修改
    如下图,获取新闻简介的两句代码注释掉,News构造对象时,新闻简介参数传入null。这样改动较小,界面会丑点,自行调整便可。


    源码下载地址:请戳这里##

    总结:虽然只是一个简单的应用,但还是有所收获。有什么错误的地方,欢迎指出。菜鸟的点滴积累,希望能早日有所成长

  • 相关阅读:
    Vector(同步)和ArrayList(异步)异同
    集合框架(1)
    如何优化limit
    Mysql5大引擎之间的区别和优劣之分
    差分约束 poj 3169
    最大权森林 poj 3723
    次短路 poj 3255
    P1604 B进制星球 (高精度进制计算器)
    最小生成树入门 kruskal和堆优化的prim
    并查集入门 POJ 1182(带权并查集)
  • 原文地址:https://www.cnblogs.com/AaronPasi/p/6344123.html
Copyright © 2020-2023  润新知