Python網路爬蟲實戰:爬取知乎話題下 18934 條回答資料

機靈鶴發表於2019-01-17

好久沒有更爬蟲了,因為馬上要畢業了,最近在準備畢設的專案,沒時間搞這個了,不好意西了大家。

事情是這樣的,上週末,我一單身單身單身的好哥兒們找我,拜託我個事兒。。。我這個單身單身單身的好哥們喜歡逛知乎,尤其喜歡看一些情感型別的話題,寄希望於這個來解決單身的煩惱。某天,他看到了知乎上這樣一個問題:

你的擇偶標準是怎樣的?

 這個問題下的回答數竟有有一萬八千多條,然後這傢伙忍不住了,來找我幫忙,看能不能用爬蟲爬一下,看看到底都是些什麼人在評論,回答的人裡面到底是小哥哥們多還是小姐姐們多呢?

單身單身單身的好哥們求助,肯定要幫嘛是吧!畢設先放一邊也要幫嘛!

所以我們這次來爬知乎!

 

一、首先明確需求,爬什麼資料?


通過一番交流,最終我們確認要爬的資料是:知乎網站上 “你的擇偶標準是怎樣的?”問題下回答的使用者的資料,包括

  • 使用者的 ID
  • 使用者的暱稱
  • 使用者的性別
  • 使用者回答的贊同數
  • 使用者回答的評論數

經過初步檢視,這些資訊在不登陸的情況下,在網頁中都可以檢視到(傳說中,知乎的反爬機制幾乎沒有,是爬蟲愛好者最喜歡爬的網站之一)。

接下來,我們只需要分析網頁原始碼,看看這些資料都藏在哪裡就好了。

 

二、分析網站原始碼,找到資料存放位置


老規矩,開啟目標網站,按 F12 ,召喚出開發者工具,檢視原始碼,以及監控網路請求。

逛網頁版知乎的時候,我發現(其實大家都能發現),回答數雖有一萬多條,但是網站並不會全部顯示出來,而且也沒有翻頁按鈕,在我們向下滾動頁面到底的時候,後面的回答才會動態的載入出來。

這不就是我們特別喜歡的 Ajax 動態載入的技術嘛?

這種載入方式的原理,簡單通俗點講就是,我伺服器上有很多資料,一下子也發不過去,發過去你也看不完,所以乾脆我分批給你發,你看完一批,然後跟我講,我給你發下一批。瀏覽器就是這樣,每次檢測到你進度條快到底了,趕緊給伺服器發個訊息,把下一批資料拿過來載入上去。

所以我們應付 ajax 的方法也很簡單,截獲瀏覽器發給伺服器的請求,然後分析出請求的規律,然後我們用爬蟲偽裝成瀏覽器不斷向伺服器傳送請求,這樣就可以獲取源源不斷的資料了。

 

1.  將開發者工具切換到 Network 視窗,清空其中的內容(主要是為了防止干擾),然後不斷的向下拖動頁面的滑動條(讓瀏覽器多向伺服器傳送一些請求,方便我們查詢)。

2. 上圖中便是我篩查到的請求(截圖裡我是做了篩選的,實際上接獲到的請求是相當多的,需要花點功夫去從裡面篩選的),可能有人會疑惑,我怎麼知道哪個請求才是我要的呢?這裡有幾個篩選小技巧。 

a. 請求的型別基本都是 XHR 這一型別的,過濾器中選擇這個,可以幫我們過濾到大量的圖片,指令碼,以及網頁樣式檔案等等。

b. 點選請求之後,出現詳情頁,然後切換到 Preview 選項卡,可以預覽該請求返回的內容。我們需要找的請求,返回的內容一般是 json 格式的資料,其中包含了關於知乎回答的一些資訊。

 c. 根據以上兩條,基本上稍微花點功夫,很快就可以找到所要找的請求。

3. 分析請求頭的格式(找規律),找到請求頭構成的一般規律,然後我們根據這些規律,構造出剩餘所有的回答資料的請求頭。

第一頁:https://www.zhihu.com/api/v4/questions/275359100/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset=0&platform=desktop&sort_by=default
第二頁:https://www.zhihu.com/api/v4/questions/275359100/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset=5&platform=desktop&sort_by=default
第三頁:https://www.zhihu.com/api/v4/questions/275359100/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset=10&platform=desktop&sort_by=default

 仔細觀察可以發現,三者除了最後 offset 引數不一致外,其餘構成完全一樣,而且 offset 的值還很有規律,每次數字增加 5。然後聯想到請求返回的 json 資料可知,每次請求返回 5 條資料。

 4. 思路一下子清晰了,offset 是一個偏移量,表示從第幾條回答開始獲取,每次獲取5條回答,所以下次獲取時 offset 就要加 5 啦。所以我們編寫爬蟲時,用一個迴圈,讓 offset 的值從零開始,每次加 5,一直到總回答數為止,這樣就可以獲取到所有的回答資料了。

 

 

三、動手擼程式碼,寫爬蟲


爬蟲我們之前寫過很多遍了,思路都差不多,而且知乎網站對爬蟲真的很友好,完全不設反爬機制,我都沒有用到 代理IP和動態UA,甚至沒有寫延遲函式,近兩萬條資料,直接就順利的跑完了。

全部原始碼獻上,關於程式碼的解釋,可以參考我的其他爬蟲文章,已經講的很詳細了,這裡就不再贅述了,如果還有問題,可以私信或者評論問我。

import requests
import json
import time
import re
import datetime
import pandas as pd

def get_data(url):
    '''
    功能:訪問 url 的網頁,獲取網頁內容並返回
    引數:
        url :目標網頁的 url
    返回:目標網頁的 html 內容
    '''
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }

    try:
        r = requests.get(url, headers=headers)
        r.raise_for_status()
        return r.text
    
    except requests.HTTPError as e:
        print(e)
        print("HTTPError")
    except requests.RequestException as e:
        print(e)
    except:
        print("Unknown Error !")
        
def parse_data(html):
    '''
    功能:提取 html 頁面資訊中的關鍵資訊,並整合一個陣列並返回
    引數:html 根據 url 獲取到的網頁內容
    返回:儲存有 html 中提取出的關鍵資訊的陣列
    '''
    json_data = json.loads(html)['data']
    comments = []
    
    try:
        for item in json_data:

            comment = []
            comment.append(item['author']['name'])    # 姓名
            comment.append(item['author']['gender'])  # 性別
            #comment.append(item['author']['url'])     # 個人主頁
            comment.append(item['voteup_count'])      # 點贊數
            comment.append(item['comment_count'])     # 評論數
            #comment.append(item['url'])               # 回答連結
            comments.append(comment)
            
        return comments
    
    except Exception as e:
        print(comment)
        print(e)
        
def save_data(comments):
    '''
    功能:將comments中的資訊輸出到檔案中/或資料庫中。
    引數:comments 將要儲存的資料  
    '''
    filename = 'Data/comments.csv'
    
    dataframe = pd.DataFrame(comments)
    dataframe.to_csv(filename, mode='a', index=False, sep=',', header=False)
    #dataframe.to_csv(filename, mode='a', index=False, sep=',', header=['name','gender','user_url','voteup','cmt_count','url'])
    

def main():
    
    url = 'https://www.zhihu.com/api/v4/questions/275359100/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset=5&platform=desktop&sort_by=default'
    
    # get total cmts number
    html = get_data(url)
    totals = json.loads(html)['paging']['totals']
    
    print(totals)
    print('---'*10)
    
    page = 0
    
    while(page < totals):
        url = 'https://www.zhihu.com/api/v4/questions/275359100/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=5&offset='+ str(page) +'&platform=desktop&sort_by=default'

        html = get_data(url)
        comments = parse_data(html)
        save_data(comments)
        
        print(page)
        page += 5
    
    
if __name__ == '__main__':
    main()
    print("完成!!")

 

四、簡單分析資料


簡單的整理了一下資料,結果如下:

  • 一共爬取了 18934 條資料,其中 10555 個是小哥哥,3806 個是小姐姐,還有 4573 個性別未知;
  • 這些回答中,有 8390 個使用者是匿名回答;
  • 點贊數排名前100的回答中,有 52 個小姐姐,有 38 個小哥哥,10 個性別未知;
  • 評論數排名前100的回答中,有 42 個小姐姐,有 51 個小哥哥,7 個性別未知;

 基於上述資料,我們可以得出一些結論:

  • 此話題下,回答的男生人數是女生人數的 2.77 倍,可以得知,男性對於擇偶標準這個話題更加關注,也更加願意去分享自己的擇偶標準(其實就是變相地在相親嘛,我這個單身單身單身的好哥兒們不也是衝著這個來的嘛!)
  • 畢竟這個話題還是有點涉及隱私,難以啟齒的,所以 44.3% 的使用者選擇了匿名回答。
  • 論評論數,男生的回答要略優於女生,而論點贊數,小姐姐們的回答則會更高。可能是男生的回答多有抖機靈幽默,比較容易有交流討論的話題性,而女生喜歡貼美美噠照片,引得小哥哥們紛紛點贊收藏罷。

 

後記


我把爬到的資料結果發給我的單身單身單身的好哥兒們看了之後,好哥兒們感到非常詫異,

因為他看回答前幾個都是妹子,還以為這個話題下的妹子會更多一點呢!

原來在網路上,也改變不了狼多肉少的局面。 

 

 

如果文章中有哪裡沒有講明白,或者講解有誤的地方,歡迎在評論區批評指正,或者掃描下面的二維碼,加我微信,大家一起學習交流,共同進步。  

相關文章