ADSL 撥號代理的搭建
From:崔慶才 - 輕鬆獲得海量穩定代理!ADSL撥號代理的搭建
我們嘗試維護過一個代理池。代理池可以挑選出許多可用代理,但是常常其穩定性不高、響應速度慢,而且這些代理通常是公共代理,可能不止一人同時使用,其IP被封的概率很大。另外,這些代理可能有效時間比較短,雖然代理池一直在篩選,但如果沒有及時更新狀態,也有可能獲取到不可用的代理。
如果要追求更加穩定的代理,就需要購買專有代理或者自己搭建代理伺服器。但是伺服器一般都是固定的IP,我們總不能搭建100個代理就用100臺伺服器吧,這顯然是不現實的。
所以,ADSL動態撥號主機就派上用場了。下面我們來了解一下ADSL撥號代理伺服器的相關設定。
一、什麼是ADSL
ADSL(Asymmetric Digital Subscriber Line,非對稱數字使用者環路),它的上行和下行頻寬不對稱,它採用分頻多工技術把普通的電話線分成了電話、上行和下行三個相對獨立的通道,從而避免了相互之間的干擾。
ADSL通過撥號的方式上網,需要輸入ADSL賬號和密碼,每次撥號就更換一個IP。IP分佈在多個A段,如果IP都能使用,則意味著IP量級可達千萬。如果我們將ADSL主機作為代理,每隔一段時間主機撥號就換一個IP,這樣可以有效防止IP被封禁。另外,主機的穩定性很好,代理響應速度很快。
二、準備工作
首先需要成功安裝Redis資料庫並啟動服務,另外還需要安裝requests、RedisPy、Tornado庫。
三、購買主機
我們先購買一臺動態撥號VPS主機,這樣的主機服務商相當多。在這裡使用了雲立方,官方網站:http://www.yunlifang.cn/dynamicvps.asp。
建議選擇電信線路。可以自行選擇主機配置,主要考慮頻寬是否滿足需求。
然後進入撥號主機的後臺,預裝一個作業系統,如下圖所示。
推薦安裝CentOS 7系統。
然後找到遠端管理皮膚-遠端連線的使用者名稱和密碼,也就是SSH遠端連線伺服器的資訊。比如我使用的IP和埠是153.36.65.214:20063,使用者名稱是root
。命令列下輸入如下程式碼:
ssh root@153.36.65.214 -p 20063
輸入管理密碼,就可以連線上遠端伺服器了。
進入之後,我們發現一個可用的指令碼檔案ppp.sh,這是撥號初始化的指令碼。執行此指令碼會提示輸入撥號的使用者名稱和密碼,然後它就開始各種撥號配置。一次配置成功,後面撥號就不需要重複輸入使用者名稱和密碼。
執行ppp.sh指令碼,輸入使用者名稱密碼等待它的配置完成,如下圖所示。
提示成功之後就可以進行撥號了。注意,在撥號之前測試ping任何網站都是不通的,因為當前網路還沒聯通。輸入如下撥號命令:
adsl-start
撥號命令成功執行,沒有報錯資訊,耗時約幾秒。接下來再去ping外網就可以通了。
如果要停止撥號,可以輸入如下指令:
adsl-stop
之後,可以發現又連不通網路了,如下圖所示。
斷線重播的命令就是二者組合起來,先執行adsl-stop
,再執行adsl-start
。每次撥號,ifconfig
命令觀察主機的IP,發現主機的IP一直在變化,網路卡名稱叫作ppp0,如下圖所示。
接下來,我們要做兩件事:一是怎樣將主機設定為代理伺服器,二是怎樣實時獲取撥號主機的IP。
四、設定代理伺服器
在Linux下搭建HTTP代理伺服器,推薦TinyProxy和Squid,配置都非常簡單。在這裡我們以TinyProxy為例來講解一下怎樣搭建代理伺服器。
1. 安裝 TinyProxy
第一步就是安裝TinyProxy軟體。在這裡我使用的系統是CentOS,所以使用yum來安裝。如果是其他系統如Ubuntu,可以選擇apt-get
等命令安裝。
命令列執行yum安裝指令:
yum install -y epel-release yum update -y yum install -y tinyproxy
2. 配置 TinyProxy
TinyProxy安裝完成之後還要配置一下才可以用作代理伺服器。我們需要編輯配置檔案,此檔案一般的路徑是/etc/tinyproxy/tinyproxy.conf。
可以看到一行程式碼:
Port 8888
在這裡可以設定代理的埠,埠預設是8888。
繼續向下找到如下程式碼:
Allow 127.0.0.1
這行程式碼表示被允許連線的主機IP。如果希望連線任何主機,那就直接將這行程式碼註釋即可。在這裡我們選擇直接註釋,也就是任何主機都可以使用這臺主機作為代理伺服器。
修改為如下程式碼:
# Allow 127.0.0.1
設定完成之後重啟TinyProxy即可:
systemctl enable tinyproxy.service systemctl restart tinyproxy.service
防火牆開放該埠:
iptables -I INPUT -p tcp --dport 8888 -j ACCEPT
當然如果想直接關閉防火牆也可以:
systemctl stop firewalld.service
這樣我們就完成了TinyProxy的配置。
3. 驗證 TinyProxy
首先,用ifconfig
檢視當前主機的IP。比如,當前我的主機撥號IP為112.84.118.216,在其他的主機執行測試一下。
用curl
命令設定代理請求httpbin,檢測代理是否生效。
curl -x 112.84.118.216:8888 httpbin.org/get
執行結果如下圖所示。
如果有正常的結果輸出,並且origin
的值為代理IP的地址,就證明TinyProxy配置成功了。
五、動態獲取IP
現在可以執行命令讓主機動態切換IP,也在主機上搭建了代理伺服器。我們只需要知道撥號後的IP就可以使用代理。
我們考慮到,在一臺主機撥號切換IP的間隙代理是不可用的,在這撥號的幾秒時間內如果有第二臺主機頂替第一臺主機,那就可以解決撥號間隙代理無法使用的問題了。所以我們要設計的架構必須要考慮支援多主機的問題。
假如有10臺撥號主機同時需要維護,而爬蟲需要使用這10臺主機的代理,那麼在爬蟲端維護的開銷是非常大的。如果爬蟲在不同的機器上執行,那麼每個爬蟲必須要獲得這10臺撥號主機的配置,這顯然是不理想的。
為了更加方便地使用代理,我們可以像上文的代理池一樣定義一個統一的代理介面,爬蟲端只需要配置代理介面即可獲取可用代理。要搭建一個介面,就勢必需要一臺伺服器,而介面的資料從哪裡獲得呢,當然最理想的還是選擇資料庫。
比如我們需要同時維護10臺撥號主機,每臺撥號主機都會定時撥號,那這樣每臺主機在某個時刻可用的代理只有一個,所以我們沒有必要儲存之前的撥號代理,因為重新撥號之後之前的代理已經不能用了,所以只需要將之前的代理更新其內容就好了。資料庫要做的就是定時對每臺主機的代理進行更新,而更新時又需要撥號主機的唯一標識,根據主機標識查出這條資料,然後將這條資料對應的代理更新。
所以資料庫端就需要儲存一個主機標識到代理的對映關係。那麼很自然地我們就會想到關係型資料庫如MySQL或者Redis的Hash儲存,只需儲存一個對映關係,不需要很多欄位,而且Redis比MySQL效率更高、使用更方便,所以最終選定的儲存方式就是Redis的Hash。
六、儲存模組
那麼接下來我們要做可被遠端訪問的Redis資料庫,各個撥號機器只需要將各自的主機標識和當前IP和埠(也就是代理)傳送給資料庫就好了。
先定義一個操作Redis資料庫的類,示例如下:
import redis
import random
# Redis資料庫IP
REDIS_HOST = 'remoteaddress'
# Redis資料庫密碼, 如無則填None
REDIS_PASSWORD = 'foobared'
# Redis資料庫埠
REDIS_PORT = 6379
# 代理池鍵名
PROXY_KEY = 'adsl'
class RedisClient(object):
def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, proxy_key=PROXY_KEY):
"""
初始化Redis連線
:param host: Redis 地址
:param port: Redis 埠
:param password: Redis 密碼
:param proxy_key: Redis 雜湊表名
"""
self.db = redis.StrictRedis(host=host, port=port, password=password, decode_responses=True)
self.proxy_key = proxy_key
def set(self, name, proxy):
"""
設定代理
:param name: 主機名稱
:param proxy: 代理
:return: 設定結果
"""
return self.db.hset(self.proxy_key, name, proxy)
def get(self, name):
"""
獲取代理
:param name: 主機名稱
:return: 代理
"""
return self.db.hget(self.proxy_key, name)
def count(self):
"""
獲取代理總數
:return: 代理總數
"""
return self.db.hlen(self.proxy_key)
def remove(self, name):
"""
刪除代理
:param name: 主機名稱
:return: 刪除結果
"""
return self.db.hdel(self.proxy_key, name)
def names(self):
"""
獲取主機名稱列表
:return: 獲取主機名稱列表
"""
return self.db.hkeys(self.proxy_key)
def proxies(self):
"""
獲取代理列表
:return: 代理列表
"""
return self.db.hvals(self.proxy_key)
def random(self):
"""
隨機獲取代理
:return:
"""
proxies = self.proxies()
return random.choice(proxies)
def all(self):
"""
獲取字典
:return:
"""
return self.db.hgetall(self.proxy_key)
這裡定義了一個RedisClient
類,在__init__()
方法中初始化了Redis連線,其中REDIS_HOST
就是遠端Redis的地址,REDIS_PASSWORD
是密碼,REDIS_PORT
是埠,PROXY_KEY
是儲存代理的雜湊表的鍵名。
接下來定義了一個set()
方法,這個方法用來向雜湊表新增對映關係。對映是從主機標識到代理的對映,比如一臺主機的標識為adsl1,當前的代理為118.119.111.172:8888,那麼雜湊表中就會儲存一個key為adsl1、value為118.119.111.172:8888的對映,Hash結構如下圖所示。
如果有多臺主機,只需要向Hash中新增對映即可。
另外,get()
方法就是從雜湊表中取出某臺主機對應的代理。remove()
方法則是從雜湊表中移除對應的主機的代理。還有names()
、proxies()
、all()
方法則是分別獲取雜湊表中的主機列表、代理列表及所有主機代理對映。count()
方法則是返回當前雜湊表的大小,也就是可用代理的數目。
最後還有一個比較重要的方法random()
,它隨機從雜湊表中取出一個可用代理,類似前面代理池的思想,確保每個代理都能被取到。
如果要對資料庫進行操作,只需要初始化RedisClient
物件,然後呼叫它的set()
或者remove()
方法,即可對雜湊表進行設定和刪除。
七、撥號模組
接下來要做的就是撥號,並把新的IP儲存到Redis雜湊表裡。
首先是撥號定時,它分為定時撥號和非定時撥號兩種選擇。
-
非定時撥號,最好的方法就是向該主機傳送一個訊號,然後主機就啟動撥號,但這樣做的話,我們首先要搭建一個重新撥號的介面,如搭建一個Web介面,請求該介面即進行撥號,但開始撥號之後,此時主機的狀態就從線上轉為離線,而此時的Web介面也就相應失效了,撥號過程無法再連線,撥號之後介面的IP也變了,所以我們無法通過介面來方便地控制撥號過程和獲取撥號結果,下次撥號還得改變撥號請求介面,所以非定時撥號的開銷還是比較大的。
-
定時撥號,我們只需要在撥號主機上執行定時指令碼即可,每隔一段時間撥號一次,更新IP,然後將IP在Redis雜湊表中更新即可,非常簡單易用,另外可以適當將撥號頻率調高一點,減少短時間內IP被封的可能性。
在這裡選擇定時撥號。
接下來就是獲取IP。獲取撥號後的IP非常簡單,只需要呼叫ifconfig
命令,然後解析出對應網路卡的IP即可。
獲取了IP之後,我們還需要進行有效性檢測。撥號主機可以自己檢測,比如可以利用requests設定自身的代理請求外網,如果成功,那麼證明代理可用,然後再修改Redis雜湊表,更新代理。
需要注意,由於在撥號的間隙撥號主機是離線狀態,而此時Redis雜湊表中還存留了上次的代理,一旦這個代理被取用了,該代理是無法使用的。為了避免這個情況,每臺主機在撥號之前還需要將自身的代理從Redis雜湊表中移除。
這樣基本的流程就理順了,我們用如下程式碼實現:
import re
import time
import requests
from requests.exceptions import ConnectionError, ReadTimeout
from db import RedisClient
# 撥號網路卡
ADSL_IFNAME = 'ppp0'
# 測試URL
TEST_URL = 'http://www.baidu.com'
# 測試超時時間
TEST_TIMEOUT = 20
# 撥號間隔
ADSL_CYCLE = 100
# 撥號出錯重試間隔
ADSL_ERROR_CYCLE = 5
# ADSL命令
ADSL_BASH = 'adsl-stop;adsl-start'
# 代理執行埠
PROXY_PORT = 8888
# 客戶端唯一標識
CLIENT_NAME = 'adsl1'
class Sender():
def get_ip(self, ifname=ADSL_IFNAME):
"""
獲取本機IP
:param ifname: 網路卡名稱
:return:
"""
(status, output) = subprocess.getstatusoutput('ifconfig')
if status == 0:
pattern = re.compile(ifname + '.*?inet.*?(\d+\.\d+\.\d+\.\d+).*?netmask', re.S)
result = re.search(pattern, output)
if result:
ip = result.group(1)
return ip
def test_proxy(self, proxy):
"""
測試代理
:param proxy: 代理
:return: 測試結果
"""
try:
response = requests.get(TEST_URL, proxies={
'http': 'http://' + proxy,
'https': 'https://' + proxy
}, timeout=TEST_TIMEOUT)
if response.status_code == 200:
return True
except (ConnectionError, ReadTimeout):
return False
def remove_proxy(self):
"""
移除代理
:return: None
"""
self.redis = RedisClient()
self.redis.remove(CLIENT_NAME)
print('Successfully Removed Proxy')
def set_proxy(self, proxy):
"""
設定代理
:param proxy: 代理
:return: None
"""
self.redis = RedisClient()
if self.redis.set(CLIENT_NAME, proxy):
print('Successfully Set Proxy', proxy)
def adsl(self):
"""
撥號主程式
:return: None
"""
while True:
print('ADSL Start, Remove Proxy, Please wait')
self.remove_proxy()
(status, output) = subprocess.getstatusoutput(ADSL_BASH)
if status == 0:
print('ADSL Successfully')
ip = self.get_ip()
if ip:
print('Now IP', ip)
print('Testing Proxy, Please Wait')
proxy = '{ip}:{port}'.format(ip=ip, port=PROXY_PORT)
if self.test_proxy(proxy):
print('Valid Proxy')
self.set_proxy(proxy)
print('Sleeping')
time.sleep(ADSL_CYCLE)
else:
print('Invalid Proxy')
else:
print('Get IP Failed, Re Dialing')
time.sleep(ADSL_ERROR_CYCLE)
else:
print('ADSL Failed, Please Check')
time.sleep(ADSL_ERROR_CYCLE)
def run():
sender = Sender()
sender.adsl()
在這裡定義了一個Sender
類,它的主要作用是執行定時撥號,並將新的IP測試通過之後更新到遠端Redis雜湊表裡。
主方法是adsl()
方法,它首先是一個無限迴圈,迴圈體內就是撥號的邏輯。
adsl()
方法首先呼叫了remove_proxy()
方法,將遠端Redis雜湊表中本機對應的代理移除,避免撥號時本主機的殘留代理被取到。
接下來利用subprocess模組來執行撥號指令碼,撥號指令碼很簡單,就是stop
之後再start
,這裡將撥號的命令直接定義成了ADSL_BASH
。
隨後程式又呼叫get_ip()
方法,通過subprocess模組執行獲取IP的命令ifconfig
,然後根據網路卡名稱獲取了當前撥號網路卡的IP地址,即撥號後的IP。
再接下來就需要測試代理有效性了。程式首先呼叫了test_proxy()
方法,將自身的代理設定好,使用requests庫來用代理連線TEST_URL
。在此TEST_URL
設定為百度,如果請求成功,則證明代理有效。
如果代理有效,再呼叫set_proxy()
方法將Redis雜湊表中本機對應的代理更新,設定時需要指定本機唯一標識和本機當前代理。本機唯一標識可隨意配置,其對應的變數為CLIENT_NAME
,保證各臺撥號主機不衝突即可。本機當前代理則由撥號後的新IP加埠組合而成。通過呼叫RedisClient
的set()
方法,引數name
為本機唯一標識,proxy
為撥號後的新代理,執行之後便可以更新雜湊表中的本機代理了。
建議至少配置兩臺主機,這樣在一臺主機的撥號間隙還有另一臺主機的代理可用。撥號主機的數量不限,越多越好。
在撥號主機上執行撥號指令碼,示例輸出如下圖所示。
首先移除了代理,再進行撥號,撥號完成之後獲取新的IP,代理檢測成功之後就設定到Redis雜湊表中,然後等待一段時間再重新進行撥號。
我們新增了多臺撥號主機,這樣就有多個穩定的定時更新的代理可用了。Redis雜湊表會實時更新各臺撥號主機的代理,如下圖所示。
圖中所示是四臺ADSL撥號主機配置並執行後的雜湊表的內容,表中的代理都是可用的。
八、介面模組
目前為止,我們已經成功實時更新撥號主機的代理。不過還缺少一個模組,那就是介面模組。像之前的代理池一樣,我們也定義一些介面來獲取代理,如random
獲取隨機代理、count
獲取代理個數等。
我們選用Tornado來實現,利用Tornado的Server模組搭建Web介面服務,示例如下:
import json
import tornado.ioloop
import tornado.web
from tornado.web import RequestHandler, Application
# API埠
API_PORT = 8000
class MainHandler(RequestHandler):
def initialize(self, redis):
self.redis = redis
def get(self, api=''):
if not api:
links = ['random', 'proxies', 'names', 'all', 'count']
self.write('<h4>Welcome to ADSL Proxy API</h4>')
for link in links:
self.write('<a href=' + link + '>' + link + '</a><br>')
if api == 'random':
result = self.redis.random()
if result:
self.write(result)
if api == 'names':
result = self.redis.names()
if result:
self.write(json.dumps(result))
if api == 'proxies':
result = self.redis.proxies()
if result:
self.write(json.dumps(result))
if api == 'all':
result = self.redis.all()
if result:
self.write(json.dumps(result))
if api == 'count':
self.write(str(self.redis.count()))
def server(redis, port=API_PORT, address=''):
application = Application([
(r'/', MainHandler, dict(redis=redis)),
(r'/(.*)', MainHandler, dict(redis=redis)),
])
application.listen(port, address=address)
print('ADSL API Listening on', port)
tornado.ioloop.IOLoop.instance().start()
這裡定義了5個介面,random
獲取隨機代理,names
獲取主機列表,proxies
獲取代理列表,all
獲取代理對映,count
獲取代理數量。
程式啟動之後便會在API_PORT埠上執行Web服務,主頁面如下圖所示。
訪問proxies
介面可以獲得所有代理列表,如下圖所示。
訪問random
介面可以獲取隨機可用代理,如下圖所示。
我們只需將介面部署到伺服器上,即可通過Web介面獲取可用代理,獲取方式和代理池類似。
九、本節程式碼
本節程式碼地址為:https://github.com/Python3WebSpider/AdslProxy。
十、結語
本節介紹了ADSL撥號代理的搭建過程。通過這種代理,我們可以無限次更換IP,而且線路非常穩定,抓取效果好很多。
相關文章
- ADSL撥號中出現的錯誤程式碼
- 海外代理HTTP和ADSL撥號有什麼區別?哪個更方便?HTTP
- adsl動態撥號伺服器是什麼伺服器
- Debian 下ADSL撥號及動態域名的使用(轉)
- slackware-10.1 下 adsl 撥號上網的 iptables 防火牆設定(轉)防火牆
- 安裝,配置rp-pppoe撥號軟體,使adsl成功上網(轉)
- android 撥號Android
- 撥號vps,遠端連線撥號vps的方法及其步驟
- adsl上網賬號及口令在哪裡看 adsl寬頻賬號密碼怎麼查詢密碼
- VC的撥號上網程式 (轉)
- HTML input tel 撥號域HTML
- 撥號上網程式 (轉)
- 混撥代理IP是什麼意思?
- Vps撥號伺服器,Vps撥號伺服器的優點及其連線方法伺服器
- win10撥號上網如何設定_win10設定撥號上網的步驟Win10
- 發起GPRS撥號請求
- FreeBSD撥號網路(轉)
- ubuntu kylin下寬頻撥號Ubuntu
- SCO Openserver 5.05撥號詳解 (轉)Server
- Proxypool代理池搭建
- Win11自動撥號的實現(教程)
- html5呼叫安卓或者ios的撥號功能HTML安卓iOS
- 電話撥號上網的基礎知識
- win10開機怎麼自動撥號_win10怎麼自動撥號上網Win10
- adsl伺服器,批次管理adsl伺服器的方法伺服器
- Linux下pppd撥號指令碼配置Linux指令碼
- win7如何建立撥號連線?Win7
- 電信貓撥號再加路由器路由器
- 在DELPHI程式中撥號上網 (轉)
- UNIX 系統上實現撥號(轉)
- 混撥vps與單城市撥號vps有什麼區別?
- 如何解決撥號連線關不掉的問題
- 如何搭建https代理?HTTP
- 電腦開機自動撥號上網怎麼設定?電腦自動撥號上網的設定方法步驟
- 什麼是撥號vps伺服器伺服器
- 如何連線vps撥號伺服器伺服器
- 如何選擇撥號VPS伺服器!伺服器
- FreeSWITCH測試撥號規則例項