設(shè)計思想:
一、具體網(wǎng)站具體對待惯悠,請求成功與否與網(wǎng)站的差異很大:
相同的代理不同的網(wǎng)站的會給予不同的反饋靠粪,有的網(wǎng)站能請求成功,但是有的網(wǎng)站卻不行;
所以在這里校驗器接受一個形參url茬底,增強對于不同網(wǎng)站的擴展性;
請求成功未必也能夠返回正確的數(shù)據(jù)获洲;這里有衍生到了校驗多種校驗方式:
二阱表、超時校驗:
此種校驗較為簡單,如果超時了贡珊,那么這個代理就不可用最爬,timeout即可判斷,在校驗器模塊內(nèi)集成超時異常判斷方法即可门岔;
三爱致、隱匿失敗校驗:
有些代理不是高匿代理,那么這些代理加了也和沒加差不多寒随,這里校驗的方式也較為簡單糠悯,為了確保自己是帶著套出去的,那么就得有個判斷妻往,這里校驗器接受一個形參ip逢防,這個形參ip指的是你的公網(wǎng)ip,校驗器內(nèi)部拿著這個ip和和代理查詢網(wǎng)站查詢的數(shù)據(jù)進行對比蒲讯,如果是一樣的那么說明隱匿失斖;提供一個比較好的查詢接口:
http://api.ipify.org
返回就是一個一個字符串形式的ip判帮,不含任何網(wǎng)頁標簽局嘁;
四、服務(wù)器響應(yīng)數(shù)據(jù)有效性的校驗:
有時候雖然你請求成功了晦墙,代理或許也是高匿的悦昵,但是,服務(wù)器返回的有可能一個報錯信息頁面比如 xxxxxx抱歉頁面無法訪問晌畅,所以這一塊就要校驗返回的數(shù)據(jù)是否包含你要的字段但指;
關(guān)于代理池:
最好動態(tài)刷新緩存
如果代理池是動態(tài)的:
需要考慮一個問題,上一次刷新的緩存要不要保留,在代理失敗切換代理的時候是不是直接刷新緩存棋凳;答案是肯定的拦坠,每一次失敗后都刷新緩存,道理很簡單剩岳,代理ip有的時效性非常短贞滨,上一秒有效的不一定下一秒有效;
一方面提供代理池的接口會有校驗拍棕,會過濾掉垃圾代理晓铆,最新返回的是經(jīng)過校驗有效的;
另一反面及時代理池接口沒有加經(jīng)過校驗,其返回的最新數(shù)據(jù)可用心也是比較大的绰播,越新越好骄噪;
提醒:此種每次失敗刷新緩存的方法,適用于接口沒有調(diào)用頻率限制的情況蠢箩。
如果代理池是靜態(tài)的:
還是會有一個問題腰池,是隨機獲取呢,還是遍歷呢忙芒?
隨機比較好,遍歷會有一個問題讳侨,打個比方呵萨,如果第一個代理是有效的,那么每回只會用第一個代理跨跨,因為校驗器設(shè)計上就是如果以上三個校驗都是沒毛病的就返回response;其他的都沒機會了潮峦,對于目標站點來說偽裝度就不夠了,失去了代理的價值勇婴,每次請求都是一個ip那不就被輕易識破了嗎忱嘹!
所以這里用random.choice(),每回都是隨機選一個
關(guān)于代理為空的情況:
解決思路:循環(huán)三次刷新緩存
如果代理池為空那么有兩種解決方式,一種是切換代理池耕渴,另一種是取消代理拘悦;
切換代理池:如果有備用代理池,那么最好是切換代理池了橱脸,
取消代理:如果沒有備用代理池础米,只能取消代理
設(shè)計實現(xiàn):
動態(tài)池:
嘗試失敗就刷新緩存
靜態(tài)池:
分為兩種,一種是純靜態(tài)添诉,一種是限制請求頻率的api代理池屁桑;這邊純靜態(tài)在代碼內(nèi)忽略,函數(shù)設(shè)置形參api接受api;對于限制請求評率的代理池栏赴,先循環(huán)遍歷每一個代理蘑斧,如果全部失敗,再次調(diào)用api遍歷,最多調(diào)用三次竖瘾,如果三次都失敗那么取消代理沟突;
外層循環(huán),
第0層校驗代理池是否為空准浴,如果為空循環(huán)3次刷新緩存事扭;再次失敗,返回無代理響應(yīng)
第一層校驗隱匿是否成功乐横,失敗后求橄,循環(huán)一個代理池的長度;再次失敗葡公,返回無代理響應(yīng)
第二層校驗超時罐农,失敗循環(huán)一個代理池的長度;再次失敗催什,返回無代理響應(yīng)
第三層校驗是否返回正確的數(shù)據(jù)涵亏,如果失敗循環(huán)一個代理池的長度;再次失敗蒲凶,返回無代理響應(yīng)
一共四層校驗
如果所有的校驗都失敗气筋,那么取消代理返回無代理響應(yīng)
提醒:這里的代理api是寫死的,因為不同的api返回的數(shù)據(jù)結(jié)構(gòu)不一樣旋圆,解析規(guī)則不同宠默,所以其他代理api調(diào)用該校驗器需要修改源碼的解析規(guī)則
import requests
import random
"""快代理接口"""
# 代理校驗類
class Proxy_Test:
# 接受ip,api,field,url,headers='',status=1留個參數(shù)
def __init__(self,ip,field,url,headers='',status=1):
# 您的公網(wǎng)ip
self.ip = ip
# 您調(diào)用代理池的api
self.api = 'http://dev.kdlapi.com/api/getproxy/?orderid=(你的賬戶id)&num=10&b_pcchrome=1&b_pcie=1&b_pcff=1&protocol=1&method=2&an_an=1&an_ha=1&sp1=1&sp2=1&sep=1'
# 您的代理池類型,動態(tài)還是靜態(tài)灵巧;接受參數(shù)類型搀矫,int, 1表示動態(tài)刻肄,0表示靜態(tài)
self.status = status
# 您的目標url源碼中瓤球,如果請求成功包含的字段;
self.field = field
# 目標站點的url
self.url = url
# 目標站點的請求頭
self.headers = headers
# 第二層校驗計數(shù)器
self.num2 = 0
# 靜態(tài)代理池敏弃,計數(shù)器卦羡,對刷新緩存次數(shù)計數(shù)
self.num3 = 1
# 代理校驗,返回response麦到,代理成功返回加代理的response,失敗則無代理response
def proxy_test(self):
status = 1
# 第一層校驗計數(shù)器
num1 = 0
# 第三層校驗計數(shù)器
num4 = 0
# 第四層校驗計數(shù)器
num5 = 0
while True:
# 刷新緩存
if status == 1:
# 返回一個帶有proxies key的json value 為列表虹茶,,隅要,蝴罪,這一塊是寫死的api
self.proxy_list = requests.get(self.api).text.split('\r\n')
# print(self.proxy_list)
try:
# 第一層校驗代理池是否為空,不為空返回一個隨機proxies
get_pool = self.get_pool()
proxies = get_pool['proxies']
length = get_pool['length']
print('ok')
# 判斷是否能低延時高匿成功
try:
# print('高匿不驗了')
# 第二層校驗步清,是否高匿成功
response_ip = requests.get('http://api.ipify.org',
proxies = proxies,
timeout=4,verify=False).text
# 校驗有沒有高匿成功,ip為您本機的公網(wǎng)ip
if response_ip == self.ip:
1 / 0
# 第三層校驗要门,是否超時
try:
response = self.wrapper_proxy(proxies)
# 第四層校驗,是否正確響應(yīng)
try:
content = response.text
# print(content)
if self.field in content:
print('恭喜虏肾,代理成功!')
li = [1,response]
return li
else:
1/0
except:
li = self.failed_retry(status, length, '正確響應(yīng)校驗')
if li:
return li
except:
li = self.failed_retry(status, length, '超時校驗')
if li:
return li
except:
li = self.failed_retry(status,length,'高匿')
if li:
return li
except:
# 循環(huán)三次刷新緩存
num1 += 1
if num1 > 3:
# 三次都失敗后欢搜,不使用代理返回response
print('代理池為空封豪,不使用代理')
response = self.remove_proxy()
li = [0,response]
return li
print("代理數(shù)據(jù)庫為空,循環(huán)刷新------第{}次".format(num1))
# 返回隨機proxy
def get_pool(self):
# 隨機選擇,如果是空拋出異常
proxy = random.choice(self.proxy_list)
# 代理池的數(shù)量炒瘟;
length = len(self.proxy_list)
# print('代理池長度{}'.format(length))
proxies = {'http': 'http://{}'.format(proxy)}
result = {'proxies':proxies,'length':length}
print(result)
return result
# 不設(shè)置代理返回response
def remove_proxy(self):
# 通過判斷headers是否傳遞來決定是否加上請求頭
if self.headers == '':
response = requests.get(self.url,timeout=20)
return response
else:
response = requests.get(url=self.url,headers = self.headers,timeout=20)
return response
# 添加代理請求
def wrapper_proxy(self,proxies):
# 通過判斷headers是否傳遞來決定是否加上請求頭
if self.headers == '':
response = requests.get(self.url,proxies = proxies,timeout=20)
return response
else:
response = requests.get(url=self.url,headers = self.headers,proxies=proxies,timeout=20)
return response
# 第二層以及以后的失敗重試
def failed_retry(self,status,length,level):
# 當失敗次數(shù)大于等于代理池的長度的時候吹埠,取消代理返回response
self.num2 += 1
print('{}失敗{}次'.format(level,self.num2))
# 如果是靜態(tài)代理池
if self.status == 0:
# 修改status狀態(tài),不刷新緩存
status = 0
# 遍歷完靜態(tài)池 有刷新三次緩存的機會,機會用完取消代理返回response
if status == 0:
if self.num2 >= length:
self.num3 += 1
if self.num3 < 4:
status = 1
self.num2 = 0
else:
print('{}失敗疮装,取消代理'.format(level))
response = self.remove_proxy()
li = [0,response]
return li
if status == 1:
if self.num2 >= length:
self.num2 = 0
print('{}失敗缘琅,取消代理'.format(level))
response = self.remove_proxy()
li = [0,response]
return li
if __name__ == '__main__':
# 接受ip,api,field,url,headers='',status=1留個參數(shù)
api = 'http://localhost:8899/api/v1/proxies?anonymous=true'
ip = '211.161.244.119'
field = '學習網(wǎng)絡(luò)爬蟲'
url = 'https://blog.csdn.net/winterto1990/article/details/51220307'
headers = {"Host":"blog.csdn.net",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36"
}
proxy = Proxy_Test(ip=ip,field=field,url=url,headers=headers)
response = proxy.proxy_test()[1]
print(response.text)