python搭建IP池

,都說標題是文章的靈魂,想了半天沒想到什麼比較有創意的標題,只好百度了一個。啊哈哈哈哈哈哈,朕真是太機智了
在這裏插入圖片描述
這是一篇介紹如何使用python搭建IP池的文章,如果愛卿對此不感興趣,那很抱歉,標題耽誤了你寶貴的時間。
**看不慣我又幹不掉我**

事情的起因是這樣,前段時間我寫了一篇介紹如何爬取小說的blog【python那些事.No2】,在爬取的過程中,發現同一個IP連續只能獲取前幾頁小說內容,原本是想搭建IP池繞過這個限制的,奈何項目上來了新任務,爲了在被關進小黑屋之前把文章發佈出來,就想了一個折中的辦法–延時獲取。
**機智**
沒想到文章發出來後,竟然還有人評論催更 搭建IP池。朕當時就龍顏大怒,長這麼大朕何時受過這種氣啊。從來都是朕催更那些小說作者,被別人催更還是頭一遭
但是打又打不到,罵又罵不得,咋辦?想了想那還是更吧。
在這裏插入圖片描述
衆所周知,由於python爬蟲這種簡單易學的技術普及之後,爲了網站的穩定運行和網站數據的安全,越來越多的網站對爬蟲做各式各樣的限制和反扒措施。其中,限制一定時間內同一IP的請求次數似乎已經成爲了最常見的手段。
很多時候,使用延時獲取的方式–‘在兩次請求之間sleep一定的時間’ 可以解決網站對爬蟲的限制。可是像朕這種年輕人,想要的現在就要,怎麼辦呢?
**表情包**
既然是對同一IP的限制,那就意味着兩次請求的IP如果不同,此限制就形同虛設。
One way of thinking 去網上買代理IP。搞這個業務的有很多,不過真正哪個服務提供商的有效IP率最高,還需要各方仔細斟酌
Another way of thinking 自己搭建IP池。畢竟對朕這種吃一碗麪都還要猶豫加不加煎蛋的社畜來說,買代理IP這種事情,是萬萬幹不出來的。那麼這個時候,就有必要了解一下如何搭建IP池,以及如何提高IP池的有效IP率
在這裏插入圖片描述

先介紹一下搭建IP池的基本思路:
1.找免費代理IP網站:網上代理IP網站有很多,大多都是免費+收費模式。如西刺代理、89免費代理、快代理等。
2.分析頁面,獲取數據(IP、端口、類型)並存儲(多存於數據庫,方便存取和分析)
3.篩選、過濾:爲了保證IP的有效性,有必要對獲取的免費代理IP進行過濾和篩選,去掉不可用的和重複的
在這裏插入圖片描述
本文以西刺代理的國內高匿代理IP爲例:
地址請在代碼裏查找,或者自行百度
頁面大概長這樣:
在這裏插入圖片描述

先寫一個方法獲取所有頁面url並put入隊,【原理:獲取頁面 下一頁按鈕的href值,並組裝】。
warning:訪問速度別太快,很容易被西刺封IP(經過朕的親自測試,確定西刺官網的封IP機制很靈敏),下同,切記。如果你不幸被封,可以切換網絡繼續(如:將WIFI切換成手機熱點),或者等第二天西刺會將IP解封。

 #獲取頁面URL
def get_url(start_url,queue):
    while True:
        print(start_url)
         #生成請求代理信息
        headers = random.choice(header_list)
        # 獲取頁面信息
        response = requests.get(url=start_url, headers=headers)
        # 獲取請求狀態碼
        code = response.status_code
        #將頁面URL入隊
        queue.put(start_url)
        if code == 200:
            html = et.HTML(response.content.decode('utf-8'))
            #獲取信息
            r = html.xpath('//a[@rel="next"]/@href')
            if r:
                #拼接下一頁的url
                start_url = 'https://www.xicidaili.com/' + r[0]
            else:
                #跳出循環,頁面url獲取完成
                break
        else:
            print(code)
        time.sleep(2)
    print('Get url complete')

然後寫一個方法獲取頁面中(頁面地址從隊列get)我們所需要的那些信息,包括IP、類型、端口。【分析頁面可得,我們所需要的信息在一個非常整齊的table裏面,只需要取相應的td就行】

#獲取頁面IP信息
def get_info(queue):
    while not queue.empty():
        #休息一下
        time.sleep(3)
        #生成請求代理信息
        headers = random.choice(header_list)
        #從隊列獲取頁面url
        page_url = queue.get()
        queue.task_done()
        print(page_url)
        # 獲取頁面信息
        response = requests.get(url=page_url, headers=headers)
        # 獲取請求狀態碼
        code = response.status_code
        #頁面所有代理IP信息存儲
        data_map = []
        if code == 200:
            html = et.HTML(response.content.decode('utf-8'))
            #獲取信息
            r = html.xpath('//tr[position()>1]')
            for i in r:
                #每一個tr中的信息
                data = {
                    'ip' : ''.join(i.xpath(".//td[2]//text()")),
                    'port' : ''.join(i.xpath(".//td[3]//text()")),
                    'type' : ''.join(i.xpath(".//td[6]//text()"))
                }
                #彙總
                data_map.append(data)
                #【一種更優雅的獲取表格數據方式:pandas】
            #存儲
            db_save(data_map)
        else:
            print(code)
    print('It is NUll')

當然還需要一個存儲方法,存入數據庫是爲了方便分析、驗證和調用(你當然也可以存入文件)

#插入數據庫
def db_save(data):
    conn = mysql.connector.connect(user='root',password='root',database='test')
    for k in data:
        try:
            cursor = conn.cursor()
            cursor.execute('insert into ip_pool (ip, port, type) values (%s, %s, %s)', [k['ip'], k['port'], k['type']])
            conn.commit()
            print('【OK】數據插入成功,IP:',k['ip'])
        except Exception as e:
            print('【ERROR】數據插入失敗:',e)
        finally:
            cursor.close()
    conn.close()

既然要存數據庫,那肯定要建個數據表(此處提供一個數據表簡單demo):

# Host: localhost  (Version: 5.7.26)
# Date: 2020-01-19 13:47:45
# Generator: MySQL-Front 5.3  (Build 4.234)

/*!40101 SET NAMES utf8 */;

#
# Structure for table "ip_pool"
#

DROP TABLE IF EXISTS `ip_pool`;
CREATE TABLE `ip_pool` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` char(16) DEFAULT NULL COMMENT 'ip',
  `port` char(5) DEFAULT NULL COMMENT '端口號',
  `type` char(5) DEFAULT NULL COMMENT '類型',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

整理整理思路,得如下代碼:

#IP池搭建   西刺代理
import requests
import random
from lxml import etree as et
from queue import Queue #導入queue模塊
import time  #導入time模塊
import mysql.connector  #導入數據庫模塊
import threading  #導入threading模塊

#常用請求代理
header_list = [
    {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"},
    {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3676.400 QQBrowser/10.4.3469.400"},
    {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36"},
    {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"}
]

#插入數據庫
def db_save(data):
    conn = mysql.connector.connect(user='root',password='root',database='test')
    for k in data:
        try:
            cursor = conn.cursor()
            cursor.execute('insert into ip_pool (ip, port, type) values (%s, %s, %s)', [k['ip'], k['port'], k['type']])
            conn.commit()
            print('【OK】數據插入成功,IP:',k['ip'])
        except Exception as e:
            print('【ERROR】數據插入失敗:',e)
        finally:
            cursor.close()
    conn.close()

#獲取頁面URL
def get_url(start_url,queue):
    while True:
        print(start_url)
         #生成請求代理信息
        headers = random.choice(header_list)
        # 獲取頁面信息
        response = requests.get(url=start_url, headers=headers)
        # 獲取請求狀態碼
        code = response.status_code
        #將頁面URL入隊
        queue.put(start_url)
        if code == 200:
            html = et.HTML(response.content.decode('utf-8'))
            #獲取信息
            r = html.xpath('//a[@rel="next"]/@href')
            if r:
                #拼接下一頁的url
                start_url = 'https://www.xicidaili.com/' + r[0]
            else:
                #跳出循環,頁面url獲取完成
                break
        else:
            print(code)
        time.sleep(2)
    print('Get url complete')

#獲取頁面IP信息
def get_info(queue):
    while not queue.empty():
        #休息一下
        time.sleep(3)
        #生成請求代理信息
        headers = random.choice(header_list)
        #從隊列獲取頁面url
        page_url = queue.get()
        queue.task_done()
        print(page_url)
        # 獲取頁面信息
        response = requests.get(url=page_url, headers=headers)
        # 獲取請求狀態碼
        code = response.status_code
        #頁面所有代理IP信息存儲
        data_map = []
        if code == 200:
            html = et.HTML(response.content.decode('utf-8'))
            #獲取信息
            r = html.xpath('//tr[position()>1]')
            for i in r:
                #每一個tr中的信息
                data = {
                    'ip' : ''.join(i.xpath(".//td[2]//text()")),
                    'port' : ''.join(i.xpath(".//td[3]//text()")),
                    'type' : ''.join(i.xpath(".//td[6]//text()"))
                }
                #彙總
                data_map.append(data)
                #【一種更優雅的獲取表格數據方式:pandas】
            #存儲
            db_save(data_map)
        else:
            print(code)
    print('It is NUll')
    

# 主函數
if __name__ == "__main__":
    
    #開始頁URL
    start_url = 'https://www.xicidaili.com/nn/'
    
    #用Queue構造一個大小爲1000的線程安全的先進先出隊列
    page_url_queue = Queue(maxsize=1000) 
    
    #創建一個線程抓取頁面url
    t1 = threading.Thread(target=get_url, args=(start_url,page_url_queue))
    #開始線程
    t1.start()
    
    time.sleep(2)
    
    #創建一個線程分析頁面信息,並存儲
    t2 = threading.Thread(target=get_info, args=(page_url_queue,))
    t2.start()
    
    #結束線程
    t1.join()
    t2.join()
    
    print('the end!')

一運行:
在這裏插入圖片描述
哎喲,我去。BUG?不存在的
在這裏插入圖片描述

打開數據庫看看:

在這裏插入圖片描述
呵,整整齊齊
在這裏插入圖片描述

當然,免費代理IP大部分都是無效的。
所以,需要將獲得的IP再進行有效性校驗,刪掉不可用的,保證我們在需要的時候取到的IP可用。
這裏提供幾個思路:
1.在插入數據庫之前,先檢查一下該代理IP是否可用,如果不可用,則直接下一個
2.由於有的代理IP有效期很短,所以需要定時檢測數據表中代理IP的有效性,去掉不可用的
3.在使用之前,從數據庫中取出的IP,先判斷該IP的有效性。

自建IP池完整代碼,git地址:~~在不久的將來,此處將會有一個git地址

眼淚不是答案,拼搏纔是選擇。只有回不了的過去,沒有到不了的明天。
所有的不甘,都是因爲還心存夢想,在你放棄之前,好好拼一把,只怕心老,不怕路長。

The end !

發佈了70 篇原創文章 · 獲贊 265 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章