http://blog.jobbole.com/77878/ html
博客 – 伯樂在線正則表達式
首頁最新文章在線課程業界開發 IT技術 設計 創業IT職場投稿更多 »瀏覽器
伯樂在線 > 首頁 > 全部文章 > Python > 零基礎自學用Python 3開發網絡爬蟲(四): 登陸服務器
零基礎自學用Python 3開發網絡爬蟲(四): 登陸cookie
原文出處: Jecvay Notes (@Jecvay) 歡迎分享原創到伯樂頭條網絡
今天的工做頗有意思, 咱們用 Python 來登陸網站, 用Cookies記錄登陸信息, 而後就能夠抓取登陸以後才能看到的信息. 今天咱們拿知乎網來作示範. 爲何是知乎? 這個很難解釋, 可是確定的是知乎這麼大這麼成功的網站徹底不用我來幫他打廣告. 知乎網的登陸比較簡單, 傳輸的時候沒有對用戶名和密碼加密, 卻又不失表明性, 有一個必須從主頁跳轉登陸的過程.app
不得不說一下, Fiddler 這個軟件是 Tpircsboy 告訴個人. 感謝他給我帶來這麼好玩的東西.ide
第一步: 使用 Fiddler 觀察瀏覽器行爲函數
在開着 Fiddler 的條件下運行瀏覽器, 輸入知乎網的網址 http://www.zhihu.com 回車後到 Fiddler 中就能看到捕捉到的鏈接信息. 在左邊選中一條 200 鏈接, 在右邊打開 Inspactors 透視圖, 上方是該條鏈接的請求報文信息, 下方是響應報文信息.post
其中 Raw 標籤是顯示報文的原文. 下方的響應報文頗有多是沒有通過解壓或者解碼的, 這種狀況他會在中間部位有一個小提示, 點擊一下就能解碼顯示出原文了.
以上這個截圖是在未登陸的時候進入 http://www.zhihu.com 獲得的. 如今咱們來輸入用戶名和密碼登錄知乎網, 再看看瀏覽器和知乎服務器之間發生了什麼.
點擊登錄後, 回到 Fiddler 裏查看新出現的一個 200 連接. 咱們瀏覽器攜帶者個人賬號密碼給知乎服務器發送了一個 POST, 內容以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST http://www.zhihu.com/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: */*
X-Requested-With: XMLHttpRequest
Referer: http://www.zhihu.com/#signin
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.4; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 97
DNT: 1
Host: www.zhihu.com
Connection: Keep-Alive
Pragma: no-cache
Cookie: __utma=51854390.1539896551.1412320246.1412320246.1412320246.1; __utmb=51854390.6.10.1412320246; __utmc=51854390; __utmz=51854390.1412320246.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmv=51854390.000–|3=entry_date=20141003=1
_xsrf=4b41f6c7a9668187ccd8a610065b9718&email=此處塗黑%40gmail.com&password=此處不可見&rememberme=y
截圖以下:
個人瀏覽器給 http://www.zhihu.com/login 這個網址(多了一個/login) 發送了一個POST, 內容都已經在上面列出來了, 有用戶名, 有密碼, 有一個」記住我」的 yes, 其中這個 WebForms 標籤下 Fiddler 可以比較層次分明的列出來 POST 的內容. 因此咱們用 Python 也發送相同的內容就能登陸了. 可是這裏出現了一個 Name 爲 _xsrf 的項, 他的值是 4b41f6c7a9668187ccd8a610065b9718. 咱們要先獲取這個值, 而後才能給他發.
瀏覽器是如何獲取的呢, 咱們剛剛是先訪問了 http://www.zhihu.com/ 這個網址, 就是首頁, 而後登陸的時候他卻給 http://www.zhihu.com/login 這個網址發信息. 因此用偵探通常的思惟去思考這個問題, 就會發現確定是首頁把 _xsrf 生成發送給咱們, 而後咱們再把這個 _xsrf 發送給 /login 這個 url. 這樣一下子事後咱們就要從第一個 GET 獲得的響應報文裏面去尋找 _xsrf
截圖下方的方框說明, 咱們不只登陸成功了, 並且服務器還告訴咱們的瀏覽器如何保存它給出的 Cookies 信息. 因此咱們也要用 Python 把這些 Cookies 信息記錄下來.
這樣 Fiddler 的工做就基本結束了!
第二步: 解壓縮
簡單的寫一個 GET 程序, 把知乎首頁 GET 下來, 而後 decode() 一下解碼, 結果報錯. 仔細一看, 發現知乎網傳給咱們的是通過 gzip 壓縮以後的數據. 這樣咱們就須要先對數據解壓. Python 進行 gzip 解壓很方便, 由於內置有庫能夠用. 代碼片斷以下:
1
2
3
4
5
6
7
8
9
import gzip
def ungzip(data):
try:
# 嘗試解壓
print('正在解壓.....')
data = gzip.decompress(data)
print('解壓完畢!')
except:
print('未經壓縮, 無需解壓')
return data
經過 opener.read() 讀取回來的數據, 通過 ungzip 自動處理後, 再來一遍 decode() 就能夠獲得解碼後的 str 了
第二步: 使用正則表達式獲取沙漠之舟
_xsrf 這個鍵的值在茫茫無際的互聯網沙漠之中指引咱們用正確的姿式來登陸知乎, 因此 _xsrf 可謂沙漠之舟. 若是沒有 _xsrf, 咱們或許有用戶名和密碼也沒法登陸知乎(我沒試過, 不過咱們學校的教務系統確實如此) 如上文所說, 咱們在第一遍 GET 的時候能夠從響應報文中的 HTML 代碼裏面獲得這個沙漠之舟. 以下函數實現了這個功能, 返回的 str 就是 _xsrf 的值.
1
2
3
4
5
import re
def getXSRF(data):
cer = re.compile('name=\"_xsrf\" value=\"(.*)\"', flags = 0)
strlist = cer.findall(data)
return strlist[0]
第三步: 發射 POST !!
集齊 _xsrf, id, password 三大法寶, 咱們能夠發射 POST 了. 這個 POST 一旦發射過去, 咱們就登錄上了服務器, 服務器就會發給咱們 Cookies. 原本處理 Cookies 是個麻煩的事情, 不過 Python 的 http.cookiejar 庫給了咱們很方便的解決方案, 只要在建立 opener 的時候將一個 HTTPCookieProcessor 放進去, Cookies 的事情就不用咱們管了. 下面的代碼體現了這一點.
1
2
3
4
5
6
7
8
9
10
11
12
13
import http.cookiejar
import urllib.request
def getOpener(head):
# deal with the Cookies
cj = http.cookiejar.CookieJar()
pro = urllib.request.HTTPCookieProcessor(cj)
opener = urllib.request.build_opener(pro)
header = []
for key, value in head.items():
elem = (key, value)
header.append(elem)
opener.addheaders = header
return opener
getOpener 函數接收一個 head 參數, 這個參數是一個字典. 函數把字典轉換成元組集合, 放進 opener. 這樣咱們創建的這個 opener 就有兩大功能:
自動處理使用 opener 過程當中遇到的 Cookies
自動在發出的 GET 或者 POST 請求中加上自定義的 Header
第四部: 正式運行
正式運行還差一點點, 咱們要把要 POST 的數據弄成 opener.open() 支持的格式. 因此還要 urllib.parse 庫裏的 urlencode() 函數. 這個函數能夠把 字典 或者 元組集合 類型的數據轉換成 & 鏈接的 str.
str 還不行, 還要經過 encode() 來編碼, 才能看成 opener.open() 或者 urlopen() 的 POST 數據參數來使用. 代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
url = 'http://www.zhihu.com/'
opener = getOpener(header)
op = opener.open(url)
data = op.read()
data = ungzip(data)
# 解壓
_xsrf = getXSRF(data.decode())
url += 'login'
id = '這裏填你的知乎賬號'
password = '這裏填你的知乎密碼'
postDict = {
'_xsrf':_xsrf,
'email': id,
'password': password,
'rememberme': 'y'
}
postData = urllib.parse.urlencode(postDict).encode()
op = opener.open(url, postData)
data = op.read()
data = ungzip(data)
print(data.decode())
# 你能夠根據你的喜歡來處理抓取回來的數據了!
代碼運行後, 咱們發現本身關注的人的動態(顯示在登錄後的知乎首頁的那些), 都被抓取回來了. 下一步作一個統計分析器, 或者自動推送器, 或者內容分級自動分類器, 均可以.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import gzip
import re
import http.cookiejar
import urllib.request
import urllib.parse
def ungzip(data):
try:
# 嘗試解壓
print('正在解壓.....')
data = gzip.decompress(data)
print('解壓完畢!')
except:
print('未經壓縮, 無需解壓')
return data
def getXSRF(data):
cer = re.compile('name=\"_xsrf\" value=\"(.*)\"', flags = 0)
strlist = cer.findall(data)
return strlist[0]
def getOpener(head):
# deal with the Cookies
cj = http.cookiejar.CookieJar()
pro = urllib.request.HTTPCookieProcessor(cj)
opener = urllib.request.build_opener(pro)
header = []
for key, value in head.items():
elem = (key, value)
header.append(elem)
opener.addheaders = header
return opener
header = {
'Connection': 'Keep-Alive',
'Accept': 'text/html, application/xhtml+xml, */*',
'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
'Accept-Encoding': 'gzip, deflate',
'Host': 'www.zhihu.com',
'DNT': '1'
}
url = 'http://www.zhihu.com/'
opener = getOpener(header)
op = opener.open(url)
data = op.read()
data = ungzip(data)
# 解壓
_xsrf = getXSRF(data.decode())
url += 'login'
id = '這裏填你的知乎賬號'
password = '這裏填你的知乎密碼'
postDict = {
'_xsrf':_xsrf,
'email': id,
'password': password,
'rememberme': 'y'
}
postData = urllib.parse.urlencode(postDict).encode()
op = opener.open(url, postData)
data = op.read()
data = ungzip(data)
print(data.decode())