知乎爬蟲之5:爬蟲優化

本文由博主原創,轉載請註明出處html

 

知乎爬蟲系列文章:mysql

  1. 知乎爬蟲之1:開篇序言
  2. 知乎爬蟲之2:爬蟲流程設計
  3. 知乎爬蟲之3:請求分析
  4. 知乎爬蟲之4:抓取頁面數據
  5. 知乎爬蟲之5:爬蟲優化

github爬蟲項目(源碼)地址(已完成,關注和star在哪~):jquery

附贈以前爬取的數據一份(mysql): 連接: 只下載不點贊,不star,差評差評~藍瘦香菇)git

1. 使用多線程加速

什麼,爬蟲爬起來數據太慢了,怎麼辦?你那固然是開啓多線程了。那麼多線程是什麼我就不介紹了。若是還不知道的,請左移多線程百度百科
恩,知道了多線程,可是多線程若是本身控制的話,會很很差控制,因此我們還須要兩個線程池,一個負責拿到我的信息,一個負責獲取用戶的token。接下來讓我們以前寫的ParserBase類實現Runnable,而後在ParserFollower裏和ParserUserInfo裏分別實現run方法,其實也很簡單了,就是把以前的爬去邏輯,丟到run方法裏。而後我們就開啓了多線程之旅。
可是在存取數據的時候會遇到不少問題,好比數據會重複,這就出現了髒數據,那麼我們就是適當的加鎖。來保證數據的乾淨。github

2. 使用隊列減小數據庫訪問

若是說數據重複的問題解決的,那麼我們還有一個大問題,由於兩次爬到的多是同一我的,可是一份在數據庫裏,一份在正在跑的內存裏,怎麼辦,固然是要鏈接下數據庫,而後判斷是數據是否已經在數據庫中存在了,那麼在多線程且獲取速度很快的狀況下,那麼將會頻繁訪問數據庫,形成速度緩慢,數據庫連接數過多,cpu使用率過大,那我們怎麼除處理這個問題呢?
首先你們都必定知道,在如今內存很是大的今天,我們徹底能夠把一部分數據直接緩存下載,並且程序訪問內存的代價要比訪問數據庫的代價要小的太多。
所以,我們能夠用一個list把我們已經有的token存下來,而後每次去這個list裏去作驗證,這樣,就減小了數據庫的訪問了頻率。web

3. 實現LRU提升緩存命中率

如上所說,用一個list的確是起到了減小數據庫訪問的目的,可是效果好像不如想象那麼好,由於數據庫裏100k的數據,我們不可能把全部數據都緩存下載,那麼怎麼才能在優化下呢?
這時候就要合理的處理這個list了,本身實現一個LRU緩存,來調高命中率。
那麼lru是什麼?y一句話LRU是Least Recently Used 近期最少使用算法,也叫淘汰算法。
具體實現和思想可見緩存淘汰算法–LRU算法
按照這個思想,其實還有個優化,那就是根據碰撞次數和上次碰撞時間進行移除。我們只是實現簡單的,具體實如今MatrixSeven/ZhihuSpider算法


 

 

========關於獲取頁面數據=======

我們上一篇分析了知乎的登錄請求和如何拿到粉絲/關注的請求,那麼我們這篇就來研究下如何拿利用Jsoup到我們想要的數據。
那麼我們說下,首先請求關注者和粉絲者是pcweb版本的,可是獲取頁面的是手機頁面的。
好,正題:sql

1.什麼是Jsoup

jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套很是省力的API,可經過DOM,CSS以及相似於jQuery的操做方法來取出和操做數據。chrome

2. HttpClient請求模擬

HttpClient 是 Apache Jakarta Common 下的子項目,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。數據庫

3.走起頁面

首先模擬手機瀏覽器的UA。就是讓我們打開的頁面返回的是移動端的頁面效果,那麼最應該怎麼怎麼作呢?其實服務器斷定你是ie仍是chrome仍是firefox是根據請求頭裏面的UA實現的,所以我們要找一個手機瀏覽器的UA。。
我們能夠某度一下或者直接在瀏覽器裏面直接f12,模擬移動端,而後看請求參數:

User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36

 

妥妥的沒問題:

那我們如何將這句體現到程序裏面呢?
簡單,在我們拿到get對象後直接設置:

httpGet.setHeader("User-Agent", "Mozilla/5.0 
        (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 
        (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36");

 

就ok了,而後我們就能夠用jsoup來拿我們想要的元素了,jsoup的語法和jq一模一樣。
我們直接對着頁面,右擊我們想要的元素,選擇審查元素,而後用jq的選擇器選出來就行了。
能夠參考jQuery 選擇器

4.拿到關注者

直接get我們以前分析的請求地址

https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&
include=data%5B%2A%5D.employments%2Ccover_url%2Callow_message%2Canswer_coun
t%2Carticles_count%2Cfavorite_count%2Cfollower_count%2Cgender%2Cis_followe
d%2Cmessage_thread_token%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer
%29%5D.topics&limit=10&offset=30

 

不過要記得替換用戶名字和在請求頭裏加入cookie的最後一段zc_0
而後請求數據返回的是json

{
  "paging": {
    "is_end": false,
    "next": "https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&include=data%5B%2A%5D.answer_count%2Carticles_count%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&limit=10&offset=20",
    "previous": "https://www.zhihu.com/api/v4/members/Sweets07/followers?per_page=10&include=data%5B%2A%5D.answer_count%2Carticles_count%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&limit=10&offset=0",
    "is_start": false,
    "totals": 398
  },
  "data": [
    {
      "is_followed": true,
      "avatar_url_template": "https://pic1.zhimg.com/da8e974dc_{size}.jpg",
      "name": "陳曉峯",
      "url": "",
      "type": "people",
      "user_type": "people",
      "answer_count": 0,
      "url_token": "chen-xiao-feng-84",
      "headline": "阿里巴巴,分佈式數據庫,",
      "avatar_url": "https://pic1.zhimg.com/da8e974dc_is.jpg",
      "is_following": false,
      "is_org": false,
      "follower_count": 14,
      "badge": [],
      "id": "ff02ea0544901a9ddfcb7ba60c73b673",
      "articles_count": 0
    }
  ]
}

 

這個數據包括了下次請求地址,上次請求地址,時候是開始,時候是結束,共有多少粉絲,關注人基本信息,
所以我們能夠在一個while裏來得到全部粉絲數:
流程:

  1. 第一次獲取數據
  2. 獲取is_end字段
  3. 判斷is_end時候爲true
  4. 根據is_end判斷是否繼續循環
  5. 若是循環,更新is_end,更新下次請求鏈接

一套下來,就能拿到一個用戶的全部粉絲了。

 

 

 

吾愛Java(QQ羣):170936712

相關文章
相關標籤/搜索