作者:虛靜 鏈接: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