第20讲:代理的基本原理和用法
我們在做爬蟲的過程中經(jīng)常會遇到這樣的情況,最初爬蟲正常運行,正常抓取數(shù)據(jù),一切看起來都是那么的美好,然而一杯茶的功夫可能就會出現(xiàn)錯誤,比如 403 Forbidden,這時候打開網(wǎng)頁一看,可能會看到 “您的 IP 訪問頻率太高” 這樣的提示,或者跳出一個驗證碼讓我們輸入,輸入之后才可能解封,但是輸入之后過一會兒就又這樣了。
出現(xiàn)這種現(xiàn)象的原因是網(wǎng)站采取了一些反爬蟲的措施,比如服務(wù)器會檢測某個 IP 在單位時間內(nèi)的請求次數(shù),如果超過了這個閾值,那么會直接拒絕服務(wù),返回一些錯誤信息,這種情況可以稱之為封 IP,于是乎就成功把我們的爬蟲禁掉了。
既然服務(wù)器檢測的是某個 IP 單位時間的請求次數(shù),那么我們借助某種方式來偽裝我們的 IP,讓服務(wù)器識別不出是由我們本機發(fā)起的請求,不就可以成功防止封 IP 了嗎?所以這時候代理就派上用場了。
本課時我們先來看下代理的基本原理和使用代理處理反爬蟲的方法。
1.基本原理
代理實際上指的就是代理服務(wù)器,英文叫作 proxy server,它的功能是代理網(wǎng)絡(luò)用戶去獲取網(wǎng)絡(luò)信息。形象地說,它是網(wǎng)絡(luò)信息的中轉(zhuǎn)站。在我們正常請求一個網(wǎng)站時,是發(fā)送了請求給 Web 服務(wù)器,Web 服務(wù)器把響應(yīng)傳回給我們。如果設(shè)置了代理服務(wù)器,實際上就是在本機和服務(wù)器之間搭建了一個橋,此時本機不是直接向 Web 服務(wù)器發(fā)起請求,而是向代理服務(wù)器發(fā)出請求,請求會發(fā)送給代理服務(wù)器,然后由代理服務(wù)器再發(fā)送給 Web 服務(wù)器,接著由代理服務(wù)器再把 Web 服務(wù)器返回的響應(yīng)轉(zhuǎn)發(fā)給本機。這樣我們同樣可以正常訪問網(wǎng)頁,但這個過程中 Web 服務(wù)器識別出的真實 IP 就不再是我們本機的 IP 了,就成功實現(xiàn)了 IP 偽裝,這就是代理的基本原理。
2.代理的作用
那么,代理有什么作用呢?我們可以簡單列舉如下。
- 突破自身 IP 訪問限制,訪問一些平時不能訪問的站點。
- 訪問一些單位或團體內(nèi)部資源,如使用教育網(wǎng)內(nèi)地址段免費代理服務(wù)器,就可以用于對教育網(wǎng)開放的各類 FTP 下載上傳,以及各類資料查詢共享等服務(wù)。
- 提高訪問速度,通常代理服務(wù)器都設(shè)置一個較大的硬盤緩沖區(qū),當(dāng)有外界的信息通過時,也將其保存到緩沖區(qū)中,當(dāng)其他用戶再訪問相同的信息時, 則直接由緩沖區(qū)中取出信息,傳給用戶,以提高訪問速度。
- 隱藏真實 IP,上網(wǎng)者也可以通過這種方法隱藏自己的 IP,免受攻擊,對于爬蟲來說,我們用代理就是為了隱藏自身 IP,防止自身的 IP 被封鎖。
3.爬蟲代理
對于爬蟲來說,由于爬蟲爬取速度過快,在爬取過程中可能遇到同一個 IP 訪問過于頻繁的問題,此時網(wǎng)站就會讓我們輸入驗證碼登錄或者直接封鎖 IP,這樣會給爬取帶來極大的不便。
使用代理隱藏真實的 IP,讓服務(wù)器誤以為是代理服務(wù)器在請求自己。這樣在爬取過程中通過不斷更換代理,就不會被封鎖,可以達到很好的爬取效果。
4.代理分類
代理分類時,既可以根據(jù)協(xié)議區(qū)分,也可以根據(jù)其匿名程度區(qū)分,下面分別總結(jié)如下:
4.1根據(jù)協(xié)議區(qū)分
根據(jù)代理的協(xié)議,代理可以分為如下類別:
- FTP 代理服務(wù)器,主要用于訪問 FTP 服務(wù)器,一般有上傳、下載以及緩存功能,端口一般為 21、2121 等。
- HTTP 代理服務(wù)器,主要用于訪問網(wǎng)頁,一般有內(nèi)容過濾和緩存功能,端口一般為 80、8080、3128 等。
- SSL/TLS 代理,主要用于訪問加密網(wǎng)站,一般有 SSL 或 TLS 加密功能(最高支持 128 位加密強度),端口一般為 443。
- RTSP 代理,主要用于 Realplayer 訪問 Real 流媒體服務(wù)器,一般有緩存功能,端口一般為 554。
- Telnet 代理,主要用于 telnet 遠程控制(黑客入侵計算機時常用于隱藏身份),端口一般為 23。
- POP3/SMTP 代理,主要用于 POP3/SMTP 方式收發(fā)郵件,一般有緩存功能,端口一般為 110/25。
- SOCKS 代理,只是單純傳遞數(shù)據(jù)包,不關(guān)心具體協(xié)議和用法,所以速度快很多,一般有緩存功能,端口一般為 1080。SOCKS 代理協(xié)議又分為 SOCKS4 和 SOCKS5,SOCKS4 協(xié)議只支持 TCP,而 SOCKS5 協(xié)議支持 TCP 和 UDP,還支持各種身份驗證機制、服務(wù)器端域名解析等。簡單來說,SOCK4 能做到的 SOCKS5 都可以做到,但 SOCKS5 能做到的 SOCK4 不一定能做到。
4.2根據(jù)匿名程度區(qū)分
根據(jù)代理的匿名程度,代理可以分為如下類別。
- 高度匿名代理,高度匿名代理會將數(shù)據(jù)包原封不動的轉(zhuǎn)發(fā),在服務(wù)端看來就好像真的是一個普通客戶端在訪問,而記錄的 IP 是代理服務(wù)器的 IP。
- 普通匿名代理,普通匿名代理會在數(shù)據(jù)包上做一些改動,服務(wù)端上有可能發(fā)現(xiàn)這是個代理服務(wù)器,也有一定幾率追查到客戶端的真實 IP。代理服務(wù)器通常會加入的 HTTP 頭有 HTTP_VIA 和 HTTP_X_FORWARDED_FOR。
- 透明代理,透明代理不但改動了數(shù)據(jù)包,還會告訴服務(wù)器客戶端的真實 IP。這種代理除了能用緩存技術(shù)提高瀏覽速度,能用內(nèi)容過濾提高安全性之外,并無其他顯著作用,最常見的例子是內(nèi)網(wǎng)中的硬件防火墻。
- 間諜代理,間諜代理指組織或個人創(chuàng)建的,用于記錄用戶傳輸?shù)臄?shù)據(jù),然后進行研究、監(jiān)控等目的的代理服務(wù)器。
5.常見代理類型
使用網(wǎng)上的免費代理,最好使用高匿代理,使用前抓取下來篩選一下可用代理,也可以進一步維護一個代理池。
- 使用付費代理服務(wù),互聯(lián)網(wǎng)上存在許多代理商,可以付費使用,質(zhì)量比免費代理好很多。
- ADSL 撥號,撥一次號換一次 IP,穩(wěn)定性高,也是一種比較有效的解決方案。
- 蜂窩代理,即用 4G 或 5G 網(wǎng)卡等制作的代理,由于蜂窩網(wǎng)絡(luò)用作代理的情形較少,因此整體被封鎖的幾率會較低,但搭建蜂窩代理的成本較高。
6.代理設(shè)置
在前面我們介紹了多種請求庫,如 Requests、Selenium、Pyppeteer 等。我們接下來首先貼近實戰(zhàn),了解一下代理怎么使用,為后面了解代理池打下基礎(chǔ)。
下面我們來梳理一下這些庫的代理的設(shè)置方法。
做測試之前,我們需要先獲取一個可用代理。搜索引擎搜索 “代理” 關(guān)鍵字,就可以看到許多代理服務(wù)網(wǎng)站,網(wǎng)站上會有很多免費或付費代理,比如免費代理“快代理”:https://www.kuaidaili.com/free/。但是這些免費代理大多數(shù)情況下都是不好用的,所以比較靠譜的方法是購買付費代理。付費代理各大代理商家都有套餐,數(shù)量不用多,穩(wěn)定可用即可,我們可以自行選購。
如果本機有相關(guān)代理軟件的話,軟件一般會在本機創(chuàng)建 HTTP 或 SOCKS 代理服務(wù),本機直接使用此代理也可以。
在這里,我的本機安裝了一部代理軟件,它會在本地的 7890 端口上創(chuàng)建 HTTP 代理服務(wù),即代理為127.0.0.1:7890,另外還會在 7891 端口創(chuàng)建 SOCKS 代理服務(wù),即代理為 127.0.0.1:7891。
我只要設(shè)置了這個代理,就可以成功將本機 IP 切換到代理軟件連接的服務(wù)器的 IP 了。下面的示例里,我將使用上述代理來演示其設(shè)置方法,你也可以自行替換成自己的可用代理。設(shè)置代理后測試的網(wǎng)址是:http://httpbin.org/get,我們訪問該網(wǎng)址可以得到請求的相關(guān)信息,其中 origin 字段就是客戶端的 IP,我們可以根據(jù)它來判斷代理是否設(shè)置成功,即是否成功偽裝了 IP。
7.requests 設(shè)置代理
對于 requests 來說,代理設(shè)置非常簡單,我們只需要傳入 proxies 參數(shù)即可。
我在這里以我本機的代理為例,來看下 requests 的 HTTP 代理的設(shè)置,代碼如下:
import requests proxy = '127.0.0.1:7890' proxies = {'http': 'http://' + proxy,'https': 'https://' + proxy, } try:response = requests.get('https://httpbin.org/get', proxies=proxies)print(response.text) except requests.exceptions.ConnectionError as e:print('Error', e.args) 運行結(jié)果: {"args": {},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Host": "httpbin.org","User-Agent": "python-requests/2.22.0","X-Amzn-Trace-Id": "Root=1-5e8f358d-87913f68a192fb9f87aa0323"},"origin": "210.173.1.204","url": "https://httpbin.org/get" }可以發(fā)現(xiàn),我們通過一個字典的形式就設(shè)置好了 HTTP 代理,它分為兩個類別,有 HTTP 和 HTTPS,如果我們訪問的鏈接是 HTTP 協(xié)議,那就用 http 字典名指定的代理,如果是 HTTPS 協(xié)議,那就用 https 字典名指定的代理。
其運行結(jié)果的 origin 如是代理服務(wù)器的 IP,則證明代理已經(jīng)設(shè)置成功。
如果代理需要認證,同樣在代理的前面加上用戶名密碼即可,代理的寫法就變成如下所示:
proxy = 'username:password@127.0.0.1:7890'這里只需要將 username 和 password 替換即可。
如果需要使用 SOCKS 代理,則可以使用如下方式來設(shè)置:
import requests proxy = '127.0.0.1:7891' proxies = {'http': 'socks5://' + proxy,'https': 'socks5://' + proxy } try:response = requests.get('https://httpbin.org/get', proxies=proxies)print(response.text) except requests.exceptions.ConnectionError as e:print('Error', e.args)在這里,我們需要額外安裝一個包,這個包叫作 requests[socks],安裝命令如下所示:
pip3 install "requests[socks]"運行結(jié)果是完全相同的:
{"args": {},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Host": "httpbin.org","User-Agent": "python-requests/2.22.0","X-Amzn-Trace-Id": "Root=1-5e8f364a-589d3cf2500fafd47b5560f2"},"origin": "210.173.1.204","url": "https://httpbin.org/get" }另外,還有一種設(shè)置方式即使用 socks 模塊,也需要像上文一樣安裝 socks 庫。這種設(shè)置方法如下所示:
import requests import socks import socket socks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 7891) socket.socket = socks.socksocket try:response = requests.get('https://httpbin.org/get')print(response.text) except requests.exceptions.ConnectionError as e:print('Error', e.args)使用這種方法也可以設(shè)置 SOCKS 代理,運行結(jié)果完全相同。相比第一種方法,此方法是全局設(shè)置。我們可以在不同情況下選用不同的方法。
8.Selenium 設(shè)置代理
Selenium 同樣可以設(shè)置代理,在這里以 Chrome 為例來介紹下其設(shè)置方法。
對于無認證的代理,設(shè)置方法如下:
from selenium import webdriver proxy = '127.0.0.1:7890' options = webdriver.ChromeOptions() options.add_argument('--proxy-server=http://' + proxy) browser = webdriver.Chrome(options=options) browser.get('https://httpbin.org/get') print(browser.page_source) browser.close()運行結(jié)果如下:
{"args": {},"headers": {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","Accept-Encoding": "gzip, deflate","Accept-Language": "zh-CN,zh;q=0.9","Host": "httpbin.org","Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36","X-Amzn-Trace-Id": "Root=1-5e8f39cd-60930018205fd154a9af39cc"},"origin": "210.173.1.204","url": "http://httpbin.org/get" }代理設(shè)置成功,origin 同樣為代理 IP 的地址。
如果代理是認證代理,則設(shè)置方法相對比較麻煩,設(shè)置方法如下所示:
from selenium import webdriver from selenium.webdriver.chrome.options import Options import zipfileip = '127.0.0.1' port = 7890 username = 'foo' password = 'bar'manifest_json = """{"version":"1.0.0","manifest_version": 2,"name":"Chrome Proxy","permissions": ["proxy","tabs","unlimitedStorage","storage","<all_urls>","webRequest","webRequestBlocking"],"background": {"scripts": ["background.js"]} } """ background_js = """ var config = {mode: "fixed_servers",rules: {singleProxy: {scheme: "http",host: "%(ip) s",port: %(port) s}}}chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});function callbackFn(details) {return {authCredentials: {username: "%(username) s",password: "%(password) s"}} }chrome.webRequest.onAuthRequired.addListener(callbackFn,{urls: ["<all_urls>"]},['blocking'] ) """ % {'ip': ip, 'port': port, 'username': username, 'password': password}plugin_file = 'proxy_auth_plugin.zip' with zipfile.ZipFile(plugin_file, 'w') as zp:zp.writestr("manifest.json", manifest_json)zp.writestr("background.js", background_js) options = Options() options.add_argument("--start-maximized") options.add_extension(plugin_file) browser = webdriver.Chrome(options=options) browser.get('https://httpbin.org/get') print(browser.page_source) browser.close()這里需要在本地創(chuàng)建一個 manifest.json 配置文件和 background.js 腳本來設(shè)置認證代理。運行代碼之后本地會生成一個 proxy_auth_plugin.zip 文件來保存當(dāng)前配置。
運行結(jié)果和上例一致,origin 同樣為代理 IP。
SOCKS 代理的設(shè)置也比較簡單,把對應(yīng)的協(xié)議修改為 socks5 即可,如無密碼認證的代理設(shè)置方法為:
from selenium import webdriverproxy = '127.0.0.1:7891' options = webdriver.ChromeOptions() options.add_argument('--proxy-server=socks5://' + proxy) browser = webdriver.Chrome(options=options) browser.get('https://httpbin.org/get') print(browser.page_source) browser.close()運行結(jié)果是一樣的。
9.aiohttp 設(shè)置代理
對于 aiohttp 來說,我們可以通過 proxy 參數(shù)直接設(shè)置即可,HTTP 代理設(shè)置如下:
import asyncio import aiohttpproxy = 'http://127.0.0.1:7890'async def main():async with aiohttp.ClientSession() as session:async with session.get('https://httpbin.org/get', proxy=proxy) as response:print(await response.text())if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())如果代理有用戶名密碼,像 requests 一樣,把 proxy 修改為如下內(nèi)容:
proxy = 'http://username:password@127.0.0.1:7890'這里只需要將 username 和 password 替換即可。
對于 SOCKS 代理,我們需要安裝一個支持庫,叫作 aiohttp-socks,安裝命令如下:pip3 install aiohttp-socks
可以借助于這個庫的 ProxyConnector 來設(shè)置 SOCKS 代理,代碼如下:
運行結(jié)果是一樣的。
另外這個庫還支持設(shè)置 SOCKS4、HTTP 代理以及對應(yīng)的代理認證,可以參考其官方介紹。
10.Pyppeteer 設(shè)置代理
對于 Pyppeteer 來說,由于其默認使用的是類似 Chrome 的 Chromium 瀏覽器,因此設(shè)置方法和 Selenium 的 Chrome 是一樣的,如 HTTP 無認證代理設(shè)置方法都是通過 args 來設(shè)置,實現(xiàn)如下:
import asyncio from pyppeteer import launchproxy = '127.0.0.1:7890'async def main():browser = await launch({'args': ['--proxy-server=http://' + proxy], 'headless': False})page = await browser.newPage()await page.goto('https://httpbin.org/get')print(await page.content())await browser.close()if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())運行結(jié)果:
{"args": {},"headers": {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9","Host": "httpbin.org","Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3494.0 Safari/537.36","X-Amzn-Trace-Id": "Root=1-5e8f442c-12b1ed7865b049007267a66c"},"origin": "210.173.1.204","url": "https://httpbin.org/get" }同樣可以看到設(shè)置成功。
對于 SOCKS 代理,也是一樣的,只需要將協(xié)議修改為 socks5 即可,代碼實現(xiàn)如下:
import asyncio from pyppeteer import launchproxy = '127.0.0.1:7891'async def main():browser = await launch({'args': ['--proxy-server=socks5://' + proxy], 'headless': False})page = await browser.newPage()await page.goto('https://httpbin.org/get')print(await page.content())await browser.close()if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())運行結(jié)果也是一樣的。
11.總結(jié)
以上總結(jié)了各個庫的代理使用方式,以后如果遇到封 IP 的問題,我們就可以輕松通過加代理的方式來解決啦。
本節(jié)代碼:https://github.com/Python3WebSpider/ProxyTest。
總結(jié)
以上是生活随笔為你收集整理的第20讲:代理的基本原理和用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第31讲:抓包利器 Charles 的使
- 下一篇: 第19讲:Pyppeteer 爬取实战