掃碼或搜索: 進擊的Coder

發送

即可 立即永久 解鎖本站全部文章

你好,我是悅創。

本篇將開啓我自己啃代理池的心得,將逐步放送,因爲代理池搭建較爲複雜,這裏我就儘可能把代理池分成幾篇來講,同時也保證,在我其他篇放出來之前,每一篇都是你們的新知識。

學習就像看小說一樣,一次一篇就會顯得額外的輕鬆!

當你把學習當作某個娛樂的事情來做,你會發現不一樣的世界!

我們無法延長生命長度,但是我們延長生命寬度,學習編程就是擴展生命最有力的武器!

1. 看完之後你會得到什麼

  • 返回 yield;
  • eval 的使用;
  • 多個代理網站同時抓取;
  • 使用異步測試代理是否可用;
  • Python 的元類編程簡單介紹;
  • 正則表達式、PyQuery 提取數據;
  • 模塊化編程;

廢話不多說,馬上步入正題!

2. 你需要的準備

在學習本篇文章時,希望你已經具備如下技能或者知識點:

  1. Python 環境(推薦 Python 3.7+);
  2. Python 爬蟲常用庫;
  3. Python 基本語法;
  4. 面向對象編程;
  5. yield、eval 的使用;
  6. 模塊化編程;

3. 課前預習知識點

對於代理池的搭建呢,雖然我已經儘可能地照顧到絕大多數地小白,把代理地的搭建呢,進行了拆分,不過對於培訓機構或者自學不是特別精進的小夥伴來說還是有些難度的,對於這些呢?我這裏也給大家準備了知識點的掃盲。以下內容節選自我個人博客文章,這裏爲了掃盲就提供必要部分,想看完整的文章可以點擊此鏈接: https://www.aiyc.top/archives/605.html

3.1 Python 的元類編程

你好,我是悅創。

好久不見,最近在啃數學、Java、英語沒有來更新公衆號,那麼今天就來更新一下啦!

又到了每日一啃,啃代碼的小悅。今天我遇到了 Python 原類,然而我啥也不懂。只能靠百度和谷歌了,主要還是用谷歌來查啦,百度前幾條永遠是廣告準確度也不行(個人觀點),也順便參考了幾個博客:廖雪峯網站,添加了一點自己的觀點和理解。

3.1.1 type()

動態語言和靜態語言最大的不同,就是函數和類的定義,不是編譯時定義的,而是運行時動態創建的。

比方說我們要定義一個 Hello 的 class,就寫一個 hello.py 模塊(這裏我實際創建的是: the_test_code_project.py 模塊):

class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)

當 Python 解釋器載入 hello 模塊時,就會依次執行該模塊的所有語句,執行結果就是動態創建出一個 Hello 的 class 對象,測試如下:

if __name__ == '__main__':
h = Hello()
h.hello()
print(type(Hello))
print(type(h))

運行結果如下:

Hello, world.
<class 'type'>
<class '__main__.Hello'>

其中,上面的輸出結果: __main__.Hello 等價於 <class 'the_test_code_project.Hello'> 運行的方式不同顯示的方式也不同,但含義是一樣的。

type() 函數可以查看一個類型或變量的類型,爲了讓小白更輕鬆,我也寫了個例子:

number = 12
string = 'Hello AIYC!'
float_number = 12.1
list_data = [1, '2', 3.5, 'AIYC'] # 可變
tuples = (1, '2', 3.5, 'AIYC') # 不可變
 
if __name__ == '__main__':
print(type(number))
print(type(string))
print(type(float_number))
print(type(list_data))
print(type(tuples))

運行結果:

<class 'int'>
<class 'str'>
<class 'float'>
<class 'list'>
<class 'tuple'>

Hello 是一個 class,它的類型就是 type ,而 h 是一個實例,它的類型就是class Hello

我們說 class 的定義是運行時動態創建的,而創建 class 的方法就是使用 type() 函數。

type() 函數既可以返回一個對象的類型,又可以創建出新的類型,比如,我們可以通過 type() 函數創建出 Hello 類,而無需通過 class Hello(object)... 的定義:

def fn(self, name='world'): # 先定義函數
print('Hello, %s.' % name)
 
Hello = type('Hello', (object,), dict(hello=fn)) # 創建 Hello class
# Hello = type('Class_Name', (object,), dict(hello=fn)) # 創建 Hello class
# type(類名, 父類的元組(針對繼承的情況,可以爲空),包含屬性的字典(名稱和值))

我們接下來來調用一下代碼,看輸出的結果如何:

if __name__ == '__main__':
h = Hello()
h.hello()
print(type(Hello))
print(type(h))

這裏推薦寫成: if __name__ == '__main__': 使代碼更加的規範。

運行結果:

Hello, world.
<class 'type'>
<class '__main__.Hello'>

要創建一個 class 對象, type() 函數依次傳入 3 個參數:

  1. class 的名稱;
  2. 繼承的父類集合,注意 Python 支持多重繼承,如果只有一個父類,別忘了 tuple 的單元素寫法;(這個個 tuple 單元素寫法起初本人不太理解,然後一查並認真觀察了一下上面的代碼就想起來 tuple 單元素寫法需要加逗號(,),就是你必須這麼寫: tuple_1 = (1,) 而不能這麼寫: tuple_2 = (1)tuple_2 = (1) 的寫法,Python 會自動認爲是一個整數而不是一個元組)
  3. class 的方法名稱與函數綁定,這裏我們把函數 fn 綁定到方法名 hello 上。

通過 type() 函數創建的類和直接寫 class 是完全一樣的,因爲 Python 解釋器遇到 class 定義時,僅僅是掃描一下 class 定義的語法,然後調用 type() 函數創建出 class。(直接 Class 創建也是)

正常情況下,我們都用 class Xxx... 來定義類,但是, type() 函數也允許我們動態創建出類來,也就是說,動態語言本身支持運行期動態創建類,這和靜態語言有非常大的不同,要在靜態語言運行期創建類,必須構造源代碼字符串再調用編譯器,或者藉助一些工具生成字節碼實現,本質上都是動態編譯,會非常複雜。

3.1.2 metaclass

除了使用 type() 動態創建類以外,要控制類的 創建行爲 ,還可以使用 metaclass。

metaclass,直譯爲元類,簡單的解釋就是:

  • 當我們定義了類以後,就可以根據這個類創建出實例,所以:先定義類,然後創建實例。

  • 但是如果我們想創建出類呢?

那就必須根據 metaclass 創建出類,所以:先定義 metaclass ,然後創建類。連接起來就是:先定義 metaclass ,就可以創建類,最後創建實例。

所以, metaclass 允許你創建類或者修改類 。換句話說,你可以把類看成是 metaclass 創建出來的“實例”。

metaclass 是 Python 面向對象裏最難理解,也是最難使用的魔術代碼。正常情況下,你不會碰到需要使用metaclass的情況,所以,以下內容看不懂也沒關係,因爲基本上你不會用到。(然而還是被我遇見了,而且還是看不懂,但經過大佬的指點就只是知道如何使用,但並不瞭解其中的原理,所以纔有了此篇。)

我們先看一個簡單的例子,這個 metaclass 可以給我們自定義的 MyList 增加一個 add 方法:

class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value) # 加上新的方法
return type.__new__(cls, name, bases, attrs) # 返回修改後的定義

定義 ListMetaclass ,按照默認習慣,metaclass 的類名總是以 Metaclass 結尾,以便清楚地表示這是一個metaclass 。

有了 ListMetaclass ,我們在定義類的時候還要指示使用 ListMetaclass 來定製類,傳入關鍵字參數 metaclass

class MyList(list, metaclass=ListMetaclass):
pass

當我們傳入關鍵字參數 metaclass 時,魔術就生效了,它指示 Python 解釋器在創建 MyList 時,要通過 ListMetaclass.__new__() 來創建,在此,我們可以修改類的定義,比如,加上新的方法,然後,返回修改後的定義。

__new__() 方法接收到的參數依次是:

  1. 當前準備創建的類的對象;
  2. 類的名字;
  3. 類繼承的父類集合;
  4. 類的方法集合。

測試一下 MyList 是否可以調用 add() 方法:

L = MyList()
L.add(1)
print(L)
 
# 輸出
[1]

而普通的 list 沒有 add() 方法:

>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'

這時候,我想你應該和我會有同樣的問題,動態修改有什麼意義?

直接在 MyList 定義中寫上 add() 方法不是更簡單嗎?

正常情況下,確實應該直接寫,我覺得通過 metaclass 修改純屬變態。

3.2 Python eval() 函數

對於 Python 的 eval 呢,大部分人是這麼定義的:eval() 函數用來執行一個字符串表達式,並返回表達式的值。

這句話,有可能對於新手來說並不是非常好理解,我們還是用例子來頓悟一下吧。

以下是 eval() 方法的語法:

eval(expression[, globals[, locals]])

參數

  • expression — 表達式。
  • globals — 變量作用域,全局命名空間,如果被提供,則必須是一個字典對象。
  • locals — 變量作用域,局部命名空間,如果被提供,可以是任何映射對象。

返回值

返回表達式計算結果。

實際操作

In [3]: x = 7
 
In [4]: eval( '3 * x' )
Out[4]: 21
 
In [5]: eval('pow(2,2)')
Out[5]: 4
 
In [6]: eval('2 + 2')
Out[6]: 4
 
In [7]: n=81
 
In [8]: eval("n + 4")
Out[8]: 85

再來個函數的操作:

In [1]: str1 = "print('Hello World')"
 
In [2]: eval(str1)
Hello World

ok,到此零基礎小白關懷文章就完成了,我就不繼續贅述啦!

看到這裏,你們的身體還行嗎?

我正在的乾貨要開始了!

進入我們的 show time 環節,起飛了、起飛了,做好準備了!

4. 抓取免費代理

這裏呢,我就不再帶帶大家手摸手的教學如何抓取代理了,因爲當你能看這篇文章時,相信你已經對爬蟲是有所入門了,如果還沒入門的小夥伴可以關注本公衆號,往期文章也有零基礎教學和基礎的課程資源,可以公衆號後臺回覆:Python爬蟲。即可獲取資源,如果失效也別擔心加小悅好友即可!(資源多多噢!)

小白不建議報名培訓機構,畢竟現在培訓結構收智商稅比較多,還是需要多多鑑別噢!

4.1 目標的代理網站

  1. 快代理: https://www.kuaidaili.com/
  2. 西刺代理: https://www.xicidaili.com
  3. 66代理: http://www.66ip.cn/
  4. 無憂代理: http://www.data5u.com
  5. 開心代理-高匿: http://www.kxdaili.com/dailiip/
  6. 雲代理: http://www.ip3366.net/free/

對於每個網站的代碼呢,具體實現也是比較簡單的,這裏我就不做過多的贅述啦!

接下來我都直接上代碼,不過在上代理的爬蟲代碼前,我們需要來寫個元類,代碼如下:

class ProxyMetaclass(type):
"""
定義 ProxyMetaclass ,按照默認習慣,metaclass 的類名總是以 Metaclass 結尾,以便清楚地表示這是一個metaclass :
元類,在 FreeProxyGetter 類中加入
CrawlFunc_list 和 CrawlFuncCount
兩個參數,分別表示爬蟲函數,和爬蟲函數的數量。
"""
def __new__(cls, name, bases, attrs):
count = 0
attrs['CrawlFunc_List'] = [] # 添加:CrawlFunc_List 列表方法
for k, v in attrs.items():
if 'crawl_' in k:
# 判斷這個函數里面是否攜帶 crawl_ 也就是利用 value in xxx
attrs['CrawlFunc_List'].append(k)
count += 1
attrs['CrawlFuncCount'] = count # 檢測添加的函數的總數量
return type.__new__(cls, name, bases, attrs) # 返回所修改的

詳細的代碼含義,已經寫在上面了,具體的這裏 就不贅述了,如果泥有任何不理解的可以去點擊閱讀原文在我的博客網站下留言,和後臺回覆數字“3”,加小編好友,拉你入交流羣。

4.2 代碼編寫

4.2.1 請求函數編寫

單獨保存成一個文件: utils.py

"""
project = 'Code', file_name = 'utils.py', author = 'AI悅創'
time = '2020/6/1 12:34', product_name = PyCharm, 公衆號:AI悅創
code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
"""
import requests
import asyncio
import aiohttp
from requests.exceptions import ConnectionError
from fake_useragent import UserAgent,FakeUserAgentError
import random
 
def get_page(url, options={}):
"""
構造隨機請求頭,如果不理解的可以閱讀此文章:
兩行代碼設置 Scrapy UserAgent:https://www.aiyc.top/archives/533.html
"""
try:
ua = UserAgent()
except FakeUserAgentError:
pass
# 生成隨機的請求頭,加 try...except... 使代碼更加健壯
base_headers = {
'User-Agent': ua.random,
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8'
}
# 如果使用者有傳入請求頭,則將此請求頭和隨機生成的合成在一起
headers = dict(base_headers, **options)
# 當前請求的 url
print('Getting', url)
try:
r = requests.get(url, headers=headers)
print('Getting result', url, r.status_code)
if r.status_code == 200:
return r.text
return None
except ConnectionError:
print('Crawling Failed', url)
return None
 
class Downloader(object):
"""
一個異步下載器,可以對代理源異步抓取,但是容易被 BAN。
self._htmls: 把請求的 html 放入列表當中
升級版的請求類
"""
 
def __init__(self, urls):
self.urls = urls
self._htmls = []
 
async def download_single_page(self, url):
"""
下載單頁
"""
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
self._htmls.append(await response.text())
 
def download(self):
loop = asyncio.get_event_loop()
tasks = [self.download_single_page(url) for url in self.urls]
loop.run_until_complete(asyncio.wait(tasks))
 
@property
def htmls(self):
self.download()
return self._htmls

上面請求函數編寫完成之後,我們就需要在抓取代理的代碼文件中,進行導包,代碼如下:

# files:Spider.py
from utils import get_page

4.2.2 代理抓取代碼

導入所需要的庫:

import requests
from utils import get_page
from pyquery import PyQuery as pq
import re

接下來我們需要創建一個類 FreeProxyGetter 並使用元類創建。

class FreeProxyGetter(object, metaclass=ProxyMetaclass):
pass

1. 快代理

def crawl_kuaidaili(self):
"""
快代理
:return:
"""
for page in range(1, 4):
# 國內高匿代理
start_url = 'https://www.kuaidaili.com/free/inha/{}/'.format(page)
html = get_page(start_url)
# print(html)
pattern = re.compile(
'<td data-title="IP">(.*)</td>s*<td data-title="PORT">(w+)</td>'
)
# s * 匹配空格,起到換行作用
# ip_addres = re.findall(pattern, html) # 寫法一
ip_addres = pattern.findall(str(html))
for adress, port in ip_addres:
# print(adress, port)
result = f"{adress}:{port}".strip()
yield result

2. 西刺代理

def crawl_xicidaili(self):
"""
西刺代理
:return:
"""
for page in range(1, 4):
start_url = 'https://www.xicidaili.com/wt/{}'.format(page)
html = get_page(start_url)
# print(html)
ip_adress = re.compile(
'<td class="country"><img src="//fs.xicidaili.com/images/flag/cn.png" alt="Cn" /></td>s*<td>(.*?)</td>s*<td>(.*?)</td>'
)
# s* 匹配空格,起到換行作用
re_ip_adress = ip_adress.findall(str(html))
for adress, port in re_ip_adress:
result = f"{adress}:{port}".strip()
yield result

3. 66代理

def crawl_daili66(self, page_count=4):
"""
66代理
:param page_count:
:return:
"""
start_url = 'http://www.66ip.cn/{}.html'
urls = [start_url.format(page) for page in range(1, page_count + 1)]
for url in urls:
print('Crawling', url)
html = get_page(url)
if html:
doc = pq(html)
trs = doc('.containerbox table tr:gt(0)').items()
for tr in trs:
ip = tr.find('td:nth-child(1)').text()
port = tr.find('td:nth-child(2)').text()
yield ':'.join([ip, port])

4. 無憂代理

def crawl_data5u(self):
"""
無憂代理
:return:
"""
start_url = 'http://www.data5u.com'
html = get_page(start_url)
# print(html)
ip_adress = re.compile(
'<ul class="l2">s*<span><li>(.*?)</li></span>s*<span style="width: 100px;"><li class=".*">(.*?)</li></span>'
)
# s * 匹配空格,起到換行作用
re_ip_adress = ip_adress.findall(str(html))
for adress, port in re_ip_adress:
result = f"{adress}:{port}"
yield result.strip()

5. 開心代理-高匿

def crawl_kxdaili(self):
"""
開心代理-高匿
:return:
"""
for i in range(1, 4):
start_url = 'http://www.kxdaili.com/dailiip/1/{}.html'.format(i)
try:
html = requests.get(start_url)
if html.status_code == 200:
html.encoding = 'utf-8'
# print(html.text)
ip_adress = re.compile('<tr.*?>s*<td>(.*?)</td>s*<td>(.*?)</td>')
# s* 匹配空格,起到換行作用
re_ip_adress = ip_adress.findall(str(html.text))
for adress, port in re_ip_adress:
result = f"{adress}:{port}"
yield result.strip()
return None
except:
pass

6. 雲代理

def IP3366Crawler(self):
"""
雲代理
parse html file to get proxies
:return:
"""
start_url = 'http://www.ip3366.net/free/?stype=1&page={page}'
urls = [start_url.format(page=i) for i in range(1, 8)]
# s * 匹配空格,起到換行作用
ip_address = re.compile('<tr>s*<td>(.*?)</td>s*<td>(.*?)</td>')
for url in urls:
html = get_page(url)
re_ip_address = ip_address.findall(str(html))
for adress, port in re_ip_address:
result = f"{adress}:{port}"
yield result.strip()

至此,代理網站的代碼已經全部編寫完成,接下來我們需要了解的是然後調用此類,直接調用嗎?

顯然不是,我們還需要編寫一個運行調用的函數,代碼如下:

def run(self):
# print(self.__CrawlFunc__)
proxies = []
callback = self.CrawlFunc_List
for i in callback:
print('Callback', i)
for proxy in eval("self.{}()".format(i)):
print('Getting', proxy, 'from', i)
proxies.append(proxy)
return proxies

以上代理的代碼中,我並沒有抓取每個網站全部頁數,如果有需要可以自行調節。

這裏我們可以直接寫一個調用代碼,來測試代碼是否正常,寫在: Spider.py 也就是代理的代碼中,代碼如下:

if __name__ == '__main__':
Tester = FreeProxyGetter()
Tester.run()

運行結果如下:

結果較多,省略大部分結果。

Getting 14.115.70.177:4216 from crawl_xicidaili
Getting 116.22.48.220:4216 from crawl_xicidaili
Getting 223.241.3.120:4216 from crawl_xicidaili
......
Getting 60.188.1.27:8232 from crawl_data5u
Callback crawl_kxdaili
Getting 117.71.165.208:3000 from crawl_kxdaili
Getting 223.99.197.253:63000 from crawl_kxdaili
Getting 60.255.186.169:8888 from crawl_kxdaili
Getting 180.168.13.26:8000 from crawl_kxdaili
Getting 125.124.51.226:808 from crawl_kxdaili
Getting 47.99.145.67:808 from crawl_kxdaili
Getting 222.186.55.41:8080 from crawl_kxdaili
Getting 36.6.224.30:3000 from crawl_kxdaili
Getting 110.43.42.200:8081 from crawl_kxdaili
Getting 39.137.69.8:80 from crawl_kxdaili

ok,至此代理成功抓取,接下來就是我們的異步檢測了。

4.2.3 異步檢測

這裏,我們需要編寫一個 error.py 來自定義包:

class ResourceDepletionError(Exception):
 
def __init__(self):
Exception.__init__(self)
 
def __str__(self):
return repr('The proxy source is exhausted')
 
class PoolEmptyError(Exception):
 
def __init__(self):
Exception.__init__(self)
 
def __str__(self):
return repr('The proxy pool is empty')

異步檢測代碼,這裏我就不做任何數據存儲,有需求的可以自行添加,下一篇將添加一個存入 redis 數據庫的,敬請關注!

# files:schedule.py
import asyncio
import time
from multiprocessing.context import Process
 
import aiohttp
from db import RedisClient
from Spider import FreeProxyGetter
from error import ResourceDepletionError
from aiohttp import ServerDisconnectedError, ClientResponseError, ClientConnectorError
from socks import ProxyConnectionError
 
get_proxy_timeout = 9
 
class ValidityTester(object):
"""
代理的有效性測試
"""
def __init__(self):
self.raw_proxies = None
self.usable_proxies = [] # 可用代理
self.test_api = 'http://www.baidu.com'
 
def set_raw_proxies(self, proxies):
self.raw_proxies = proxies # 臨時存儲一些代理
 
async def test_single_proxy(self, proxy):
"""
python3.5 之後出現的新特性
text one proxy, if valid, put them to usable_proxies.
"""
try:
async with aiohttp.ClientSession() as session:
try:
if isinstance(proxy, bytes):
proxy = proxy.decode('utf-8')
real_proxy = 'http://' + proxy
print('Testing', proxy)
async with session.get(self.test_api, proxy=real_proxy, timeout=get_proxy_timeout) as response:
if response.status == 200:
print('Valid proxy', proxy)
except (ProxyConnectionError, TimeoutError, ValueError):
print('Invalid proxy', proxy)
except (ServerDisconnectedError, ClientResponseError, ClientConnectorError) as s:
print(s)
pass
 
def test(self):
"""
aio test all proxies.
"""
print('ValidityTester is working')
try:
loop = asyncio.get_event_loop()
tasks = [self.test_single_proxy(proxy) for proxy in self.raw_proxies]
loop.run_until_complete(asyncio.wait(tasks))
except ValueError:
print('Async Error')
 
class Schedule(object):
@staticmethod
def valid_proxy():
"""
Get half of proxies which in Spider
"""
tester = ValidityTester()
free_proxy_getter = FreeProxyGetter()
# 獲取 Spider 數據庫數據
# 調用測試類
tester.set_raw_proxies(free_proxy_getter.run())
# 開始測試
tester.test()
 
def run(self):
print('Ip processing running')
valid_process = Process(target=Schedule.valid_proxy) # 獲取代理並篩選
valid_process.start()
if __name__ == '__main__':
schedule = Schedule().run()

對於上面的部分代理代碼,我爲了輸出結果更加直觀,把 print() 輸出的,包括 utils.pyprint() 全部註釋了, 運行結果(省略部分輸出)

Testing 182.46.252.84:9999
Testing 183.63.188.250:8808
Testing 39.137.69.10:8080
Testing 119.190.149.226:8060
Testing 219.159.38.201:56210
Testing 58.246.143.32:8118
Testing 114.99.117.13:3000
Testing 114.229.229.248:8118
Testing 119.57.108.53:53281
Testing 223.247.94.164:4216
Testing 219.159.38.208:56210
Testing 59.44.78.30:42335
Testing 112.74.87.172:3128
Testing 182.92.235.109:3128
Testing 58.62.115.3:4216
Testing 183.162.171.37:4216
Testing 223.241.5.46:4216
Testing 223.243.4.231:4216
Testing 223.241.5.228:4216
Testing 183.162.171.56:4216
Testing 223.241.4.146:4216
Testing 223.241.4.200:4216
Testing 223.241.4.182:4216
Testing 118.187.50.114:8080
Testing 223.214.176.170:3000
Testing 125.126.123.208:60004
Testing 163.125.248.39:8088
Testing 119.57.108.89:53281
Testing 222.249.238.138:8080
Testing 125.126.97.77:60004
Testing 222.182.54.210:8118
Valid proxy 58.220.95.90:9401
Valid proxy 60.191.45.92:8080
Valid proxy 222.186.55.41:8080
Valid proxy 60.205.132.71:80
Valid proxy 124.90.49.146:8888
 
Valid proxy 218.75.109.86:3128
Valid proxy 27.188.64.70:8060
 
Valid proxy 27.188.62.3:8060
Valid proxy 221.180.170.104:8080
Valid proxy 39.137.69.9:8080
 
Valid proxy 39.137.69.6:80
Valid proxy 39.137.69.10:8080
Valid proxy 114.229.6.215:8118
Valid proxy 221.180.170.104:8080
Valid proxy 218.89.14.142:8060
Valid proxy 116.196.85.150:3128
 
Valid proxy 122.224.65.197:3128
Valid proxy 112.253.11.103:8000
Valid proxy 112.253.11.103:8000
Valid proxy 112.253.11.113:8000
Valid proxy 124.90.52.200:8888
 
Cannot connect to host 113.103.226.99:4216 ssl:default [Connect call failed ('113.103.226.99', 4216)]
Cannot connect to host 36.249.109.59:8221 ssl:default [Connect call failed ('36.249.109.59', 8221)]
Cannot connect to host 125.126.121.66:60004 ssl:default [Connect call failed ('125.126.121.66', 60004)]
Cannot connect to host 27.42.168.46:48919 ssl:default [Connect call failed ('27.42.168.46', 48919)]
Cannot connect to host 163.125.113.220:8118 ssl:default [Connect call failed ('163.125.113.220', 8118)]
Cannot connect to host 114.55.63.34:808 ssl:default [Connect call failed ('114.55.63.34', 808)]
Cannot connect to host 119.133.207.10:4216 ssl:default [Connect call failed ('119.133.207.10', 4216)]
Cannot connect to host 58.62.115.3:4216 ssl:default [Connect call failed ('58.62.115.3', 4216)]
Cannot connect to host 117.71.164.232:3000 ssl:default [Connect call failed ('117.71.164.232', 3000)]

到此,多站抓取異步檢測,就已經完全實現,也是實現了模塊化編程。我也 順便把項目結構分享一下:

C:.
│ error.py
│ info.txt
│ schedule.py
│ Spider.py
│ the_test_code_crawler.py
│ utils.py
│
└─__pycache__
db.cpython-37.pyc
error.cpython-37.pyc
Spider.cpython-37.pyc
utils.cpython-37.pyc

本篇我想你主要學習到的目標也就是開頭所說的,這裏就不贅述了,下一篇我將帶大家把抓取到可用的代理進行存儲到 redis 數據庫中,盡請關注!

源代碼文件獲取:公衆號後臺回放:ProxyPool-1

也歡迎加入我的交流羣,一起交流!

相關文章