1. 程式人生 > >90後的青春,定格在被淡忘的QQ空間裡

90後的青春,定格在被淡忘的QQ空間裡

QQ空間,這個曾經陪我們從童年到少年再到成年,從2G時代再到如今的4G末,佔據了我們太多的青春回憶,如今好友空間動態更新的不在像從前那樣頻繁。依稀記得當年的好友買賣,搶車位再或者情侶空間,現在想想那時候真的很幼稚,那就是我們傻逼的童年,什麼互踩,火星文,跑堂見證了我們無憂無慮的童年。

有時候看看QQ推送的"那年今日",看到自己好幾年前發的動態,說的傻話,自己都怕了自己。有時候看到好朋友幾年前的動態,不由笑罵道,這孫子,怎麼這麼2! 即使現在不怎麼用QQ了,有時候看看曾經發的說說還有空間的留言。即使讓我再尷尬也不捨得刪,因為那都是青春滿滿的回憶。

空間留言上千條,說說也比較多,一頁一頁的翻比較麻煩。索性就把這些資料都下載到本地。同理我們也可以匯出全部聯絡人的說說和留言板。

 

Selenium

由於訪問好友留言板需要登入,為了方便起見我們使用Web應用程式測試的Selenium工具。該工具可以用於單元測試,整合測試,系統測試等等。它可以像真正的使用者一樣去操作瀏覽器等,支援Mozilla Firefox、Google Chrome、Safari、Opera、IE等等瀏覽器。

使用這個工具之前我們需要安裝selenium庫和下載相應瀏覽器的驅動。然後通過分析QQ空間登入介面我們發現預設是掃碼登入,因此需要切換成賬號密碼登入。

 

通過分析html標籤屬性,我們發現 id="switcher_plogin",是個切換登入的全域性唯一屬性。同理我們再需要找到賬號、密碼輸入框和點選登入的元素就可以用selenium模擬登入了

 

登入部分程式碼如下:

from selenium import webdriver

driver = webdriver.Chrome()
    # 獲取谷歌瀏覽器驅動
    driver = webdriver.Chrome()
    # 登入網站
    driver.get('https://i.qq.com')
    # 選擇賬號密碼登入
    driver.switch_to_frame('login_frame')
    # 點選輸入框獲取輸入
    driver.find_element_by_id('switcher_plogin').click()
    # 輸入賬號
    driver.find_element_by_id('u').send_keys('你的qq號')
    # 輸入密碼
    driver.find_element_by_id('p').send_keys('qq密碼')
    # 點選登入
    driver.find_element_by_id('login_button').click()

  

工作前的引數準備

通過檢視開發者工具中的請求我們發現,登入之後每次請求除了攜帶必要的引數以外,還攜帶了登入獲取的token和g_tk。token我們可以從網頁原始碼中直接獲取,但是g_tk在原始碼中沒有,根據以往經驗第一步只能從js中檢視,果然發現了一段加密程式碼,再結合上下文發現是從cookie中取出“p_skey”的值再經過一系列操作就是g_tk的值了。因為我們需要先獲取cookie,然後再通過cookie獲取g_tk。

在這裡插入圖片描述

部分js加密邏輯程式碼

if (e) {
     if (e.host && e.host.indexOf("qzone.qq.com") > 0) {
        try {
           t = parent.QZFL.cookie.get("p_skey")
        } catch(e) {
           t = QZFL.cookie.get("p_skey")
        }
     } 
        ............
  }

"g_tk=" + QZFL.pluginsDefine.getACSRFToken(t)

QZFL.pluginsDefine.getACSRFToken._DJB = function(e) {
        var t = 5381;
        for (var n = 0,
        r = e.length; n < r; ++n) {
            t += (t << 5) + e.charCodeAt(n)
        }
        return t & 2147483647
    };

 

 

獲取token && cookie && g_tk程式碼

"""
 獲取g_tk的值
"""
def get_g_tk(cookie):
    hashes = 5381
    for letter in cookie['p_skey']:
        hashes += (hashes << 5) + ord(letter)
    return hashes & 0x7fffffff

# 獲取登入之後的cookie資訊
cookie = {}
for elem in driver.get_cookies():
    cookie[elem['name']] = elem['value']
# 獲取g_tk
g_tk = get_g_tk(cookie)
# 利用xpath獲取登入之後的網頁原始碼
html = driver.page_source
xpath = r'window\.g_qzonetoken = \(function\(\)\{ try\{return "(.*?)";}'
# 通過xpath 獲得登入後的token
token = re.compile(xpath).findall(html)[0]
 

開始搞事

破解了一個簡單的反爬蟲,下面就可以編寫正式的爬蟲程式碼了,首先確定我們目標url、通過瀏覽器分析響應的json物件、編寫headers

因為每次請求都需要攜帶登入資訊,為了方便我們用到了session類,其次觀察相應我們發現返回的response有無用的字元,因此需要進行擷取

在這裡插入圖片描述

headers = {
    'authority': 'user.qzone.qq.com',
    'method': 'GET',
    'scheme': 'https',
    'accept-language': 'zh-CN,zh;q=0.9',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}

def get_resp(cookie, g_tk, token, page):
    session = requests.session()
    # 將cookie字典轉換成RequestsCookieJar
    c = requests.utils.cookiejar_from_dict(cookie)
    # 將headers 放入session
    session.headers = headers
    # RequestsCookieJar複製給session
    session.cookies = c
    # 訪問留言板的url
    url = f'https://user.qzone.qq.com/proxy/domain/m.qzone.qq.com/cgi-bin/new/get_msgb?uin={登陸的qq}&hostUin={要查詢留言內容的QQ號}&start={page}&num=10&g_tk={g_tk}&qzonetoken={token}'
    print(url)
    response = session.get(url)
    # 擷取無用的字元
    resp_text = response.text[10: -3]
    # 轉為json
    resp_json = json.loads(resp_text)
    return resp_json

  

 

上面的方法,只是獲得了某一頁的介面相應,我們通過json獲取留言總數,再除以每頁的條數,就可以知道總頁數了。然後再遍歷去取每頁的資料,為了方便檢視將資料儲存在csv檔案中,另外將留言內容儲存在txt檔案中,生成詞雲。

def get_zone_xx(cookie, g_tk, token, page=0):
    # 初始化請求為了取總條數
    resp_json = get_resp(cookie, g_tk, token, page)
    # 總條數
    total = resp_json['data']['total']
    print(f'共{total}條留言資訊')
    # 總頁數
    size = int(total/10 + 1)
    # 已經讀取的資訊條數
    use_page = 0
    # 儲存每條資料資訊,生成csv檔案用
    content_arr = []
    for i in range(0, size):
        # 請求每一頁的內容
        resp_json = get_resp(cookie, g_tk, token, i)
        # 當條數大於或等於總條數 跳出迴圈
        if use_page >= total:
            break
        # 從每頁資料中取出需要的欄位值
        for comment in resp_json['data']['commentList']:
            use_page += 1
            print(f'當前正在讀取第{use_page}條')
            page_json = []
            # 留言日期
            page_json.append(comment['pubtime'])
            # 暱稱
            page_json.append(comment['nickname'])
            # 內容
            content = replace_html(comment['htmlContent'])
            # 將內容寫入文字 生成詞雲用
            with open('zone_text111.txt', 'a') as f:
                f.write(content)

            page_json.append(content)
            content_arr.append(page_json)

  

 

生成csv檔案

# 將總資料轉化為data frame再輸出
df = pd.DataFrame(data=content_arr,
                      columns=['留言日期', '暱稱', '留言內容'])
df.to_csv('QQ_ZONE.csv', index=False, encoding='utf-8_sig')
print('已儲存為csv檔案.')

  

執行上面程式碼生成csv檔案部分內容如下

 


生成詞雲(wordcloud)程式碼如下

 

from wordcloud import WordCloud
import matplotlib.pyplot as plt
with open('zone_text.txt','r') as f:
    mytext = f.read()

font = r'C:\Windows\Fonts\simfang.ttf'
wc = WordCloud(collocations=False, font_path=font, width=1400, height=1400, margin=2).generate(mytext)
plt.imshow(wc)
plt.axis("off")
plt.show()

plt.show()

  

執行結果如下:

 

寫在最後

上面的程式碼並沒有太複雜,也許是觸景生情,也許是對現在朋友圈各種亂七八糟的資訊產生了抵觸,所以試著去回憶青春的那些往事。
朋友圈和空間並不能去衡量一個人是是否成熟,但是對於大部分90後來說,空間真的是承載了太多純真的回憶。不忘初心,砥礪前行!!!


公眾號 程式設計師共成長 內回覆【空間】,獲取原始碼

本文首發於公眾號 程式設計師共成長 公眾號內回覆 [禮包] 即可領取優質資源,包括但不限於Java、Python、Linux、資料庫、大資料、架構、測試、前端、ui以及各方向電子書

掃碼關注公眾號