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。這樣改動較小,界面會丑點,自行調整便可。


源碼下載地址:請戳這里##

總結:雖然只是一個簡單的應用,但還是有所收獲。有什么錯誤的地方,歡迎指出。菜鳥的點滴積累,希望能早日有所成長


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM