Scrapy ip代理池


一、概述

在眾多的網站防爬措施中,有一種是根據ip的訪問頻率進行限制,即在某一時間段內,當某個ip的訪問次數達到一定的閥值時,該ip就會被拉黑、在一段時間內禁止訪問。

應對的方法有兩種:

1. 降低爬蟲的爬取頻率,避免IP被限制訪問,缺點顯而易見:會大大降低爬取的效率。

2. 搭建一個IP代理池,使用不同的IP輪流進行爬取。

 

環境說明

操作系統:centos 7.6

ip地址:192.168.31.230

說明:運行redis和ip代理池

 

操作系統:windows 10

ip地址:192.168.31.230

說明:運行Scrapy 爬蟲項目

 

二、搭建IP代理池

介紹

在github上,有一個現成的ip代理池項目,地址:https://github.com/jhao104/proxy_pool

爬蟲代理IP池項目,主要功能為定時采集網上發布的免費代理驗證入庫,定時驗證入庫的代理保證代理的可用性,提供API和CLI兩種使用方式。同時你也可以擴展代理源以增加代理池IP的質量和數量。

 

搭建redis

注意:此項目運行時,依賴於redis。這里直接用docker搭建

docker run -d --name redis -p 6380:6379 redis --requirepass 123456

說明:

映射端口6080,redis密碼為:123456

 

運行ip代理池

由於ip代理池項目,在dockerhub上面有現成的鏡像,直接拿來使用即可。

啟動命令示例:

docker run --env DB_CONN=redis://:password@ip:port/db -p 5010:5010 jhao104/proxy_pool:latest

注意:這里要根據實際情況,指定redis的連接信息。

 

所以:根據上面的環境說明,正確的redis連接信息為:DB_CONN=redis://:123456@192.168.31.230:6380/0

因此啟動命令為:

docker run -d --env DB_CONN=redis://:123456@192.168.31.230:6380/0 -p 5010:5010 jhao104/proxy_pool:latest

注意:請根據實際情況,修改redis連接信息,-d表示后台運行。

 

使用

api

啟動web服務后, 默認配置下會開啟 http://127.0.0.1:5010 的api接口服務:

 

 

隨機獲取一個代理,訪問頁面

http://192.168.31.230:5010/get/

效果如下:

 

 其中,proxy字段,就是我們需要的代理了

那么在爬蟲項目中,獲取到這個字段,就可以使用了。

 

 

三、項目演示

那么如何知道,我的爬蟲項目,用了ip代理,去訪問指定的網站呢?

一般來說,打開:https://www.ip138.com/ 就能看到我的公網ip了。但是通過代碼爬取這個頁面,得到我的公網ip比較麻煩。

有一個簡單網頁:http://httpbin.org/get 直接訪問它,它會返回一段json數據,里面就包含了我的公網ip地址。

 

那么下面,我將創建一個Scrapy 項目,應用ip代理池,去訪問 http://httpbin.org/get,並打印出公網ip地址。

 

創建項目

打開Pycharm,並打開Terminal,執行以下命令

scrapy startproject ip_proxy
cd ip_proxy
scrapy genspider httpbin httpbin.org

 

在scrapy.cfg同級目錄,創建bin.py,用於啟動Scrapy項目,內容如下:

#在項目根目錄下新建:bin.py
from scrapy.cmdline import execute
# 第三個參數是:爬蟲程序名
execute(['scrapy', 'crawl', 'blog',"--nolog"])

 

在items.py同級目錄,創建proxy_handle.py,內容如下:

import json
import requests

def get_proxy():
    response = requests.get("http://192.168.31.230:5010/get/").text
    result = json.loads(response)
    # print(result, type(result))
    return result['proxy']

def delete_proxy(proxy):
    requests.get("http://192.168.31.230:5010/delete/?proxy={}".format(proxy))

 

創建好的項目樹形目錄如下:

./
├── bin.py
├── ip_proxy
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── proxy_handle.py
│   ├── settings.py
│   └── spiders
│       ├── httpbin.py
│       └── __init__.py
└── scrapy.cfg

 

修改httpbin.py

# -*- coding: utf-8 -*-
import scrapy
import json
from scrapy import Request  # 導入模塊

class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    # start_urls = ['http://httpbin.org/']
    # 自定義配置,注意:變量名必須是custom_settings
    custom_settings = {
        'REQUEST_HEADERS': {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
        }
    }

    def start_requests(self):
        r1 = Request(url="http://httpbin.org/get",
                     headers=self.settings.get('REQUEST_HEADERS'), )
        yield r1

    def parse(self, response):
        # print(response.text,type(response.text))
        result = json.loads(response.text)
        # print(result,type(result))
        origin = result['origin']
        print("公網ip: ",origin)

 

修改middlewares.py

這里,我主要修改下載中間件的內容

class IpProxyDownloaderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        """
        請求需要被下載時,經過所有下載器中間件的process_request調用
        :param request:
        :param spider:
        :return:
            None,繼續后續中間件去下載;
            Response對象,停止process_request的執行,開始執行process_response
            Request對象,停止中間件的執行,將Request重新調度器
            raise IgnoreRequest異常,停止process_request的執行,開始執行process_exception
        """
        proxy = "http://" + get_proxy()
        request.meta['download_timeout'] = 10
        request.meta["proxy"] = proxy
        print('為 %s 添加代理 %s ' % (request.url, proxy))
        # print('元數據為', request.meta.get("proxy"))

    def process_response(self, request, response, spider):
        """
        spider處理完成,返回時調用
        :param response:
        :param result:
        :param spider:
        :return:
            Response 對象:轉交給其他中間件process_response
            Request 對象:停止中間件,request會被重新調度下載
            raise IgnoreRequest 異常:調用Request.errback
        """
        print('返回狀態碼', response.status)
        return response

    def process_exception(self, request, exception, spider):
        """
        當下載處理器(download handler)或 process_request() (下載中間件)拋出異常
        :param response:
        :param exception:
        :param spider:
        :return:
            None:繼續交給后續中間件處理異常;
            Response對象:停止后續process_exception方法
            Request對象:停止中間件,request將會被重新調用下載
        """
        print('代理%s,訪問%s出現異常:%s' % (request.meta['proxy'], request.url, exception))
        import time
        time.sleep(5)
        delete_proxy(request.meta['proxy'].split("//")[-1])
        request.meta['proxy'] = 'http://' + get_proxy()

        return request

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

說明:在請求之前,從ip代理池中獲取一個隨機ip。當ip代理訪問異常時,從ip代理池中,刪除這個代理ip。

 

修改settings.py,應用下載中間件

DOWNLOADER_MIDDLEWARES = {
   'ip_proxy.middlewares.IpProxyDownloaderMiddleware': 543,
}

 

執行bin.py,啟動爬蟲項目,效果如下:

 

 

注意:每次訪問一個鏈接時,ip地址是不一樣的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM