Android編寫簡單的網(wǎng)絡爬蟲
一、網(wǎng)絡爬蟲的基本知識
網(wǎng)絡爬蟲通過遍歷互聯(lián)網(wǎng)絡,把網(wǎng)絡中的相關網(wǎng)頁全部抓取過來,這體現(xiàn)了爬的概念。爬蟲如何遍歷網(wǎng)絡呢,互聯(lián)網(wǎng)可以看做是一張大圖,每個頁面看做其中的一個節(jié)點,頁面的連接看做是有向邊。圖的遍歷方式分為寬度遍歷和深度遍歷,但是深度遍歷可能會在深度上過深的遍歷或者陷入黑洞。所以,大多數(shù)爬蟲不采用這種形式。另一方面,爬蟲在按照寬度優(yōu)先遍歷的方式時候,會給待遍歷的網(wǎng)頁賦予一定優(yōu)先級,這種叫做帶偏好的遍歷。
實際的爬蟲是從一系列的種子鏈接開始。種子鏈接是起始節(jié)點,種子頁面的超鏈接指向的頁面是子節(jié)點(中間節(jié)點),對于非html文檔,如excel等,不能從中提取超鏈接,看做圖的終端節(jié)點。整個遍歷過程中維護一張visited表,記錄哪些節(jié)點(鏈接)已經(jīng)處理過了,跳過不作處理。
二、Android網(wǎng)絡爬蟲demo的簡單實現(xiàn)
看一下效果
抓的是這個網(wǎng)頁 然后寫了一個APP
是這樣的
把listview做成卡片式的了 然后配色弄的也很有紙質(zhì)感啊啊啊
反正自己還挺喜歡的
然后就看看是怎么弄的
看一下每個類都是干啥的 :
MainActivity:主界面的Activity
MainAdapter:listview的適配器
NetWorkClass:鏈接網(wǎng)絡 使用HttpClient發(fā)送請求、接收響應得到content 大概就是拿到了這個網(wǎng)頁的什么鬼東西
還有好多就是一個html的代碼 要解析這個
News:這個類里有兩個屬性 一個標題 一個是這個標題新聞點進去那個url;
NewsActivity:詳細新聞界面
PullListView:重寫了listview 具有下拉刷新和上拉加載功能
然后從oncreat()開始看:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); InitView(); MainThread mt = new MainThread(newsUrl); final Thread t = new Thread(mt, "MainThread"); t.start(); pullListView.setOnRefreshListener(new PullListView.OnRefreshListener() { @Override public void onRefresh() { isGetMore = false; MainThread mt = new MainThread(newsUrl); Thread t = new Thread(mt, "MainThread"); t.start(); } }); pullListView.setOnGetMoreListener(new PullListView.OnGetMoreListener() { @Override public void onGetMore() { isGetMore = true; if (num > 1) { MainThread mt = new MainThread(nextPage); Thread t = new Thread(mt, "MainThread"); t.start(); } } }); pullListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(MainActivity.this,NewsActivity.class); intent.putExtra("url",list.get(position-1).getUrl()); startActivity(intent); } }); }
這個里面主要就是先初始化了數(shù)據(jù)
然后new了一個線程 因為涉及到了網(wǎng)絡請求 所以我們要開線程去執(zhí)行 然后有一些listview
的下拉上拉點擊的綁定
所以主要內(nèi)容是在線程里面
再看線程之前 先看一下networkClass
package com.example.katherine_qj.news; import android.net.http.HttpResponseCache; import android.util.Log; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * Created by Katherine-qj on 2016/7/24. */ public class NetWorkClass { public String getDataByGet(String url){ Log.e("qwe","content"); String content =""; HttpClient httpClient = new DefaultHttpClient(); Log.e("qwe","content1"); /*使用HttpClient發(fā)送請求、接收響應很簡單,一般需要如下幾步即可。 1. 創(chuàng)建HttpClient對象。 2. 創(chuàng)建請求方法的實例,并指定請求URL。如果需要發(fā)送GET請求,創(chuàng)建HttpGet對象;如果需要發(fā)送POST請求,創(chuàng)建HttpPost對象。 3. 如果需要發(fā)送請求參數(shù),可調(diào)用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數(shù);對于HttpPost對象而言,也可調(diào)用setEntity(HttpEntity entity)方法來設置請求參數(shù)。 4. 調(diào)用HttpClient對象的execute(HttpUriRequest request)發(fā)送請求,該方法返回一個HttpResponse。 5. 調(diào)用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調(diào)用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內(nèi)容。程序可通過該對象獲取服務器的響應內(nèi)容。 6. 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接*/ HttpGet httpGet = new HttpGet(url); try { HttpResponse httpResponse = httpClient.execute(httpGet); // HttpReponse是服務器接收到瀏覽器的請求后,處理返回結(jié)果常用的一個類。 if(httpResponse.getStatusLine().getStatusCode() == 200) { /*getStatusLine() 獲得此響應的狀態(tài)行。狀態(tài)欄可以設置使用setstatusline方法之一,也可以在構(gòu)造函數(shù)初始化*/ InputStream is = httpResponse.getEntity().getContent(); /*getEntity() 獲取此響應的消息實體,如果有。實體是通過調(diào)用setentity提供。*/ BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null){ content += line; } } }catch (IOException e) { Log.e("http",e.toString()); } Log.e("sdf",content); return content; } }
注釋的很詳細了
大概就是 有一個getDataByGet
方法 然后接受一個url參數(shù) 經(jīng)過一系列請求得到網(wǎng)頁內(nèi)容 返回一個content
下來就是使用這個類的線程了
public class MainThread implements Runnable{ private String url; public MainThread(String url){ this.url = url; } @Override public void run() { NetWorkClass netWorkClass =new NetWorkClass();//new 了一個network類 content = netWorkClass.getDataByGet(url);//接收這個類返回的那個字符串也就是需要解析的那一串 Log.e("qwe",content); handler.sendEmptyMessage(111); } }
就是利用這個線程去得到content 然后通過handle傳遞到主線程去解析
private final android.os.Handler handler = new android.os.Handler(){ public void handleMessage(Message msg){ switch (msg.what){ case 111: analyseHTML(); if(isGetMore){ mainAdapter.notifyDataSetChanged(); /*每一次notifyDataSetChange()都會引起界面的重繪。當需要修改界面上View的相關屬性的時候, 最后先設置完成再調(diào)用notifyDataSetChange()來重繪界面。*/ }else { mainAdapter = new MainAdapter(MainActivity.this, list); pullListView.setAdapter(mainAdapter); } pullListView.refreshComplete(); pullListView.getMoreComplete(); break; } } };
analyseHTML();
發(fā)現(xiàn)其實解析的東西在這個方法里面 所以 這里才是解析網(wǎng)頁的東西?。?/p>
public void analyseHTML(){ if(content!=null){ int x= 0; Document document = Jsoup.parse(content); //解析HTML字符串 if (!isGetMore) { list.clear(); Element element = document.getElementById("fanye3942");//拿到fanye3942這個節(jié)點 String text = element.text();//得到這個節(jié)點的文本部分 System.out.print(text); num = Integer.parseInt(text.substring(text.lastIndexOf('/') + 1, text.length() - 1)); System.out.print(num); } Elements elements = document.getElementsByClass("c3942");//得到c3942這個節(jié)點中的所有子節(jié)點 while(true){ if(x==elements.size()){ System.out.print(elements.size()); break;//遍歷到最后就退出 } News news = new News(); news.setTitle(elements.get(x).attr("title"));//分別得到每一個子節(jié)點的需要的文本部分 news.setUrl(elements.get(x).attr("href")); // list.add(news); if (!isGetMore||x>10){ list.add(news); if(x>=25){ break; }//這個是因為我們學校的網(wǎng)頁有重復 } x++; } if (num>1){ nextPage = url+"/"+ --num+".htm";//因為有翻頁這里得到了下一頁的url在上拉的時候會開啟線程去請求數(shù)據(jù) System.out.println("qqqqqqqqqqq"+nextPage); } } }
Document 對象使我們可以從腳本中對 HTML 頁面中的所有元素進行訪問。
所以android基于Jsoup 把content搞成Document 對象
然后就可以慢慢分解去拿了 然后拿哪里的數(shù)據(jù)就要看需要了
我開始一直不知道那些fanye3942 和c3942是啥 后來才知道是需要的數(shù)據(jù)的節(jié)點id或者class
就像這樣
然后把每一次遍歷的數(shù)據(jù)都加到集合里面 給listview綁定集合就好了
大概主頁面就是這樣 然后跳轉(zhuǎn)頁面 就是因為news里面還放入了每一個新聞點擊之后的url所以傳到NewsActivity中再利用相同的思路去解析顯示就好了
package com.example.katherine_qj.news; import android.app.Activity; import android.os.Bundle; import android.os.Message; import android.util.Log; import android.widget.EditText; import android.widget.TextView; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; /** * Created by Katherine-qj on 2016/7/25. */ public class NewsActivity extends Activity { private TextView textTitle; private TextView textEdit; private TextView textDetail; private String title; private String edit; private String detail; private StringBuilder text; private String url; private Document document; private String content; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_news); InitView(); url=getIntent().getStringExtra("url"); Log.e("qqq",url); NewsThread newsThread = new NewsThread(url); final Thread t = new Thread(newsThread,"NewsActivity"); t.start(); } public void InitView(){ textTitle =(TextView)findViewById(R.id.textTitle); textEdit =(TextView)findViewById(R.id.textEdit); textDetail = (TextView)findViewById(R.id.textDetail); } private final android.os.Handler handler = new android.os.Handler(){ public void handleMessage(Message msg){ if(msg.what==1001){ document = Jsoup.parse(content); analyseHTML(document); textTitle.setText(title); textEdit.setText(edit); textDetail.setText(text); } } }; public class NewsThread implements Runnable{ String url; public NewsThread(String url){ this.url = url; } @Override public void run() { NetWorkClass netWorkClass = new NetWorkClass(); content = netWorkClass.getDataByGet(url); System.out.print("qqq"+content); handler.sendEmptyMessage(1001); } } public void analyseHTML(Document document){ if (document!=null){ Element element = document.getElementById("nrys"); Elements elements = element.getAllElements(); title = elements.get(1).text(); edit = elements.get(4).text(); Element mElement = document.getElementById("vsb_content_1031"); if(mElement != null) { Elements mElements = mElement.getAllElements(); text = new StringBuilder(); for (Element melement : mElements) { if(melement.className().equals("nrzwys") || melement.tagName().equals("strong")){ continue; } if(!melement.text().equals(" ") && !melement.text().equals(""));{ text.append(" ").append(melement.text()).append("\n"); } if (melement.className().equals("vsbcontent_end")) { break; } } } } } }
以上就是基于Android編寫簡單的網(wǎng)絡爬蟲的全部內(nèi)容,本文介紹的很詳細,希望給大家在Android開發(fā)的過程中有所幫助。
相關文章
Android通過json向MySQL中讀寫數(shù)據(jù)的方法詳解【讀取篇】
這篇文章主要介紹了Android通過json向MySQL中讀寫數(shù)據(jù)的方法,涉及Android解析json以及與php交互讀取mysql的方法,需要的朋友可以參考下2016-06-06Android實現(xiàn)彈出輸入法時頂部固定中間部分上移的效果
本文主要介紹了Android實現(xiàn)彈出輸入法時頂部固定中間部分上移的效果的方法。具有很好的參考價值,下面跟著小編一起來看下吧2017-03-03Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
因為Android官方從來沒有給出過沉浸式狀態(tài)欄這樣的命名,只有沉浸式模式(Immersive Mode)這種說法.下面通過本文給大家介紹Android沉浸式狀態(tài)欄微技巧,需要的朋友參考下2016-12-12源碼淺析Android中內(nèi)存泄漏檢測工具Leakcanary的使用
大名鼎鼎的 Leakcanary 想必作為 Android 開發(fā)都多多少少接觸過,新版本的 Leakcanary 也用 Kotlin 重寫了一遍,最近詳細查看了下源碼,就來和大家簡單分享一下2023-04-04Android 中 onSaveInstanceState()使用方法詳解
這篇文章主要介紹了Android 中 onSaveInstanceState()使用方法詳解的相關資料,希望通過本文大家能夠掌握這部分知識,需要的朋友可以參考下2017-09-09Android實現(xiàn)第三方授權登錄、分享以及獲取用戶資料
本篇文章介紹了Android實現(xiàn)第三方授權登錄、分享以及獲取用戶資料,詳細的介紹了第三方授權登錄的實現(xiàn)代碼,有需要的朋友可以了解一下。2016-11-11