溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

網(wǎng)絡(luò)爬蟲

發(fā)布時(shí)間:2020-07-10 22:03:15 來(lái)源:網(wǎng)絡(luò) 閱讀:332 作者:zzlghzzq 欄目:編程語(yǔ)言

網(wǎng)絡(luò)爬蟲在信息檢索與處理中有很大的作用,是收集網(wǎng)絡(luò)信息的重要工具。

接下來(lái)就介紹一下爬蟲的簡(jiǎn)單實(shí)現(xiàn)。

爬蟲的工作流程如下

網(wǎng)絡(luò)爬蟲

爬蟲自指定的URL地址開始下載網(wǎng)絡(luò)資源,直到該地址和所有子地址的指定資源都下載完畢為止。http://mmm.qqq23.com

下面開始逐步分析爬蟲的實(shí)現(xiàn)。


1. 待下載集合與已下載集合

為了保存需要下載的URL,同時(shí)防止重復(fù)下載,我們需要分別用了兩個(gè)集合來(lái)存放將要下載的URL和已經(jīng)下載的URL。

因?yàn)樵诒4?/span>URL的同時(shí)需要保存與URL相關(guān)的一些其他信息,如深度,所以這里我采用了Dictionary來(lái)存放這些URL

具體類型是Dictionary<string,int>其中stringUrl字符串,int是該Url相對(duì)于基URL的深度。

每次開始時(shí)都檢查未下載的集合,如果已經(jīng)為空,說(shuō)明已經(jīng)下載完畢;如果還有URL,那么就取出第一個(gè)URL加入到已下載的集合中,并且下載這個(gè)URL的資源。


2. HTTP請(qǐng)求和響應(yīng)

C#已經(jīng)有封裝好的HTTP請(qǐng)求和響應(yīng)的類HttpWebRequestHttpWebResponse,所以實(shí)現(xiàn)起來(lái)方便不少。

為了提高下載的效率,http://www.qqq100.com我們可以用多個(gè)請(qǐng)求并發(fā)的方式同時(shí)下載多個(gè)URL的資源,一種簡(jiǎn)單的做法是采用異步請(qǐng)求的方法。

控制并發(fā)的數(shù)量可以用如下方法實(shí)現(xiàn)

網(wǎng)絡(luò)爬蟲

1privatevoid DispatchWork()

2{

3if (_stop) //判斷是否中止下載

4    {

5return;

6    }

7for (int i = 0; i < _reqCount; i++)

8    {

9if (!_reqsBusy[i]) //判斷此編號(hào)的工作實(shí)例是否空閑

10        {

11            RequestResource(i); //讓此工作實(shí)例請(qǐng)求資源

12        }

13    }

14 }

網(wǎng)絡(luò)爬蟲

由于沒有顯式開新線程,所以用一個(gè)工作實(shí)例來(lái)表示一個(gè)邏輯工作線程

1privatebool[] _reqsBusy = null; //每個(gè)元素代表一個(gè)工作實(shí)例是否正在工作

2privateint _reqCount = 4; //工作實(shí)例的數(shù)量

每次一個(gè)工作實(shí)例完成工作,相應(yīng)的_reqsBusy就設(shè)為false,并調(diào)用DispatchWork,那么DispatchWork就能給空閑的實(shí)例分配新任務(wù)了。


接下來(lái)是發(fā)送請(qǐng)求

網(wǎng)絡(luò)爬蟲

1privatevoidRequestResource(int index)

2{

3int depth;

4string url = "";

5try

6    {

7lock (_locker)

8        {

9if (_urlsUnload.Count <= 0)

10            {

11                _workingSignals.FinishWorking(index);

12return;

13            }

14            _reqsBusy[index] = true;

15            _workingSignals.StartWorking(index);

16            depth = _urlsUnload.First().Value;

17            url = _urlsUnload.First().Key;

18            _urlsLoaded.Add(url, depth);

19            _urlsUnload.Remove(url);

20        }

21

22         HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);

23        req.Method = _method; //請(qǐng)求方法

24        req.Accept = _accept; //接受的內(nèi)容

25        req.UserAgent = _userAgent; //用戶代理

26        RequestState rs = new RequestState(req, url, depth, index); //回調(diào)方法的參數(shù)

27var result =req.BeginGetResponse(new AsyncCallback(ReceivedResource), rs); //異步請(qǐng)求

28        ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, //注冊(cè)超時(shí)處理方法

29                 TimeoutCallback, rs, _maxTime,true);

30    }

31catch (WebException we)

32    {

33        MessageBox.Show("RequestResource" + we.Message + url + we.Status);

34    }

35 }

網(wǎng)絡(luò)爬蟲

26行的請(qǐng)求的額外信息在異步請(qǐng)求的回調(diào)方法作為參數(shù)傳入,之后還會(huì)提到。

27行開始異步請(qǐng)求,這里需要傳入一個(gè)回調(diào)方法作為響應(yīng)請(qǐng)求時(shí)的處理,同時(shí)傳入回調(diào)方法的參數(shù)。

28行給該異步請(qǐng)求注冊(cè)一個(gè)超時(shí)處理方法TimeoutCallback,最大等待時(shí)間是_maxTime,且只處理一次超時(shí),并傳入請(qǐng)求的額外信息作為回調(diào)方法的參數(shù)。


RequestState的定義是

網(wǎng)絡(luò)爬蟲

1class RequestState

2{

3privateconstint BUFFER_SIZE = 131072; //接收數(shù)據(jù)包的空間大小

4privatebyte[] _data = newbyte[BUFFER_SIZE]; //接收數(shù)據(jù)包的buffer

5private StringBuilder _sb = new StringBuilder(); //存放所有接收到的字符

6

7public HttpWebRequest Req { get; privateset; } //請(qǐng)求

8publicstring Url { get; privateset; } //請(qǐng)求的URL

9publicint Depth { get; privateset; } //此次請(qǐng)求的相對(duì)深度

10publicint Index { get; privateset; } //工作實(shí)例的編號(hào)

11public StreamResStream { get; set; } //接收數(shù)據(jù)流

12public StringBuilder Html

13    {

14get

15        {

16return _sb;

17        }

18    }

19

20publicbyte[] Data

21    {

22get

23        {

24return _data;

25        }

26    }

27

28publicint BufferSize

29    {

30get

31        {

32return BUFFER_SIZE;

33        }

34    }

35

36publicRequestState(HttpWebRequest req, string url, int depth, int index)

37    {

38        Req = req;

39        Url = url;

40        Depth = depth;

41        Index = index;

42    }

43 }

網(wǎng)絡(luò)爬蟲


TimeoutCallback的定義是

網(wǎng)絡(luò)爬蟲

1privatevoidTimeoutCallback(object state, bool timedOut)

2{

3if (timedOut) //判斷是否是超時(shí)

4    {

5         RequestState rs = state as RequestState;

6if (rs != null)

7        {

8             rs.Req.Abort(); //撤銷請(qǐng)求

9        }

10        _reqsBusy[rs.Index] = false; //重置工作狀態(tài)

11        DispatchWork(); //分配新任務(wù)

12    }

13 }

網(wǎng)絡(luò)爬蟲


接下來(lái)就是要處理請(qǐng)求的響應(yīng)了

網(wǎng)絡(luò)爬蟲

1privatevoidReceivedResource(IAsyncResult ar)

2{

3     RequestState rs = (RequestState)ar.AsyncState;//得到請(qǐng)求時(shí)傳入的參數(shù)

4     HttpWebRequest req = rs.Req;

5string url = rs.Url;

6try

7    {

8         HttpWebResponse res =(HttpWebResponse)req.EndGetResponse(ar); //獲取響應(yīng)

9if (_stop) //判斷是否中止下載

10        {

11            res.Close();

12            req.Abort();

13return;

14        }

15if (res != null &&res.StatusCode == HttpStatusCode.OK) //判斷是否成功獲取響應(yīng)

16        {

17            Stream resStream = res.GetResponseStream(); //得到資源流

18            rs.ResStream = resStream;

19var result =resStream.BeginRead(rs.Data, 0, rs.BufferSize, //異步請(qǐng)求讀取數(shù)據(jù)

20new AsyncCallback(ReceivedData), rs);

21        }

22else//響應(yīng)失敗

23        {

24            res.Close();

25            rs.Req.Abort();

26            _reqsBusy[rs.Index] = false; //重置工作狀態(tài)

27            DispatchWork(); //分配新任務(wù)

28        }

29    }

30catch (WebException we)

31    {

32        MessageBox.Show("ReceivedResource" + we.Message + url + we.Status);

33    }

34 }

網(wǎng)絡(luò)爬蟲

19行這里采用了異步的方法來(lái)讀數(shù)據(jù)流是因?yàn)槲覀冎安捎昧水惒降姆绞秸?qǐng)求,不然的話不能夠正常的接收數(shù)據(jù)。

該異步讀取的方式是按包來(lái)讀取的,所以一旦接收到一個(gè)包就會(huì)調(diào)用傳入的回調(diào)方法ReceivedData,然后在該方法中處理收到的數(shù)據(jù)。

該方法同時(shí)傳入了接收數(shù)據(jù)的空間rs.Data和空間的大小rs.BufferSize。


接下來(lái)是接收數(shù)據(jù)和處理

網(wǎng)絡(luò)爬蟲

1privatevoidReceivedData(IAsyncResult ar)

2{

3     RequestState rs =(RequestState)ar.AsyncState; //獲取參數(shù)

4     HttpWebRequest req = rs.Req;

5     Stream resStream = rs.ResStream;

6string url = rs.Url;

7int depth = rs.Depth;

8string html = null;

9int index = rs.Index;

10int read = 0;

11

12try

13    {

14        read = resStream.EndRead(ar); //獲得數(shù)據(jù)讀取結(jié)果

15if (_stop)//判斷是否中止下載

16        {

17            rs.ResStream.Close();

18            req.Abort();

19return;

20        }

21if (read > 0)

22        {

23            MemoryStream ms = new MemoryStream(rs.Data, 0, read); //利用獲得的數(shù)據(jù)創(chuàng)建內(nèi)存流

24            StreamReader reader = new StreamReader(ms, _encoding);

25string str = reader.ReadToEnd(); //讀取所有字符

26            rs.Html.Append(str); // 添加到之前的末尾

27var result =resStream.BeginRead(rs.Data, 0, rs.BufferSize, //再次異步請(qǐng)求讀取數(shù)據(jù)

28new AsyncCallback(ReceivedData), rs);

29return;

30        }

31        html = rs.Html.ToString();

32        SaveContents(html, url); //保存到本地

33string[] links = GetLinks(html); //獲取頁(yè)面中的鏈接

34        AddUrls(links, depth + 1); //過(guò)濾鏈接并添加到未下載集合中

35

36        _reqsBusy[index] = false; //重置工作狀態(tài)

37        DispatchWork(); //分配新任務(wù)

38    }

39catch (WebException we)

40    {

41        MessageBox.Show("ReceivedDataWeb " + we.Message + url + we.Status);

42    }

43 }

網(wǎng)絡(luò)爬蟲

14行獲得了讀取的數(shù)據(jù)大小read,如果read>0說(shuō)明數(shù)據(jù)可能還沒有讀完,所以在27行繼續(xù)請(qǐng)求讀下一個(gè)數(shù)據(jù)包;

如果read<=0說(shuō)明所有數(shù)據(jù)已經(jīng)接收完畢,這時(shí)rs.Html中存放了完整的HTML數(shù)據(jù),就可以進(jìn)行下一步的處理了。



附件:http://down.51cto.com/data/2362930
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI