我所理解的爬蟲就是編寫程序,模擬瀏覽器發送請求,而後服務端把數據響應給我,而後我再對響應的數據作解析,拿到我想要的那一小部分,這就是整個爬蟲過程。提及來很簡單哈,其實否則,門戶網站是很不但願他們的數據被爬蟲程序拿到,有可能說有些不懷好意的人拿數據幹見不得人的勾當,或者說如今大數據時代,有數據才能說明一切,因此門戶網站也很珍惜他們的數據,不想那麼輕易被別人拿走,因而乎門戶網站的程序員就是開始設定不少規則來判斷髮送請求的是否是一個爬蟲程序了,若是是,就直接不給他數據,這就是反爬機制。俗話說「上有政策,下有對策」,難道就你門戶網站的人聰明嗎?你錯了,天外有天,人外有人,你覺得你那點小伎倆就能難倒我(確實有不少反爬策略很噁心),就算是大海撈針,我也要經過你的反爬機制,拿到數據,我在找出它的反爬機制和制定相應的對策的過程就叫反反爬。git
爬蟲總共分爲四部分,發送請求,獲取響應,解析數據,保存數據,以下圖:程序員
今天要說的就是利用requests模塊發送請求的過程,也是這整個過程當中最重要的一步,也應該是最困難的一步(純屬一家之言),由於上面所說的反爬和反反爬都在這其中。github
requests.get("http://httpbin.org/get") requests.post("http://httpbin.org/post") requests.put("http://httpbin.org/put") requests.delete("http://httpbin.org/delete") requests.head("http://httpbin.org/get") requests.options("http://httpbin.org/get")
咱們最經常使用的就是get和post請求,二者的區別就是get請求沒有請求體,而post請求有請求體
import requests
#這是請求的路徑,必須有 url='https://dig.chouti.com/'
#這是本次訪問攜帶的參數,發送請求時,它會以?name=hh的形式加在路徑後面,不是必需要有 params={'name':'hh'}
#這是請求的請求體,它是以鍵值對的形式存在的,也不是必需要有 headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'} #這是訪問攜帶的cookie數據
cookies='dshdjdsadhsaghgdasjjdasjdhasdashdjkhskad'
res = requests.get(url=url, headers=headers,params=params,cookies=cookies)
post請求和get請求是同樣的,只是多了一個請求體數據
#data和json都是請求體數據,data是字典類型的,json發送的是json字符串 data={'name':'hh','pwd':'12345'} json={"name":"hh","pwd":"12345"}
#請求體是以data形式發的,默認的contentType類型是urlencoded,若是請求體數據是以json形式發的,默認的contentType類型爲json res1=requests.post(url='http://httpbin.org/post', data={'name':'yuan'}) res2=requests.post(url='http://httpbin.org/post',json={'age':"22",})
對於post請求來講,除了比get請求多一個請求體之外,其餘用法如出一轍,get請求擁有的參數,post都擁有,好比說params參數,
若是在post請求裏面加一個params參數,他會和get請求同樣,會以?a=1的形式加在請求路徑後面
4.1 常見屬性ajax
import requests respone=requests.get('https://sh.lianjia.com/ershoufang/') respone.text #響應文本,自己是字節類型,但會以一種猜想的編碼格式幫你解碼成字符串 respone.content #響應文本,字節類型的文本 respone.status_code #響應的狀態碼 respone.headers #響應頭數據 respone.cookies #響應的cookie respone.cookies.get_dict() #響應的字典形式的cookie respone.cookies.items() #響應的元祖形式的cookie respone.url respone.history #若是請求過程當中發生了重定向,這會幫你記錄過程 respone.encoding #指定text的是用哪一種方式解碼
4.2 編碼問題json
剛纔上面講了,res.text會以一種猜想的編碼幫你解碼,但有時是不正確的編碼,因此致使拿到數據編碼會有問題,其實咱們能夠給text指定用哪一種編碼解碼
import requests
res=requests.get('http://www.baidu.com')
coding=res.apparent_encoding #這纔是拿到別人的編碼方式
res.encoding=coding #把別人的編碼方式賦給text的解碼方式
res.text #這樣解碼後的字符串就不會有問題了
4.3 對於圖片、視頻等字節類型文件瀏覽器
首先咱們直接用res.content就好了,其次是因爲文件太大,咱們不該該一下就全寫進文件裏,而是應該一段一段的寫進去,因而咱們就能夠for循環res.iter_content(),而後再一次一次的寫入
import requests response=requests.get('http://bangimg1.dahe.cn/forum/201612/10/200447p36yk96im76vatyk.jpg') with open("res.png","wb") as f: # f.write(response.content) # 好比下載視頻時,若是視頻100G,用response.content而後一會兒寫到文件中是不合理的 for line in response.iter_content(): f.write(line)
4.4針對接收json數據服務器
import requests import json response=requests.get('http://httpbin.org/get')
#咱們是能夠用反序列化本身手動處理json數據,其實這樣麻煩了 res1=json.loads(response.text)
#別人已經給咱們封裝好了一個方法res.json()這樣就直接幫你完成了反序列化的過程 res2=response.json()
4.5關於重定向cookie
在requests的一些列請求方式中,除了head方式,其餘的都會自動處理重定向,上面的屬性講了,能夠經過res.history能夠查看發送了那些重定向 咱們還能夠經過allow_redirects參數設置不讓他重定向 r = requests.get('http://github.com', allow_redirects=False)
User-Agent:請求載體身份標識,經過瀏覽器發起的請求,請求載體爲瀏覽器;使用爬蟲程序發起的請求,請求載體爲爬蟲程序,因此能夠經過判斷UA值判斷該請求是基於瀏覽器仍是爬蟲程序,門戶網站對訪問的UA進行判斷時,若是是爬蟲程序,就直接拒接提供數據session
反反爬策略就是把咱們的UA僞形成瀏覽器載體標識,咱們只須要在請求頭加上一個瀏覽器載體的UA就能夠了數據結構
headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' }
這樣就把請求體載體設置成了Google瀏覽器的UA標識
也就是說,不少網站要求登陸才能訪問,它的反爬機制是:首先你得訪問它的主頁面,而後從主頁面發送你得用戶名密碼到服務器,若是驗證成功後,它會自動幫你重定向到你得頁面,在這個過程,通常狀況下,在你第一次訪問它的主頁面時會返回給你一個cookie,而後你發送用戶名和密碼的路徑不是主頁面的路徑,而是另外的請求路徑,並且他要求的數據結構還不同。
反反爬策略:首先咱們用瀏覽器去訪問主頁面,找到登陸位置,輸入一組錯誤的數據,而後點‘檢查’,再點‘network’,把以前返回的包都清掉,而後點擊‘登陸’,這時候咱們就得去分析下剛纔接收的包,看看哪個是含有我用戶名和密碼的數據,這個差很少就是咱們要的包,再去看這個包請求的路徑和數據結構,這是第一步作分析;第二步就是寫爬蟲程序,通常在發送登陸數據的請求體裏會包含第一次訪問主頁面返回的cookie,因此代碼第一步是發送主頁面請求,獲取到返回的cookie,那這個cookie加上數據去訪問登陸的路徑就差很少了。
2.1 頁面分析
點擊登陸以後:
查看其餘數據
2.2 書寫爬蟲代碼
url1='https://www.douban.com/' #這是首頁的路徑 url2='https://accounts.douban.com/j/mobile/login/basic' #這是登陸發送請求的路徑 headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' } data={ #這是請求的數據 'ck': '', 'name': 'xxxxxx0', 'password':'hxxxxx', 'remember': 'false', 'ticket': ''} session=requests.session() session.get(url=url1,headers=headers) #這兩步就會把我訪問主頁面的cookie放在session裏面,從而我就不用把cookie取出來,下次直接用session發送請求就會自動帶上返回的cookie res=session.post(url=url2,headers=headers,data=data) #這是帶着返回的cookie向登陸路徑提交數據 print(res.text)
這整個過程就是通常登陸程序的爬蟲程序,在用session發送post請求時,若有重定向,會自動幫你重定向的,獲得的res是最後重定向後的結果
但這個豆瓣網有點不一樣,我獲得的res是一個json字符串,裏面有不少信息,
{"status":"success","message":"success","description":"處理成功","payload":{"account_info":{"name":"黃麻口","weixin_binded":true,"phone":"18839124390",
"avatar":{"medium":"https://img3.doubanio.com\/icon\/up192624730-1.jpg","median":"https://img3.doubanio.com\/icon\/us192624730-1.jpg","large":"https://img3.doubanio.com\/icon\/ul192624730-1.jpg",
"raw":"https://img3.doubanio.com\/icon\/ur192624730-1.jpg","small":"https://img3.doubanio.com\/icon\/u192624730-1.jpg","icon":"https://img3.doubanio.com\/icon\/ui192624730-1.jpg"},"id":"192624730",
"uid":"192624730"}}}
我猜想,這個登陸請求是用ajax發的,服務端驗證用戶成功後,返回一個json字符串,包含不少信息,好比第一個鍵值對‘status’:'success',ajax根據收到數據,再執行其餘操做,好比status等於success時,帶着cookie去訪問主頁面就能夠成功了,
但個人爬蟲程序不能實現ajax的重定向,因此咱們能夠用剛纔返回的cookie去訪問主頁面就能夠了,代碼改變以下:
session.post(url=url2,headers=headers,data=data) #把上面的post請求改爲這樣就好了
res=session.get(url=url1,headers=headers)
有些門戶網站會檢測某一段時間某個IP的訪問次數,若是訪問頻率太快,好比1分鐘訪問幾百次,這種顯然不是人爲手動點擊訪問,確定是爬蟲程序搞得,此時門戶網站就會禁止這個IP的訪問。
反反爬策略:就是使用代理IP,我每次訪問不用本身的IP,就算被封了,下次再換個IP就好了。總共分爲兩類:一是正向代理,代理客戶端獲取數據,爲了保護客戶端,防止被追究責任;而是反向代理,代理服務器提供數據,爲了保護服務器。網上的免費代理IP網站:‘快代理’
import requests import random if __name__ == "__main__": #不一樣瀏覽器的UA header_list = [ # 遨遊 {"user-agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)"}, # 火狐 {"user-agent": "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"}, # 谷歌 { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"} ] #不一樣的代理IP proxy_list = [ {"http": "112.115.57.20:3128"}, {'http': '121.41.171.223:3128'} ] #隨機獲取UA和代理IP header = random.choice(header_list) proxy = random.choice(proxy_list) url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip' #參數3:設置代理 response = requests.get(url=url,headers=header,proxies=proxy) 當沒有proxies參數時,就是用的本身的IP