看了網上的許多博客,基本上都是一個樣,基本的知識也沒詳細解釋,我這次也想跟大家仔細分析一下,自己還是要有一定爬蟲基礎,本人技術有限,如果本文哪有錯誤或不夠準確的地方,還望大牛們指點ヾ(๑╹◡╹)ノ"
一、環境配置:
- Python 3.6
- selenium (注意:先配置好自己瀏覽器的驅動,下載地址看下面)
- pymysql
- re
- requests
點擊下載chrome的---->Chrome_webdriver
點擊下載Firefox的---->Firefox_webdriver
點擊下載IE的---->IE_webdriver
二、思路:
作爲一個菜雞學了一點爬蟲,就想做一個好友的說說分析,最開始我以爲這個爬蟲很簡單幾十行就可以搞定,然而忽略了一些東西。。。。(先賣個關子)
先說說整個過程的想法:
看起來是不是很簡單?(手動dog)那現在我們就來按步驟操作一下
1.找到包含好友的QQ信息的url(這裏也有兩種方法)
- 法一:
先點開好友這一欄,通過親密度的排行來獲取,這裏我們點開F12,選中Network
一般這種信息都在XHR或者JS類型裏面,大家可以在這裏面找找,通過一會的尋找我們就發現friend_ship開頭這xhr裏面的items_list就包含了好友的QQ號和姓名,但是此方法獲取的qq不全,只是大部分的qq
- 法二:
點擊頁面最上面的設置按鈕,滑動可見好友,通過js的結果分析,隨着下滑請求的url的頁數都在變化,我們只要每次修改下頁數的參數就可以獲取所有好友的QQ,這個方法可以獲取所有的好友的qq,但對於qq好友很少的朋友來說,此方法不適用
2.找到包含好友的說說的url
我們先隨便點進一個好友的空間進行分析
點進去過後,我們F12 進行分析,發現一頁最多存20條說說,以此我們可以通過說說總數(re提取)來算出一共有多少頁,然後通過構造url來獲取
通過以上的分析過後我們開始獲取url:
我們先看看獲取qq的第一種方法的url:
我們分析一下這裏面每次登錄都在變的參數
- g_tk (空間加密算法)
- qzonetoken (空間源碼裏面的參數)
那這兩個參數我們要怎麼獲取呢?爲什麼每次登錄這兩個參數的都在變呢?
我們首先先要了解一下---->cookie
在看看session的基本概念
快速查看 cookies 的方法:按F12進入瀏覽器的開發者模式——console——在命令行輸入javascript:alert(document.cookie)
,再回車即可看見
所以我們登錄過後,每次都訪問url的時候都要保持着參數不變,也就是說cookie不能變
每次都要是同一個cookie(就相當於每次都是以你的身份保持着登錄狀態去訪問他人空間),否則就會出現以下情況↓
理解好以上的幾個問題過後,問題就解決了一大部分了
接着我們分析g_tk參數,在自己qq空間主頁 F12 點JS類型文件,找到以下文件,查看Preview部分,分析一下其中的代碼
其實這個程序的意思, 還是直接上代碼吧
def get_tk(cookie):
hashes = 5381
for i in cookie['p_skey']: #提取cookie中p_skey每個字母
hashes += (hashes << 5) + ord(i) #加密過程,ord()將 字符轉化爲ASCII碼
# << 二進制 左移運算 左移幾位就相當於乘以2的幾次方
return hashes & 2147483647 #二進制 與運算
# 比如 2&3 轉爲二進制 10&11
# 都是1結果爲1,否則爲0
# 所以二進制算出來是 10 返回2
# 還不懂的朋友,還是自行Baidu吧
隨着我們分析第二個參數qzonetoken
這個參數很好獲取,在我們空間主頁右鍵查看網頁源代碼,Ctrl+F查找下可以找到,之後我們可以通過正則提取
ok,理解了上面的全部,基本就完成了80%了,接下來我們開始代碼實現
三、代碼實現:
先導入第三方庫
import re, requests
import time, pymysql
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
- 首先是登錄,我們這裏用selenium模擬瀏覽器實現
def login():
driver = webdriver.Chrome() # 傳入瀏覽器對象
wait = WebDriverWait(driver, 5) # 顯式等待
driver.get('https://qzone.qq.com/')
driver.switch_to_frame('login_frame') #切換到登錄窗口
input = wait.until(EC.presence_of_element_located((By.ID, 'switcher_plogin')))# 顯式等待 找到賬號密碼登錄按鈕
time.sleep(1)
input.click() # 交互點擊
driver.find_element_by_id('u').clear() #清空裏面的內容
driver.find_element_by_id('u').send_keys('your_qq') #傳入你的QQ
time.sleep(3)
driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('your_password') #傳入你的密碼
button = driver.find_element_by_id('login_button') #找到登錄按鈕
time.sleep(3)
button.click()
time.sleep(1)
driver.switch_to.default_content() # 將frame重新定位回來,不然後續會出錯
return driver
- 通過傳回來的driver對象獲取網頁源代碼和cookies
通過源代碼獲取qzonetoken參數
def get_qzonetoken(html):
paa = re.compile(r'window\.g_qzonetoken = \(function\(\)\{ try\{return "(.*?)";\} catch\(e\)', re.S)
res = re.findall(paa, html)[0] # 因爲返回的是列表形式,所以只取第一個元素
return res
注意:driver.get_cookies()
獲取的cookies是散的,所以要進行以下操作:
def get_tk(cookie): #加密過程
hashes = 5381
for i in cookie['p_skey']:
hashes += (hashes << 5) + ord(i)
return hashes & 2147483647
cookies = driver.get_cookies()
for item in cookies:
cookie[item['name']] = item['value'] #將對應表達聯繫起來
# 上一步不懂的可以把 cookies的值輸出來看一下
g_tk = get_tk(cookie)
3.將cookies傳給requests,以保證都是在登錄狀態(最關鍵)
def back_session(driver):
mysession = requests.session() # 建立一個session對話
cookies = driver.get_cookies()
cookie = {}
for item in cookies:
cookie[item['name']] = item['value']
headers = {
'authority': 'user.qzone.qq.com',
'referer': 'https://qzone.qq.com/',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36'
}
c = requests.utils.cookiejar_from_dict(cookie, cookiejar=None, overwrite=True)
# 將字典轉化爲cookiejar形式
mysession.headers = headers # 請求頭,防止反爬
mysession.cookies.update(c) # 更新cookies
return mysession # 返回帶cookies的requests
- 存入MySQL
connection = pymysql.connect(host='your_host', port=3306, user='你的賬戶', passwd='你的密碼', db='your_database')
connection.autocommit(True) #開啓自動提交 不然每次執行
def save_mysql(say, stime, QQ, connection): #這裏我存入說說、說說時間、qq號
stime = str(stime)
content = str(say)
QQ = str(QQ)
sql = 'insert into qq values ("{}","{}","{}")'.format(content, stime, QQ)
connection.query(sql)
# 數據庫建表的時候 一定要把字符集改成utf8
看看效果圖,爬了40多分鐘,四萬多條數據,有點小慢。。。,很慢。大家可以嘗試下多線程爬取
完成以上步驟之後整個框架就都搭好了,其餘數據的提取大家就先自己完成了吧(本文最後會給出GitHub地址),也希望大家看思路過後,自己操作,不僅僅是copy、paste and run
導出某個好友的數據庫,用Notepad++過濾一些數據後,通過詞雲分析
import jieba
from matplotlib import pyplot as plt
from wordcloud import WordCloud
from PIL import Image
import numpy as np
path = r'your_data.text_path'
font = r'C:\Windows\Fonts\simkai.TTF' # 字體path
text = (open('C:/Users/hp/Desktop/233.txt', 'r', encoding='utf-8')).read() # 如果是中文的話encoding換成utf8
cut = jieba.cut(text) # 分詞
string = ' '.join(cut)
print(len(string))
img = Image.open('your_photo_path') # 打開圖片
img_array = np.array(img) # 將圖片裝換爲數組
stopword = ['xa0'] # 設置停止詞,也就是你不想顯示的詞,這裏這個詞是我前期處理沒處理好,你可以刪掉他看看他的作用
wc = WordCloud(
scale=4, #清晰度
background_color='white', #背景顏色
max_words=400, #最多單詞
width=1000,
height=800,
mask=img_array,
font_path=font,
stopwords=stopword # 停用詞
)
wc.generate_from_text(string) # 繪製圖片
plt.imshow(wc)
plt.axis('off')
plt.figure()
#plt.show() # 顯示圖片
wc.to_file('F:/3.png') # 保存圖片
最後我麼就分析到以下圖片,字越大說明出現次數最多
最後貼上我的代碼鏈接 https://github.com/Leaderzhangyi/QQspider 希望大家能夠共同改進