基本原理

代理實際上指的就是代理伺服器,它的功能是代理網路使用者去取得網路資訊 。也可以說它是網路資訊的中轉站 。

在我們正常請求一個網站時, 是將請求傳送給 Web 伺服器,Web 伺服器把響應傳回給我們 。 如果設定了代理伺服器 , 實際上就是在本機和伺服器之間搭建了一個橋, 此時本機不是直接 向 Web 伺服器發起請求,而是向代理伺服器發出請求,請求會發送給代理伺服器,然後由代理伺服器再發送給 Web 伺服器,接著由代理伺服器再把 Web 伺服器返回的響應轉發給本機。 這樣我們同樣可以正常訪問網頁,但這個過程中 Web 伺服器識別出的真實 IP 就不再是我們本機的 IP 了,就成功實現了 IP 偽裝,解決爬蟲中封IP的難題。

瞭解代理伺服器的基本原理後,我們不禁會想到幾個問題,代理IP從何而來?如何保證代理可用性?代理如何儲存?如何使用這些代理?

獲取代理IP:

爬取網站的免費代理。比如西刺、快代理之類有免費代理的網站, 但是這些免費代理大多數情況下都是不好用的,所以比較靠譜的方法是購買付費代理。當然,如果你有更好的代理介面也可以自己接入。

檢測IP代理可用性:

因為免費代理大部分是不可用的,所以採集回來的代理IP不能直接使用,可以寫檢測程式不斷的去用這些代理訪問一個穩定的網站,看是否可以正常使用。

儲存代理IP:

儲存的代理IP首先要保證代理不重複 , 要檢測代理的可用情況,還要動態實時處理每個代理,本文利用來MongoDB儲存,當然也可用其他方式儲存。

使用代理:

最簡單的辦法就是用 API 來提供對外服務的介面 。

IP代理池設計

我們瞭解了代理池的四大問題,所以我們可以根據這四個問題去分析設計一個代理池框架,我們可以分成四個模組。分別是

獲取模組、檢測模組、儲存模組、介面模組 。

這樣不僅有利於我們的維護,也使得可以更高效的完成我們的需求。

架構圖

python構建IP代理池(Proxy Pool)

程式碼模組

在這裡只是簡單的寫出了程式碼模組的實現,並不完整不具有邏輯性,如想檢視獲取原始碼,請移步到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()