目錄清單
- 入門程序了解爬蟲采集數(shù)據(jù)步驟
- 底層操作Request對象
- 請求頭設(shè)置之UserAgent用戶代理
- 請求頭設(shè)置
- 用戶代理——UserAgent
- 自定義請求頭消息
- 請求方式之GET/POST請求
- GET請求處理
- POST請求處理
- Handler處理器自定義開鎖人opener
- 自定義HTTP OPENER
- 自定義PROXY OPENER
- 會話跟蹤之cookie操作
- cookie基本操作
- cookie寫入數(shù)據(jù)操作
- cookie讀取數(shù)據(jù)操作
- 異常和錯誤處理
- 異常處理
- 錯誤處理
- HTTPS數(shù)字簽名問題
1. 入門
首先球拦,這里通過一個簡單的程序認(rèn)識什么是爬蟲!
程序清單:demo01.py
# -*- coding:utf-8 -*-
# 添加指定編碼的注釋费变,表示當(dāng)前源代碼支持中文操作
import urllib2
# 引入需要的模塊
response = urllib2.urlopen("http://www.taobao.com")
# 通過urlopen()方法請求淘寶網(wǎng)數(shù)據(jù)碳锈,獲取到的響應(yīng)數(shù)據(jù)保存在response中
print(response.read())
# 打印展示獲取到的數(shù)據(jù)
運行程序
python2 demo01.py
在控制臺中劝术,就可以看到獲取到了淘寶網(wǎng)網(wǎng)頁的源代碼數(shù)據(jù)
<!doctype html>
<html>
<head>
<title>淘寶網(wǎng) - 淘!我喜歡</title>
...
...
</html>
至此噪窘,我們可以描述爬蟲程序妆艘,就是用來根據(jù)一定的規(guī)則采集獲取網(wǎng)絡(luò)中的數(shù)據(jù)的!
整個采集過程主要步驟如下:
- 分析確定目標(biāo)網(wǎng)址
- 連接并向目標(biāo)網(wǎng)址發(fā)送請求
- 獲取目標(biāo)服務(wù)器返回的數(shù)據(jù)
2. Request對象
上述程序我們了解了爬蟲程序的操作步驟旷太,底層操作過程中其實是將請求和響應(yīng)兩部分都是分步驟進(jìn)行的:
# -*- coding:utf-8 -*-
# 添加 注釋展懈,指定當(dāng)前源代碼支持中文操作
from urllib2 import Request, urlopen
# 引入需要的模塊
request = Request("http://www.taobao.com")
# 訪問目標(biāo)網(wǎng)址,封裝請求對象
response = urlopen(request)
# 發(fā)送請求供璧,得到服務(wù)器響應(yīng)數(shù)據(jù)存崖,存儲在變量response中
print(response.read())
# 打印展示響應(yīng)的數(shù)據(jù)內(nèi)容
3. 請求頭設(shè)置之UserAgent操作
書接上回,爬蟲程序已經(jīng)開發(fā)并成功采集數(shù)據(jù)睡毒,但是我們通過抓包工具進(jìn)行發(fā)送的請求查看時来惧,發(fā)現(xiàn)一個非常嚴(yán)重的問題:請求的UserAgent(用戶代理),也就是指帶客戶端身份的描述字段演顾,赤果果顯示的是python urllib2的字樣供搀,這不是迅雷不及掩耳盜鈴嗎,用自己爬蟲的身份直接訪問服務(wù)器~很容被服務(wù)器進(jìn)行分析過濾并屏蔽訪問的钠至!
所以葛虐,要對我們發(fā)起請求的爬蟲程序進(jìn)行偽造,將爬蟲程序發(fā)送的請求偽造程瀏覽器發(fā)送的請求棉钧,通過設(shè)置請求中的UserAgent就可以實現(xiàn)
# 添加注釋屿脐,源代碼支持中文
# -*- coding:utf-8 -*-
# 引入需要的模塊
from urllib2 import Request, urlopen
# 定義訪問的目標(biāo)url地址
url = "http://www.taobao.com"
# 定義請求頭信息
headers = {
"User-agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;",
"info": "自定義消息",
}
# 構(gòu)建請求對象
request = Request(url, headers=headers)
# 發(fā)送請求獲得響應(yīng)對象
response = urlopen(response)
# 打印展示信息
print(response.read())
4. 請求處理方式get/post
請求常規(guī)處理方式GET/POST操作,通過兩個案例進(jìn)行操作宪卿,并通過抓包工具進(jìn)行數(shù)據(jù)分析:
# 添加注釋的诵,python2支持中文編碼
# -*- coding:utf-8 -*-
# 引入需要的模塊
from urllib2 import Request, urlopen
from urllib import urlencode
# 定義訪問url地址和傳遞的數(shù)據(jù)
url = "http://www.baidu.com/s?"
data = {
"wd": "火車票",
}
# 處理完整的請求
fullurl = url + urlencode(data)
# 構(gòu)建請求對象
request = Request(fullurl)
# 發(fā)送請求獲取響應(yīng)數(shù)據(jù)
response = urlopen(request)
# 打印 展示 響應(yīng)數(shù)據(jù)
print response.read()
POST請求的處理方式,我們通過有道在線翻譯詞典的操作來完成愧捕,首先打開瀏覽器訪問有道在線翻譯http://fanyi.youdao.com
奢驯,在翻譯頁面輸入詞語之后會自動翻譯,抓包分析訪問路徑可以得到真實路徑次绘;有道在線翻譯由于近年來被爬蟲頻繁操作瘪阁,所以做了簡單的反爬蟲措施,通過加密混淆參數(shù)和cookie標(biāo)記完成反爬蟲邮偎,我們簡單破解一下就可以了管跺,干貨如下:
# -*- coding:utf-8 -*-
from urllib2 import Request, urlopen
from urllib import urlencode
import time
import random
import hashlib
# 引入需要的模塊
url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
# 真實翻譯地址
n = raw_input("請輸入要翻譯的詞匯:")
c = "fanyideskweb"
x = "aNPG!!u6sesA>hBAW1@(-"
r = str(int(time.time() * 1000) + random.randint(1, 10))
# 處理加密字段
sign = hashlib.md5(c + n + r + x).hexdigest()
# 加密字段
headers = {
# "Host": "fanyi.youdao.com",
# "Content-Length": "201",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Origin": "http://fanyi.youdao.com",
# "X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "http://fanyi.youdao.com/",
# "Accept-Encoding": "gzip, deflate",
# "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Cookie": "OUTFOX_SEARCH_USER_ID_NCOO=1092450716.3850443; OUTFOX_SEARCH_USER_ID=-1512266810@10.168.1.241; JSESSIONID=aaal2mcR1N1zx5rcovkdw; fanyi-ad-id=39535; fanyi-ad-closed=1; ___rl__test__cookies=1515335942852"
}
# 請求頭設(shè)置
form_data = {
"i": n,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": c,
"salt": r,
"sign": sign,
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTIME",
"typoResult": "false",
}
data = urlencode(form_data)
# 請求的表單數(shù)據(jù)
request = Request(url, data=data, headers=headers)
# 構(gòu)建請求對象
response = urlopen(request)
# 訪問指定的url,得到響應(yīng)對象數(shù)據(jù)
print(response.read())
# 打印展示獲取的數(shù)據(jù)
5. 自定義Opener開鎖人
在前面的所有操作案例中禾进,都是直接使用urllib2模塊的操作函數(shù)進(jìn)行的處理豁跑,處理的方式一般都集中在HTTP或者HTTPS請求,那么urllib2.urlopen()底層具體做了什么樣的操作呢泻云?
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
cafile=None, capath=None, cadefault=False, context=None):
global _opener
if cafile or capath or cadefault:
if context is not None:
raise ValueError(
"You can't pass both context and any of cafile, capath, and "
"cadefault"
)
if not _have_ssl:
raise ValueError('SSL support not available')
context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH,
cafile=cafile,
capath=capath)
https_handler = HTTPSHandler(context=context)
opener = build_opener(https_handler)
elif context:
https_handler = HTTPSHandler(context=context)
opener = build_opener(https_handler)
elif _opener is None:
_opener = opener = build_opener()
else:
opener = _opener
return opener.open(url, data, timeout)
觀察底層代碼操作艇拍,無非就是通過HTTPSHandler創(chuàng)建了一個HTTPS協(xié)議的處理器對象狐蜕,然后通過build_opener()函數(shù)構(gòu)建了一個opener實現(xiàn)和指定服務(wù)器url地址之間的數(shù)據(jù)訪問操作。鑒于此卸夕,我們也可以自定義opener的實現(xiàn)過程:
# -*- coding:utf-8 -*-
import urllib2
# 引入需要的模塊
handler = urllib2.HTTPSHandler()
# 創(chuàng)建一個HTTPS處理器對象
opener = urllib2.build_opener(handler)
# 構(gòu)建一個開鎖人對象
response = opener.open("https://www.taobao.com")
# 訪問指定的數(shù)據(jù)
print response.read()
# 打印展示獲取的數(shù)據(jù)
可能大家對于上述代碼并沒有什么太多的感受层释,認(rèn)為我們就是通過手工編碼的方式,將官方底層源代碼重新實現(xiàn)了一次而已
在反爬蟲的操作過程中快集,有一種反爬蟲操作是針對出現(xiàn)異常訪問量的ip地址進(jìn)行封鎖的操作贡羔,這樣的情況下,你如果使用自己真實ip地址就很可能會導(dǎo)致自己的ip地址被封个初,再也不能訪問目標(biāo)數(shù)據(jù)了乖寒,此時~我們需要使用代理ip地址幫助我們實現(xiàn)對于目標(biāo)數(shù)據(jù)的訪問
代理ip地址的操作,主要處理和代理服務(wù)器之間的數(shù)據(jù)交互院溺,就需要使用到urllib2模塊中的代理操作對象ProxyHandler楣嘁,如果是常規(guī)的HTTPHandler/HTTPSHandler是不能滿足我們的需要的
# -*- coding:utf-8 -*-
# 引入需要的模塊
import urllib2
# 創(chuàng)建代理處理器
proxy_handler = urllib2.ProxyHandler({'http': '61.135.217.7:80'})
# 創(chuàng)建Opener對象
proxy_opener = urllib2.urlopen(proxy_handler)
# 打開指定的網(wǎng)址
response = proxy_opener.open("https://www.taobao.com")
# 打印展示獲取的數(shù)據(jù)
print response.read()
這里對于代理ip地址做一個簡要的說明
通常情況下,我們會看到代理服務(wù)器會有高匿覆获、匿名马澈、透明的一個描述瓢省,這個描述主要是用來作什么的呢弄息?其實主要是在代理服務(wù)器這一側(cè),通過變量控制被代理的客戶端是否對于訪問的服務(wù)器可見勤婚,主要通過三個參數(shù)進(jìn)行控制[REMOTE_ADDR摹量, HTTP_VIA,HTTP_X_FORWARDED_FOR]
透明代理:服務(wù)器明確知道你用了代理馒胆,你的ip和代理的ip服務(wù)器都能直接獲取
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:proxy_ip
- HTTP_X_FORWARDED_FOR:client_ip
匿名代理:服務(wù)器知道你用了代理缨称,可以獲取到代理服務(wù)器ip,但是獲取不到你的ip
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:proxy_ip
- HTTP_X_FORWARDED_FOR:proxy_ip
高匿代理:服務(wù)器根本不知道你是否代理訪問
- REMOTE_ADDR:proxy_ip
- HTTP_VIA:not datemined
- HTTP_X_FORWARDED_FOR:not datemined
盡管我們可以通過代理的方式來將我們自己隱藏在幕后進(jìn)行數(shù)據(jù)的采集工作祝迂,但是不得不說~免費的東西總是會存在一定的問題的睦尽,網(wǎng)絡(luò)上公布的所有的免費代理ip地址,穩(wěn)定性非常的差型雳,可持續(xù)性較低当凡,流量很小速度很慢并且存活時間較短
那么 ~, 有人看到了商機纠俭,專門有公司開始從事代理IP的操作沿量,設(shè)置各種私密代理IP,然后設(shè)置不同的限制規(guī)則冤荆,給爬蟲工程師或者其他需要的人提供代理服務(wù)朴则!
私密代理需要設(shè)置對應(yīng)的賬號和密碼的驗證操作,在實際操作過程中钓简,需要簡單的設(shè)置即可乌妒,和以后爬蟲其他高級使用方式基本是一致的汹想,在創(chuàng)建自定義Handler時進(jìn)行如下的操作
proxy_url = "188.68.16.55:80"
user = "damu"
passwd = "mu@!#13"
# format()函數(shù)和%符號一樣,都是通過占位符的方式替換數(shù)據(jù)的
proxy_handler = urllib2.ProxyHandler("http://{}:{}@{}".format(proxy_url, user, passwd))
6. 會話跟蹤之cookie操作
在很多網(wǎng)站上撤蚊,都使用了基于cookie的會話跟蹤技術(shù)欧宜,如有道在線翻譯的操作過程中就是用cookie進(jìn)行了狀態(tài)保持的操作;
在進(jìn)行爬蟲操作的過程中拴魄,我們會大量的使用到cookie的操作冗茸,對于cookie的操作,最原始的方式就是在請求頭中設(shè)置cookie字段匹中,通過抓包的數(shù)據(jù)給cookie設(shè)置固定的數(shù)據(jù)即可夏漱,但是在實際操作過程中,cookie數(shù)據(jù)如果動態(tài)發(fā)生變化時候就會變得異常繁瑣
python提供了cookielib模塊顶捷,可以很方便的實現(xiàn)cookie數(shù)據(jù)的讀寫操作挂绰,核心提供了CookieJar、FileCookieJar服赎、MozillaCookieJar等模塊實現(xiàn)對cookie的不同操作方式
服務(wù)器和客戶端的交互僅限于請求/響應(yīng)過程葵蒂,結(jié)束之后便斷開,在下一次請求時重虑,服務(wù)器會認(rèn)為新的客戶端践付。
為了維護(hù)他們之間的鏈接,讓服務(wù)器知道這是前一個用戶發(fā)送的請求缺厉,必須在一個地方保存客戶端的信息永高。
Cookie:通過在 客戶端 記錄的信息確定用戶的身份。
Session:通過在 服務(wù)器端 記錄的信息確定用戶的身份提针。
* 獲取訪問網(wǎng)站的cookie數(shù)據(jù)
-- coding:utf-8 --
引入需要的模塊
import urllib2
import cookielib
創(chuàng)建一個基于cookie的核心操作對象
cookie = cookielib.CookieJar()
創(chuàng)建一個基于cookie的操作對象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
創(chuàng)建一個可以操作cookie的opener對象
cookie_opener = urllib2.build_opener(cookie_handler)
打開目標(biāo)網(wǎng)站
response cookie_opener.open("http://fanyi.youdao.com")
查看cookie數(shù)據(jù)
for item in cookie:
print item.name + "--" + item.value
* 將訪問到的cookie數(shù)據(jù)存儲到文件中
-- coding:utf-8 --
引入需要的模塊
import urllib2
imorot cookielib
創(chuàng)建基于cookie的核心操作對象
cookie = urllib2.MozillaCookieJar("youdao.txt")
創(chuàng)建基于cookie的操作對象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
創(chuàng)建可以操作Cookie的opener對象
cookie_opener = urllib2.build_opener(cookie_handler)
訪問目標(biāo)網(wǎng)站
response = cookie_opener.open("http://fanyi.youdao.com")
保存cookie到文件中
cookie.save()
* 從文件中加載cookie數(shù)據(jù)
-- coding:utf-8 --
引入需要的模塊
import urllib2
import cookielib
創(chuàng)建基于cookie的核心對象
cookie = cookielib.CookieJar()
加載cookie文件中的數(shù)據(jù)
cookie.load("youdao.txt")
創(chuàng)建基于cookie的操作對象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
創(chuàng)建可以操作cookie的opener對象
cookie_opener = urllib2.build_opener(cookie_handler)
打開目標(biāo)地址
response = cookie_opener.open("http://fanyi.youdao.com")
..其他代碼請參考有道在線翻譯爬蟲程序
### 7\. 爬蟲異常行為
在進(jìn)行目標(biāo)網(wǎng)址的訪問和數(shù)據(jù)采集過程中命爬,由于目標(biāo)網(wǎng)址拼寫、網(wǎng)絡(luò)阻塞等等各種問題辐脖,可能會導(dǎo)致目標(biāo)網(wǎng)址訪問失敗的情況饲宛,在python爬蟲模塊中,主要通過URLError類型來規(guī)范定義爬蟲訪問url地址出現(xiàn)的問題嗜价,同時提供了繼承自URLError的HTTPError類型專門針對HTTP請求進(jìn)行的異常行為的處理
但是切記艇抠,一切服務(wù)器返回的異常行為,都是服務(wù)器內(nèi)部直接出現(xiàn)錯誤行為并且返回錯誤狀態(tài)碼導(dǎo)致的異常行為炭剪,如果服務(wù)器本身已經(jīng)處理了訪問的異常操作的話练链,爬蟲程序是不會得到異常數(shù)據(jù)的
* 訪問異常行為
-- coding:utf-8 --
引入需要的模塊
import urllib2
定義請求對象
requset = urllib2.Request("http://www.dailiyun.com/damu")
使用try-except包裹請求
try:
response = urllib2.urlopen(request)
print(response.read())
except URLError, err
print err.code
print err
print "程序運行完成"
最終運行的結(jié)果如下:
404
HTTP Error 404: Not Found
程序運行完成
* 訪問異常行為[服務(wù)器處理了異常]
-- coding:utf-8 --
引入需要的模塊
import urllib2
定義請求對象
requset = urllib2.Request("http://www.baidu.com/damu")
使用try-except包裹請求
try:
response = urllib2.urlopen(request)
print(response.read())
except URLError, err
print err.code
print err
print("程序運行完成")
最終執(zhí)行的結(jié)果如下:
<!DOCTYPE html>
<html>
...
<html>
程序運行完成
我們可以看到程序并沒有出現(xiàn)任何和異常行為相關(guān)的錯誤信息,因為百度的服務(wù)器已經(jīng)將404的異常行為在服務(wù)器中進(jìn)行了處理并且返回了指定的404網(wǎng)頁數(shù)據(jù)奴拦,所以爬蟲在訪問時獲取到了404網(wǎng)頁的數(shù)據(jù)
盡管一個完善的服務(wù)器端代碼可以將很多異常行為直接補貨并且處理掉媒鼓,將異常行為掐死在搖籃中給用戶提供更加友好的體驗,但是在實際爬蟲操作過程中,還是有大量的網(wǎng)站并沒有對異常訪問進(jìn)行處理绿鸣,所以我們要通過異常處理的方式得到異常訪問代碼疚沐,方便排查解決爬蟲遇到的各種問題!
* 特殊的異常行為:HTTPS數(shù)字簽名
所有的HTTPS安全網(wǎng)站潮模,都會有自己的數(shù)字簽名證書亮蛔,由第三方數(shù)字證書認(rèn)證中心發(fā)放并管理,主要目的是防止請求數(shù)據(jù)被惡意篡改和偽造擎厢,通過雙向非對稱加密的方式保證數(shù)據(jù)傳輸?shù)陌踩?
但是不是所有的網(wǎng)站都在第三方數(shù)字簽名認(rèn)證中心登記并頒發(fā)證書的究流,所以某些https開頭的網(wǎng)站在訪問的時候瀏覽器后提示不安全的信息提示,必須主動忽略警告信息才能正常訪問动遭,如[http://www.12306.cn](https://link.jianshu.com?t=http%3A%2F%2Fwww.12306.cn)就是如此芬探,我們通過如下程序進(jìn)行訪問時就會出錯:
import urllib2
request = urllib2.Request("https://www.12306.cn")
response = urllib2.urlopen(request)
print response.read()
出現(xiàn)如下的錯誤提示:
urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)>
這樣的情況下,我們必須手工操作忽略警告信息并繼續(xù)訪問厘惦,可以通過Python內(nèi)置的ssl模塊忽略數(shù)字簽名驗證訪問
import ssl
..
創(chuàng)建一個忽略警告的上下文對象
context = ssl._create_unverify_context()
..
將忽略驗證的環(huán)境包含在請求中發(fā)送
response = urllib2.urlopen(url, context=context)
..
### 跟蹤案例:爬取豆瓣電影上選電影中各個板塊的電影名稱和評分
**提示1:** [https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=40](https://link.jianshu.com?t=https%3A%2F%2Fmovie.douban.com%2Fexplore%23%21type%3Dmovie%26tag%3D%25E7%2583%25AD%25E9%2597%25A8%26sort%3Drecommend%26page_limit%3D20%26page_start%3D40)
**提示2:** 針對每個類型盡可能多的爬韧捣隆:熱門 最新 經(jīng)典 可播放 豆瓣高分 冷門佳片 華語 歐美 韓國 日本 動作 喜劇 愛情 科幻 懸疑 恐怖 文藝
**提示3:** 正則表達(dá)式篩選數(shù)據(jù)
**提示4:** 統(tǒng)計爬取的時間和數(shù)據(jù)量的大小