QQ空間動態爬蟲

来源:http://www.cnblogs.com/wuzhiblog/archive/2016/12/31/qqzone_crawler.html
-Advertisement-
Play Games

作者:虛靜 鏈接:https://zhuanlan.zhihu.com/p/24656161 來源:知乎 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。 先說明幾件事: 題目的意思是,用於獲取“QQ空間動態”的爬蟲,而不是”針對QQ空間“的”動態爬蟲“ 這裡的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



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 純粹是記錄一下自己在剛開始使用的時候遇到的一些坑,以及自己是怎樣通過配合redis來解決問題的。文章分為三個部分,一是怎樣跑起來,並且怎樣監控相關的隊列和任務;二是遇到的幾個坑;三是給一些自己配合redis使用的代碼示例。 一.celery使用: Ⅰ.把任務中間件伺服器跑起來,rabbitmq-se ...
  • Input 輸入數據首先包括一個整數C,表示測試實例的個數,每個測試實例的第一行是一個整數N(1 <= N <= 100),表示數塔的高度,接下來用N行數字表示數塔,其中第i行有個i個整數,且所有的整數均在區間[0,99]內。 Output 對於每個測試實例,輸出可能得到的最大和,每個實例的輸出占一 ...
  • 在學習CGlib動態代理時,遇到如下錯誤: Exception in thread "main" java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.<init>(I)V 經過百度上尋找答案,是jar包衝突導致,解決方案: 把cgli ...
  • 今天學習主要內容: Python: 1、with語句(補充昨天的文件操作) 用with打開的文件在腳本結束會自動關閉,以防普通打開方式忘記關閉文件連接 語法: with open("demo.txt","r",encoding="utf-8") as file: for line in file: ...
  • JVM的類載入機制就是:JVM把描述類的class文件載入到記憶體,並對數據進行校驗、轉換解析和初始化,最終形成可以被JVM直接使用的Java類型 ...
  • 假設網站A有以下功能需求:1,pc端微信掃碼登錄;2,微信瀏覽器中的靜默登錄功能需求,這兩種需求就需要用到用戶的unionID,這樣才能在多個登錄點(終端)識別用戶。那麼這兩種需求下用戶的unionID該如何獲取呢? 1,先看pc端的解決方案 以snsapi_login為scope發起網頁授權,先拿 ...
  • 攔截導彈動態規劃問題 某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。 輸入導彈 ...
  • DispatcherServlet是Spring MVC的核心,按照傳統方式, 需要把它配置到web.xml中. 我個人比較不喜歡XML配置方式, XML看起來太累, 冗長繁瑣. 還好藉助於Servlet 3規範和Spring 3.1的功能增強, 可以採用一種全新的,更簡潔的方式配置Spring M ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...