前言:作為一個籃球迷,每天必刷NBA新聞。用了那么多新聞APP,就想自己能不能也做個簡易的新聞APP。於是便使用Jsoup抓取了虎撲NBA新聞的數據,完成了一個簡易的新聞APP。雖然沒什么技術含量,但還是寫一下過程,滿足一下菜鳥小小的成就感。
關於Jsoup
分析與思路
虎撲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源代碼:
- 新聞2源代碼:
- 新聞1源代碼:
-
分析上圖兩條新聞的源代碼,找到我們所打算要獲取的新聞的標題、概要、時間與來源、鏈接地址這四個數據。我們可以發現在每條新聞的[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();
}
- 新聞時間及來源源代碼

用如下代碼獲得新聞時間與來源
```
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。這樣改動較小,界面會丑點,自行調整便可。
源碼下載地址:請戳這里##
總結:雖然只是一個簡單的應用,但還是有所收獲。有什么錯誤的地方,歡迎指出。菜鳥的點滴積累,希望能早日有所成長