從零實現一個高性能網絡爬蟲(一)網絡請求分析及代碼實現


摘要

從零實現一個高性能網絡爬蟲系列教程第一篇,后續會有關於url去重、如何反爬蟲、如何提高抓取效率、分布式爬蟲系列文章。
以我寫的一個知乎爬蟲為Demo講解,github地址 (https://github.com/wycm/zhihu-crawler) ,有興趣的朋友可以star下。
網絡請求的分析是寫網絡爬蟲非常關鍵且重要的一個步驟。這篇文章以知乎網站為例,從網絡請求分析到代碼(java)實現。

目的

獲取某個知乎用戶的所有關注用戶的個人資料

請求分析

  • 就目前的大部分網頁來說,網頁上能看到的數據大多都是直接在網站后台生成好數據(有的網頁是在網站前端通過js代碼處理后顯示,如數據混淆、加密等)直接在前台顯示。
  • 雖然很多網站采用了ajax異步加載,但是歸根結底它還是一個http請求。只要能夠分析出對應數據的請求來源,那么就很容易的拿到你想要的數據了。以下步驟講解如何分析http請求。
  1. 以我的知乎賬戶為例,獲取我的所有關注用戶資料。首先打開我的關注列表,可以看到主面板就是我的關注用戶列表,
    我一共關注233個用戶,現在目的是就是要獲取這233個用戶的個人資料信息。打開F12->NetWork,勾選上Preserve log和Disable cache(如下圖)。
  2. 下拉滾動條,點擊下一頁獲取對應請求(在翻頁的過程會有很多無關的請求),待頁面加載完成后,在請求列表中右鍵->Save as HAR with content,這個文件是把當前請求(request)列表保存為
    json格式文本,保存后使用chrome打開這個文件,搜索(Ctrl+F)頁面出現的關鍵字,要注意這里中文采用了unicode編碼,我這里直接搜索5032(李博傑的關注者數,見下圖)。這一步驟的目的是獲取我們想要數據(關注用戶的個人資料)的請求來源。
  3. 由步驟2搜索得出,關注用戶的資料數據來自以下請求(如下圖),url解碼后為https://www.zhihu.com/api/v4/members/wo-yan-chen-mo/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset=20&limit=20(url1),從這里可以看出關注列表的數據並不是從(url2)同步加載而來的,而是直接通過ajax異步請求url1來獲得關注用戶數據,然后通過js代碼填充數據。這里要注意用紅色矩形圈住的authorization request header,在代碼實現的時候必須加上這個header。這個數據並不是動態改變的,通過步驟2的方式可以發現它是來自一個js文件。該步驟注意的是,我寫該文章的時候是2017-04-27,隨着時間推移,知乎可能會更新相關api接口的url,也就是說通過步驟2得出的url有可能並不是我上面的url1,但是具體分析的方法還是通用的。
  4. 多測試幾次可以得出以上url1的參數含義如下
    參數名
    類型
    必填
    說明
     
    include
     
    String
     
     
    data[*]answer_count,articles_count
     
    需要返回的字段(這個值可以改根據需要增加一些字段)
     
    offset
     
    int
     
     
    0
     
    偏移量(通過調整這個值可以獲取到一個用戶的所有關注用戶資料)
     
    limit
     
    int
     
     
    20
     
    返回用戶數(最大20,超過20無效)
  5. 關於如何測試請求,我常用的以下三種方式
    • 原生chrome瀏覽器。可以做一些簡單的GET請求測試,這種方式有很大的局限性,不能編輯http header。如果直接(未登錄知乎)通過瀏覽器訪問url1,會得到401的response code。因為它沒有帶上authorization request header。所以這種方式能測試一些簡單且沒有特殊request header的GET請求。
    • chrome插件Postman。一個很強大的http請求測試工具,可以直接編輯request header(包括cookies)。如果可以翻牆的話,強烈推薦。GET、POST、PUT等都是支持的,幾乎可以發送任意類型的http請求,測試的url1如下圖。通過修改它參數的值,來看服務器響應數據的變化來確定參數含義

       

    • intellij idea ultimate版自帶的工具。打開方式 Tools->Test RESTful Web Service。也是可以直接編輯http header(包括cookies)請求發送,GET、POST、PUT等請求方式也都是支持的。
  6. response是一段json格式的數據,中文是采用的unicode編碼,解碼后數據內容如下圖

 

代碼實現

  • 代碼采用的Java HttpClient4.x,關於HttpClient4.x的使用我這里不過多講解,要注意的是HttpClient4.x和3.x API有很大的差異。
 1 package com.cnblogs.wycm;
 2 
 3 import com.alibaba.fastjson.JSON;
 4 import com.alibaba.fastjson.JSONObject;
 5 import org.apache.http.client.methods.CloseableHttpResponse;
 6 import org.apache.http.client.methods.HttpGet;
 7 import org.apache.http.impl.client.CloseableHttpClient;
 8 import org.apache.http.impl.client.HttpClients;
 9 import org.apache.http.util.EntityUtils;
10 
11 import java.io.IOException;
12 
13 /**
14  * 獲取wo-yan-chen-mo關注的所有知乎用戶信息
15  * 只是把用戶資料打印出來,沒有具體解析(關於解析出詳細數據可以采用正則表達式、json庫、jsonpath等方式)
16  */
17 public class Demo {
18     public static void main(String[] args) throws IOException {
19         //創建http客戶端
20         CloseableHttpClient httpClient = HttpClients.createDefault();
21 
22         String url = "https://www.zhihu.com/api/v4/members/wo-yan-chen-mo/followees?include=data%5B*%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics&offset=0&limit=20";
23 
24         //創建http request(GET)
25         HttpGet request = new HttpGet(url);
26 
27         //設置http request header
28         request.setHeader("authorization", "oauth c3cef7c66a1843f8b3a9e6a1e3160e20");
29         //執行http請求
30         CloseableHttpResponse response = httpClient.execute(request);
31         //打印response
32         String responseStr = EntityUtils.toString(response.getEntity());
33         System.out.println(responseStr);
34 
35         String nextPageUrl = getNextPageUrl(responseStr);
36         boolean isEnd = getIsEnd(responseStr);
37 
38         while (!isEnd && nextPageUrl != null){
39             //創建http request(GET)
40             request = new HttpGet(nextPageUrl);
41 
42             //設置http request header
43             request.setHeader("authorization", "oauth c3cef7c66a1843f8b3a9e6a1e3160e20");
44             response = httpClient.execute(request);
45             //打印response
46             responseStr = EntityUtils.toString(response.getEntity());
47             System.out.println(responseStr);
48             nextPageUrl = getNextPageUrl(responseStr);
49             isEnd = getIsEnd(responseStr);
50         }
51     }
52 
53     /**
54      * 獲取next url
55      * @param responseStr
56      * @return
57      */
58     private static String getNextPageUrl(String responseStr){
59         JSONObject jsonObject = (JSONObject) JSON.parse(responseStr);
60         jsonObject = (JSONObject) jsonObject.get("paging");
61         return jsonObject.get("next").toString();
62     }
63 
64     /**
65      * 獲取is_end
66      * @param responseStr
67      * @return
68      */
69     private static boolean getIsEnd(String responseStr){
70         JSONObject jsonObject = (JSONObject) JSON.parse(responseStr);
71         jsonObject = (JSONObject) jsonObject.get("paging");
72         return (boolean) jsonObject.get("is_end");
73     }
74 }
  • maven依賴

 1   <dependency>
 2       <groupId>org.apache.httpcomponents</groupId>
 3       <artifactId>httpclient</artifactId>
 4       <version>4.5</version>
 5     </dependency>
 6 
 7     <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
 8     <dependency>
 9       <groupId>com.alibaba</groupId>
10       <artifactId>fastjson</artifactId>
11       <version>1.2.31</version>
12     </dependency>

 

 



一個程序員日常分享,包括但不限於爬蟲、Java后端技術,歡迎關注。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM