Android編寫簡(jiǎn)單的網(wǎng)絡(luò)爬蟲
一、網(wǎng)絡(luò)爬蟲的基本知識(shí)
網(wǎng)絡(luò)爬蟲通過(guò)遍歷互聯(lián)網(wǎng)絡(luò),把網(wǎng)絡(luò)中的相關(guān)網(wǎng)頁(yè)全部抓取過(guò)來(lái),這體現(xiàn)了爬的概念。爬蟲如何遍歷網(wǎng)絡(luò)呢,互聯(lián)網(wǎng)可以看做是一張大圖,每個(gè)頁(yè)面看做其中的一個(gè)節(jié)點(diǎn),頁(yè)面的連接看做是有向邊。圖的遍歷方式分為寬度遍歷和深度遍歷,但是深度遍歷可能會(huì)在深度上過(guò)深的遍歷或者陷入黑洞。所以,大多數(shù)爬蟲不采用這種形式。另一方面,爬蟲在按照寬度優(yōu)先遍歷的方式時(shí)候,會(huì)給待遍歷的網(wǎng)頁(yè)賦予一定優(yōu)先級(jí),這種叫做帶偏好的遍歷。
實(shí)際的爬蟲是從一系列的種子鏈接開(kāi)始。種子鏈接是起始節(jié)點(diǎn),種子頁(yè)面的超鏈接指向的頁(yè)面是子節(jié)點(diǎn)(中間節(jié)點(diǎn)),對(duì)于非html文檔,如excel等,不能從中提取超鏈接,看做圖的終端節(jié)點(diǎn)。整個(gè)遍歷過(guò)程中維護(hù)一張visited表,記錄哪些節(jié)點(diǎn)(鏈接)已經(jīng)處理過(guò)了,跳過(guò)不作處理。
二、Android網(wǎng)絡(luò)爬蟲demo的簡(jiǎn)單實(shí)現(xiàn)
看一下效果

抓的是這個(gè)網(wǎng)頁(yè) 然后寫了一個(gè)APP
是這樣的

把listview做成卡片式的了 然后配色弄的也很有紙質(zhì)感啊啊啊
反正自己還挺喜歡的
然后就看看是怎么弄的

看一下每個(gè)類都是干啥的 :
MainActivity:主界面的Activity
MainAdapter:listview的適配器
NetWorkClass:鏈接網(wǎng)絡(luò) 使用HttpClient發(fā)送請(qǐng)求、接收響應(yīng)得到content 大概就是拿到了這個(gè)網(wǎng)頁(yè)的什么鬼東西
還有好多就是一個(gè)html的代碼 要解析這個(gè)
News:這個(gè)類里有兩個(gè)屬性 一個(gè)標(biāo)題 一個(gè)是這個(gè)標(biāo)題新聞點(diǎn)進(jìn)去那個(gè)url;
NewsActivity:詳細(xì)新聞界面
PullListView:重寫了listview 具有下拉刷新和上拉加載功能
然后從oncreat()開(kāi)始看:
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);
}
});
}
這個(gè)里面主要就是先初始化了數(shù)據(jù)
然后new了一個(gè)線程 因?yàn)樯婕暗搅司W(wǎng)絡(luò)請(qǐng)求 所以我們要開(kāi)線程去執(zhí)行 然后有一些listview的下拉上拉點(diǎn)擊的綁定
所以主要內(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ā)送請(qǐng)求、接收響應(yīng)很簡(jiǎn)單,一般需要如下幾步即可。
1. 創(chuàng)建HttpClient對(duì)象。
2. 創(chuàng)建請(qǐng)求方法的實(shí)例,并指定請(qǐng)求URL。如果需要發(fā)送GET請(qǐng)求,創(chuàng)建HttpGet對(duì)象;如果需要發(fā)送POST請(qǐng)求,創(chuàng)建HttpPost對(duì)象。
3. 如果需要發(fā)送請(qǐng)求參數(shù),可調(diào)用HttpGet、HttpPost共同的setParams(HetpParams params)方法來(lái)添加請(qǐng)求參數(shù);對(duì)于HttpPost對(duì)象而言,也可調(diào)用setEntity(HttpEntity entity)方法來(lái)設(shè)置請(qǐng)求參數(shù)。
4. 調(diào)用HttpClient對(duì)象的execute(HttpUriRequest request)發(fā)送請(qǐng)求,該方法返回一個(gè)HttpResponse。
5. 調(diào)用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務(wù)器的響應(yīng)頭;調(diào)用HttpResponse的getEntity()方法可獲取HttpEntity對(duì)象,該對(duì)象包裝了服務(wù)器的響應(yīng)內(nèi)容。程序可通過(guò)該對(duì)象獲取服務(wù)器的響應(yīng)內(nèi)容。
6. 釋放連接。無(wú)論執(zhí)行方法是否成功,都必須釋放連接*/
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
// HttpReponse是服務(wù)器接收到瀏覽器的請(qǐng)求后,處理返回結(jié)果常用的一個(gè)類。
if(httpResponse.getStatusLine().getStatusCode() == 200) {
/*getStatusLine()
獲得此響應(yīng)的狀態(tài)行。狀態(tài)欄可以設(shè)置使用setstatusline方法之一,也可以在構(gòu)造函數(shù)初始化*/
InputStream is = httpResponse.getEntity().getContent();
/*getEntity()
獲取此響應(yīng)的消息實(shí)體,如果有。實(shí)體是通過(guò)調(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;
}
}
注釋的很詳細(xì)了
大概就是 有一個(gè)getDataByGet方法 然后接受一個(gè)url參數(shù) 經(jīng)過(guò)一系列請(qǐng)求得到網(wǎng)頁(yè)內(nèi)容 返回一個(gè)content
下來(lái)就是使用這個(gè)類的線程了
public class MainThread implements Runnable{
private String url;
public MainThread(String url){
this.url = url;
}
@Override
public void run() {
NetWorkClass netWorkClass =new NetWorkClass();//new 了一個(gè)network類
content = netWorkClass.getDataByGet(url);//接收這個(gè)類返回的那個(gè)字符串也就是需要解析的那一串
Log.e("qwe",content);
handler.sendEmptyMessage(111);
}
}
就是利用這個(gè)線程去得到content 然后通過(guò)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()都會(huì)引起界面的重繪。當(dāng)需要修改界面上View的相關(guān)屬性的時(shí)候,
最后先設(shè)置完成再調(diào)用notifyDataSetChange()來(lái)重繪界面。*/
}else {
mainAdapter = new MainAdapter(MainActivity.this, list);
pullListView.setAdapter(mainAdapter);
}
pullListView.refreshComplete();
pullListView.getMoreComplete();
break;
}
}
};
analyseHTML();
發(fā)現(xiàn)其實(shí)解析的東西在這個(gè)方法里面 所以 這里才是解析網(wǎng)頁(yè)的東西?。?/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這個(gè)節(jié)點(diǎn)
String text = element.text();//得到這個(gè)節(jié)點(diǎn)的文本部分
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這個(gè)節(jié)點(diǎn)中的所有子節(jié)點(diǎn)
while(true){
if(x==elements.size()){
System.out.print(elements.size());
break;//遍歷到最后就退出
}
News news = new News();
news.setTitle(elements.get(x).attr("title"));//分別得到每一個(gè)子節(jié)點(diǎn)的需要的文本部分
news.setUrl(elements.get(x).attr("href"));
// list.add(news);
if (!isGetMore||x>10){
list.add(news);
if(x>=25){
break;
}//這個(gè)是因?yàn)槲覀儗W(xué)校的網(wǎng)頁(yè)有重復(fù)
}
x++;
}
if (num>1){
nextPage = url+"/"+ --num+".htm";//因?yàn)橛蟹?yè)這里得到了下一頁(yè)的url在上拉的時(shí)候會(huì)開(kāi)啟線程去請(qǐng)求數(shù)據(jù)
System.out.println("qqqqqqqqqqq"+nextPage);
}
}
}
Document 對(duì)象使我們可以從腳本中對(duì) HTML 頁(yè)面中的所有元素進(jìn)行訪問(wèn)。
所以android基于Jsoup 把content搞成Document 對(duì)象
然后就可以慢慢分解去拿了 然后拿哪里的數(shù)據(jù)就要看需要了
我開(kāi)始一直不知道那些fanye3942 和c3942是啥 后來(lái)才知道是需要的數(shù)據(jù)的節(jié)點(diǎn)id或者class

就像這樣
然后把每一次遍歷的數(shù)據(jù)都加到集合里面 給listview綁定集合就好了
大概主頁(yè)面就是這樣 然后跳轉(zhuǎn)頁(yè)面 就是因?yàn)?strong>news里面還放入了每一個(gè)新聞點(diǎn)擊之后的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編寫簡(jiǎn)單的網(wǎng)絡(luò)爬蟲的全部?jī)?nèi)容,本文介紹的很詳細(xì),希望給大家在Android開(kāi)發(fā)的過(guò)程中有所幫助。
相關(guān)文章
ExpandableListView實(shí)現(xiàn)簡(jiǎn)單二級(jí)列表
這篇文章主要為大家詳細(xì)介紹了ExpandableListView實(shí)現(xiàn)簡(jiǎn)單二級(jí)列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
Android編程設(shè)計(jì)模式之迭代器模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之迭代器模式,結(jié)合實(shí)例形式詳細(xì)分析了Android迭代器模式的概念、原理、應(yīng)用場(chǎng)景、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-12-12
android BottomSheetDialog新控件解析實(shí)現(xiàn)知乎評(píng)論列表效果(實(shí)例代碼)
BottomSheetDialog是一個(gè)自定義的從底部滑入的對(duì)話框,這篇文章主要介紹了android BottomSheetDialog新控件解析實(shí)現(xiàn)知乎評(píng)論列表效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Android實(shí)現(xiàn)app分享文件到微信功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)app分享文件到微信功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
Android自定義view實(shí)現(xiàn)TextView方形輸入框
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)TextView方形輸入框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Android中使用自定義ViewGroup的總結(jié)
本篇文章主要介紹了Android中使用自定義ViewGroup的總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
Android判斷當(dāng)前應(yīng)用程序處于前臺(tái)還是后臺(tái)的兩種方法
這篇文章主要介紹了Android判斷當(dāng)前應(yīng)用程序處于前臺(tái)還是后臺(tái)的兩種方法,有需要的朋友可以參考一下2013-12-12
Android基于騰訊云實(shí)時(shí)音視頻仿微信視頻通話最小化懸浮
這篇文章主要為大家詳細(xì)介紹了Android基于騰訊云實(shí)時(shí)音視頻實(shí)現(xiàn)類似微信視頻通話最小化懸浮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android編程實(shí)現(xiàn)帶有圖標(biāo)的ListView并帶有長(zhǎng)按菜單效果示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)帶有圖標(biāo)的ListView并帶有長(zhǎng)按菜單效果,結(jié)合實(shí)例形式分析了Android帶圖標(biāo)的ListView及菜單功能相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06
Android多點(diǎn)觸控實(shí)現(xiàn)對(duì)圖片放大縮小平移,慣性滑動(dòng)等功能
這篇文章主要介紹了Android多點(diǎn)觸控實(shí)現(xiàn)對(duì)圖片放大縮小平移,慣性滑動(dòng)等功能的相關(guān)資料,需要的朋友可以參考下2016-02-02

