QQ空間動態爬蟲


作者:虛靜
鏈接:https://zhuanlan.zhihu.com/p/24656161
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

先說明幾件事:

  • 題目的意思是,用於獲取“QQ空間動態”的爬蟲,而不是”針對QQ空間“的”動態爬蟲“
  • 這里的QQ空間動態,特指“說說”
  • 程序是使用cookie登錄的。所以如果是想知道如何使用爬蟲根據QQ號和密碼來實現登錄的朋友可以把頁面關了
  • 本程序用python3實現,具體版本為python3.5,唯一需要用到的第三方庫是requests
  • 程序代碼獲取方式在最后面

----------------------------------------

程序主要由三部分構成,它們分別對應着本爬蟲的三個步驟。

1. 獲取所有QQ好友信息

間接獲取。先把QQ空間的訪問權限設置為僅QQ好友可訪問

點保存后,上方會出現“當前權限下,XXX好友可以訪問你的空間”的提示,如上圖。此時打開F12,切換到JavaScript監測窗口。點擊上圖中畫下划線的那幾個字,就可以發現瀏覽器發送了一個GET請求,在Firebug中看到是這樣的:

查看它的response,會發現里面就是由自己好友的名字和QQ號碼組成的近似於JSON格式的內容。爬蟲程序中的get_my_friends.py就是用於獲取它的內容的,其主要代碼如下:

    def get_friends(self):
        key = True
        position = 0
        while key:
            url = self.base_url + '&offset=' + str(position)
            referer = 'http://qzs.qq.com/qzone/v8/pages/setting/visit_v8.html'
            self.headers['Referer'] = referer

            print("\tDealing with position\t%d." % position)
            res = requests.get(url, headers=self.headers)
            html = res.text
            with open('friends/offset' + str(position) + '.json', 'w') as f:
                f.write(html)

            # 檢查是否已經全部都獲取完,如果是的話
            # uinlist對應的是一個空列表
            with open('friends/offset' + str(position) + '.json') as f2:
                con = f2.read()
            if '''"uinlist":[]''' in con:
                print("Get friends Finish")
                break

            position += 50 

2. 獲取所有好友的QQ號碼

這一步其實只是文本處理,或者說是字符串處理而已。把上一步中保存好的文件進行處理,從中提取好友的QQ號碼和名稱,將其保存在一個文件中(其名為qqnumber.inc)。由於其內容本身近於字典形式,所以稍加處理,將其轉成字典,再進行處理。處理程序為爬蟲程序中的get_qq_number.py,主要代碼如下:

def exact_qq_number(self):
    friendsFiles = [x for x in os.listdir('friends') if x.endswith("json")]

    qqnumber_item = []
    i = 0
    for each_file in friendsFiles:
        with open('friends/' + each_file) as f:
            source = f.read()
            con_dict = source[75:-4].replace('\n', '')
            con_json = json.loads(con_dict)
            friends_list = con_json['uinlist']

            # Get each item from friends list, each item is a dict
            for item in friends_list:
                i = i + 1
                qqnumber_item.append(item)
    else:
        with open('qqnumber.inc', 'w') as qqfile:
            qqfile.write(str(qqnumber_item))

3. 分別獲取每個好友的空間動態(說說)

獲取好友的說說,方法類似於第1步。先打開F12,保持在默認的All選項卡下就行。再打開好友的空間,點開他們的說說主頁,此時可以在請求列表中找到一個URL中包含emotion_cgi_msglist的請求,根據名字就可以猜到,它就是我們要的信息了。然后我們可以模擬這個請求,獲取返回的內容並保存。爬蟲程序中的get_moods.py就用於此。

此程序文件中包含兩個類:Get_moods_start()、Get_moods()。后者實現發送HTTP請求並獲取返回內容、保存內容,前者用於把QQ號傳到后者的方法中進行處理、控制循環、處理異常。Get_moods()功能實現的主要方法代碼如下:

def get_moods(self, qqnumber):
    '''Use cookie and header to get moods file and save it to result folder with QQnumber name'''

    referer = 'http://user.qzone.qq.com/' + qqnumber
    self.headers['Referer'] = referer

    # Create a folder with qq number to save it's result file
    util.check_path('mood_result/' + qqnumber)

    # Get the goal url, except the position argument.
    url_base = util.parse_moods_url(qqnumber)
    pos = 0
    key = True

    while key:
        print("\tDealing with position:\t%d" % pos)
        url = url_base + "&pos=%d" % pos
        res = self.session.get(url, headers = self.headers)
        con = res.text
        with open('mood_result/' + qqnumber + '/' + str(pos), 'w') as f:
            f.write(con)

        if '''"msglist":null''' in con:
            key = False

        # Cannot access...
        if '''"msgnum":0''' in con:
            with open('crawler_log.log', 'a') as log_file:
                log_file.write("%s Cannot access..\n" % qqnumber)
            key = False

        # Cookie expried
        if '''"subcode":-4001''' in con:
            with open('crawler_log.log', 'a') as log_file:
                log_file.write('Cookie Expried! Time is %s\n' % time.ctime())
            sys.exit()

        pos += 20
        time.sleep(5)

程序運行的結果會保存在名為mood_result的文件夾中,其中包含以各好友QQ號碼為名的文件夾,他們的說說信息文件都保存在對應的文件夾中。

-----------------------------------------

其它說明

程序還有兩個文件,util.py和main.py,后者是程序運行的入口,前者則包含了一些通用功能,例如獲取cookie、生成發送HTTP請求時要用到的g_tk值、構造URL。此處講一下g_tk值。

在前面第1步和第3步中,發送的HTTP請求的URL參數里面,都包含有g_tk值,這個值是通過cookie中的p_skey參數的值生成的。可以在登錄QQ空間時通過F12查看JS文件,找到它的對應算法。它位於名為qzfl_v8_2.1.57的js文件中。由於該文件內容過大,近6千行,在firebug中直接看response還找不到,不過可以通過在response中搜索得到,或者將單獨在瀏覽器中打開,就可以得到它的全部內容了。找到這個g_tk的計算方法:

不要被這里的hash誤導,在python里面hash()是一個內置方法,但在JS中,在此處,它只是個變量名而已。在本爬蟲程序里面是這樣實現的:

def get_g_tk():
    ''' make g_tk value'''

    pskey_start = cookie.find('p_skey=')
    pskey_end = cookie.find(';', pskey_start)
    p_skey = cookie[pskey_start+7: pskey_end]

    h = 5381

    for s in p_skey:
        h += (h << 5) + ord(s)

    return h & 2147483647

主要是通過位移和並運算,得到一個唯一值。

最后

如第3步中貼出來的代碼后面部分寫的,如果好友的空間不對自己開放,那么是無法獲取到他的說說的,發送請求后有返回,但主要內容是空的。

如果cookie過期了,程序會記錄日志並自動退出。我的程序運行了15個小時,請求了494個好友的說說文件,發送1萬1千多個請求(每個請求得到一個文件,我的結果文件夾中就有這么多個文件),cookie沒有過期,也沒有被空間反爬。哦,對了,為了防止反爬蟲,本程序是使用每請求一個文件就暫停5秒的方式應對的。(所以才那么慢,也不敢上多線程)

最終獲取到的所有好友的說說文件,還需要自己去提取所需要的信息。本程序只獲取源數據,不處理數據。

Github代碼鏈接:QQzone_crawler


免責聲明!

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



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