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

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

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

分析与思路
虎扑NBA新闻网页的新闻列表如图所示:

SouthEast

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

SouthEast


具体实现过程

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();
}
- 新闻时间及来源源代码
![这里写图片描述](https://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。这样改动较小,界面会丑点,自行调整便可。

811723-20170519115327807-1486937439.png


源码下载地址:请戳这里

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

转载于:https://www.cnblogs.com/AaronPasi/p/6344123.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值