基本原理
代理實際上指的就是代理伺服器,它的功能是代理網路使用者去取得網路資訊 。也可以說它是網路資訊的中轉站 。
在我們正常請求一個網站時, 是將請求傳送給 Web 伺服器,Web 伺服器把響應傳回給我們 。 如果設定了代理伺服器 , 實際上就是在本機和伺服器之間搭建了一個橋, 此時本機不是直接 向 Web 伺服器發起請求,而是向代理伺服器發出請求,請求會發送給代理伺服器,然後由代理伺服器再發送給 Web 伺服器,接著由代理伺服器再把 Web 伺服器返回的響應轉發給本機。 這樣我們同樣可以正常訪問網頁,但這個過程中 Web 伺服器識別出的真實 IP 就不再是我們本機的 IP 了,就成功實現了 IP 偽裝,解決爬蟲中封IP的難題。
瞭解代理伺服器的基本原理後,我們不禁會想到幾個問題,代理IP從何而來?如何保證代理可用性?代理如何儲存?如何使用這些代理?
獲取代理IP:
爬取網站的免費代理。比如西刺、快代理之類有免費代理的網站, 但是這些免費代理大多數情況下都是不好用的,所以比較靠譜的方法是購買付費代理。當然,如果你有更好的代理介面也可以自己接入。
檢測IP代理可用性:
因為免費代理大部分是不可用的,所以採集回來的代理IP不能直接使用,可以寫檢測程式不斷的去用這些代理訪問一個穩定的網站,看是否可以正常使用。
儲存代理IP:
儲存的代理IP首先要保證代理不重複 , 要檢測代理的可用情況,還要動態實時處理每個代理,本文利用來MongoDB儲存,當然也可用其他方式儲存。
使用代理:
最簡單的辦法就是用 API 來提供對外服務的介面 。
IP代理池設計
我們瞭解了代理池的四大問題,所以我們可以根據這四個問題去分析設計一個代理池框架,我們可以分成四個模組。分別是
獲取模組、檢測模組、儲存模組、介面模組 。
這樣不僅有利於我們的維護,也使得可以更高效的完成我們的需求。
架構圖
程式碼模組
在這裡只是簡單的寫出了程式碼模組的實現,並不完整不具有邏輯性,如想檢視獲取原始碼,請移步到GitHub:
獲取模組
import requests
import chardet
import traceback
from lxml import etree
class Downloader(object):
def __init__(self):
self。headers = {
‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/68。0。3440。106 Safari/537。36’
}
def download(self, url):
print(‘正在下載頁面:{}’。format(url))
try:
resp = requests。get(url, headers=self。headers)
resp。encoding = chardet。detect(resp。content)[‘encoding’]
if resp。status_code == 200:
return self。xpath_parse(resp。text)
else:
raise ConnectionError
except Exception:
print(‘下載頁面出錯:{}’。format(url))
traceback。print_exc()
def xpath_parse(self, resp):
try:
page = etree。HTML(resp)
trs = page。xpath(‘//div[@id=“list”]/table/tbody/tr’)
proxy_list = []
for tr in trs:
ip = tr。xpath(‘。/td[1]/text()’)[0]
port = tr。xpath(‘。/td[2]/text()’)[0]
proxy = {
‘proxy’: ip + ‘:’ + port
}
proxy_list。append(proxy)
return proxy_list
except Exception:
print(‘解析IP地址出錯’)
traceback。print_exc()
if __name__ == ‘__main__’:
print(Downloader()。download(‘https://www。kuaidaili。com/free/inha/1/’))
儲存模組
import pymongo
from pymongo。errors import DuplicateKeyError
class MongoDB(object):
def __init__(self):
self。client = pymongo。MongoClient()
self。db = self。client[‘proxypool3’]
self。proxies = self。db[‘proxies’]
self。proxies。ensure_index(‘proxy’, unique=True)
self。proxies。create_index()
# createIndex()
def insert(self, proxy):
try:
self。proxies。insert(proxy)
print(‘插入成功:{}’。format(proxy))
except DuplicateKeyError:
pass
def delete(self, conditions):
self。proxies。remove(conditions)
print(‘刪除成功:{}’。format(conditions))
def update(self, conditions, values):
self。proxies。update(conditions, {“$set”: values})
print(‘更新成功:{},{}’。format(conditions,values))
def get(self, count, conditions=None):
conditions = conditions if conditions else {}
count = int(count)
items = self。proxies。find(conditions, limit=count)。sort(‘delay’, pymongo。ASCENDING)
items = list(items)
return items
def get_count(self):
return self。proxies。count({})
if __name__ == ‘__main__’:
m = MongoDB()
print(m。get(3))
檢測模組
import requests
import time
import traceback
from requests。exceptions import ProxyError, ConnectionError
from db。mongo_db import MongoDB
from multiprocessing。pool import ThreadPool
def valid_many(proxy_list, method):
pool = ThreadPool(16)
for proxy in proxy_list:
pool。apply_async(valid_one, args=(proxy, method))
pool。close()
pool。join()
def valid_one(proxy, method, url=‘https://www。baidu。com’):
headers = {
‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/68。0。3440。106 Safari/537。36’
}
proxies = {
‘http’: ‘http://’ + proxy[‘proxy’],
‘https’: ‘http://’ + proxy[‘proxy’]
}
try:
start_time = time。time()
# requests。packages。urllib3。disable_warnings()
resp = requests。get(url, headers=headers, proxies=proxies, timeout=5, verify=False)
delay = round(time。time() - start_time, 2)
if resp。status_code == 200:
proxy[‘delay’] = delay
if method == ‘insert’:
MongoDB()。insert(proxy)
elif method == ‘check’:
MongoDB()。update({‘proxy’: proxy[‘proxy’]}, {‘delay’: proxy[‘delay’]})
else:
if method == ‘check’:
MongoDB()。delete({‘proxy’: proxy[‘proxy’]})
except (ProxyError, ConnectionError):
if method == ‘check’:
MongoDB()。delete({‘proxy’: proxy[‘proxy’]})
except Exception:
traceback。print_exc()
API介面模組
import flask
import json
from db。mongo_db import MongoDB
app = flask。Flask(__name__)
@app。route(‘/one’)
def get_one():
proxies = MongoDB()。get(1)
result = [proxy[‘proxy’] for proxy in proxies]
return json。dumps(result)
@app。route(‘/many’)
def get_many():
args = flask。request。args
proxies = MongoDB()。get(args[‘count’])
result = [proxy[‘proxy’] for proxy in proxies]
return json。dumps(result)
def run():
app。run()