Python搭建代理IP池實現儲存IP的方法
上一文寫了如何從代理服務網站提取 IP,本文就講解如何儲存 IP,畢竟代理池還是要有一定量的 IP 數量才行。儲存的方式有很多,直接一點的可以放在一個文字檔案中,但操作起來不太靈活,而我選擇的是 MySQL 資料庫,因為資料庫便於管理而且功能強大,當然你還可以選擇其他資料庫,比如 MongoDB、Redis 等。
程式碼地址:https://github.com/Stevengz/Proxy_pool
另外三篇:
Python搭建代理IP池(一)- 獲取 IP
Python搭建代理IP池(三)- 檢測 IP
Python搭建代理IP池(四)- 介面設定與整體排程
使用的庫:pymysql
定義規則
資料庫儲存的主要物件是各個 IP,首先需要保證不重複,另外還需要標 IP 的可用情況,而且需要動態實時處理每個 IP,因此還需要定義一個分數字段,分數是可以重複的,最好是整數型別,每個 IP 都有一個分數,表現其可用性
對於代理池來說,分數可以作為我們判斷一個代理可用不可用的標誌,我們將設定一個最高分(滿分,值由自己設定),代表可用,0 設為最低分,代表不可用。從代理池中獲取代理的時候會先從滿分 IP 中隨機獲取一個,注意這裡是隨機,這樣可以保證每個可用 IP 都會被呼叫到,如果沒有滿分的就從所有 IP 從隨機選一個
分數規則如下:
- 滿分為可用,檢測器會定時迴圈檢測每個 IP 的可用情況,一旦檢測到有可用的 IP 就立即置為滿分,檢測到不可用就將分數減 1,減至 0 後移除。
- 新獲取的代理新增時將分數置為 10,當測試可行立即置 100,不可行分數減 1,減至 0 後移除
新增設定
先在一個檔案中定義一些配置資訊,如資料庫的設定、一些不變數如滿分的數值等
setting.py
# 資料庫地址 HOST = '127.0.0.1' # MySql埠 MYSQL_PORT = 3306 # MySQl使用者名稱、密碼 MYSQL_USERNAME = '***' MYSQL_PASSWORD = '***' # 資料庫名 SQL_NAME = 'test' # 代理等級 MAX_SCORE = 30 MIN_SCORE = 0 INITIAL_SCORE = 10 # 代理池數量界限 POOL_UPPER_THRESHOLD = 1000
MAX_SCORE、MIN_SCORE、INITIAL_SCORE 分別代表最大分數、最小分數、初始分數
定義方法
定義一個類來操作資料庫的有序集合,內含一些方法來實現分數的設定、代理的獲取等
db.py
import pymysql from error import PoolEmptyError from setting import * from random import choice import re class MySqlClient(object): # 初始化 def __init__(self,host=HOST,port=MYSQL_PORT,username=MYSQL_USERNAME,password=MYSQL_PASSWORD,sqlname=SQL_NAME): self.db = pymysql.connect(host=host,user=username,password=password,port=port,db=sqlname) self.cursor = self.db.cursor() # 新增代理IP def add(self,ip,score=INITIAL_SCORE): sql_add = "INSERT INTO PROXY (IP,SCORE) VALUES ('%s',%s)" % (ip,score) if not re.match('\d+\.\d+\.\d+\.\d+\:\d+',ip): print('代理不符合規範','丟棄') return if not self.exists(ip): self.cursor.execute(sql_add) self.db.commit() # 減少代理分數 def decrease(self,ip): sql_get = "SELECT * FROM PROXY WHERE IP='%s'" % (ip) self.cursor.execute(sql_get) score = self.cursor.fetchone()[1] print(score) if score and score > MIN_SCORE: print('代理','當前分數',score,'減1') sql_change = "UPDATE PROXY SET SCORE = %s WHERE IP = '%s'" % (score-1,ip) else: print('代理','移除') sql_change = "DELETE FROM PROXY WHERE IP = %s" % (ip) self.cursor.execute(sql_change) self.db.commit() # 分數最大化 def max(self,ip): print('代理','可用,設定為',MAX_SCORE) sql_max = "UPDATE PROXY SET SCORE = %s WHERE IP = '%s'" % (MAX_SCORE,ip) self.cursor.execute(sql_max) self.db.commit() # 隨機獲取有效代理 def random(self): # 先從滿分中隨機選一個 sql_max = "SELECT * FROM PROXY WHERE SCORE=%s" % (MAX_SCORE) if self.cursor.execute(sql_max): results = self.cursor.fetchall() return choice(results)[0] # 沒有滿分則隨機選一個 else: sql_all = "SELECT * FROM PROXY WHERE SCORE BETWEEN %s AND %s" % (MIN_SCORE,MAX_SCORE) if self.cursor.execute(sql_all): results = self.cursor.fetchall() return choice(results)[0] else: raise PoolEmptyError # 判斷是否存在 def exists(self,ip): sql_exists = "SELECT 1 FROM PROXY WHERE IP='%s' limit 1" % ip return self.cursor.execute(sql_exists) # 獲取數量 def count(self): sql_count = "SELECT * FROM PROXY" return self.cursor.execute(sql_count) # 獲取全部 def all(self): self.count() return self.cursor.fetchall() # 批量獲取 def batch(self,start,stop): sql_batch = "SELECT * FROM PROXY LIMIT %s,%s" % (start,stop - start) self.cursor.execute(sql_batch) return self.cursor.fetchall()
方法作用:
- init():初始化的方法,引數是 MySQL 的連線資訊,預設的連線資訊已經定義為常量,在 init() 方法中初始化建立 MySQL 連線。這樣當 MySqlClient 類初始化的時候就建立了 MySQL 的連線
- add():向資料庫新增代理並設定分數,預設的分數是 INITIAL_SCORE 也就是 10,返回結果是新增的結果
- decrease():在 檢測無效的時候設定分數減 1 的方法,傳入代理,然後將此代理的分數減 1,如果達到最低值就刪除
- max():將代理的分數設定為 MAX_SCORE,也就是當 IP 有效時的設定
- random():隨機獲取 IP 的方法,首先獲取滿分的 IP,然後隨機選擇一個返回,如果不存在滿分的 IP,則隨機選擇一個返回,否則丟擲異常
- exists():判斷 IP 是否存在於資料庫中
- count():返回當前 IP個數
- all():返回所有的 IP,供檢測使用
- batch():返回資料庫中從第 start 行開始(從0開始數)的共 stop-start 行資料
抓取儲存
當資料庫設定好了之後,就可以直接把抓取的 IP 直接放在資料庫中了
直接把前面用到的抓取程式碼更改一下就行了
getter.py
from crawler import Crawler from db import MySqlClient from setting import * import sys class Getter(): def __init__(self): self.mysql = MySqlClient() self.crawler = Crawler() # 判斷數量是否足夠 def is_over_threshold(self): if self.mysql.count() >= POOL_UPPER_THRESHOLD: return True else: return False def run(self): print('獲取器開始執行') if not self.is_over_threshold(): for callback_label in range(self.crawler.__CrawlFuncCount__): callback = self.crawler.__CrawlFunc__[callback_label] # 獲取代理 all_ip = self.crawler.get_proxies(callback) sys.stdout.flush() for ip in all_ip: self.mysql.add(ip) if __name__ == '__main__': get = Getter() get.run()
結果:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。