详解基于Scrapy的IP代理池搭建


Posted in Python onSeptember 29, 2020

一、为什么要搭建爬虫代理池

在众多的网站防爬措施中,有一种是根据ip的访问频率进行限制,即在某一时间段内,当某个ip的访问次数达到一定的阀值时,该ip就会被拉黑、在一段时间内禁止访问。

应对的方法有两种:

1. 降低爬虫的爬取频率,避免IP被限制访问,缺点显而易见:会大大降低爬取的效率。

2. 搭建一个IP代理池,使用不同的IP轮流进行爬取。

二、搭建思路

1、从代理网站(如:西刺代理、快代理、云代理、无忧代理)爬取代理IP;

2、验证代理IP的可用性(使用代理IP去请求指定URL,根据响应验证代理IP是否生效);

3、将可用的代理IP保存到数据库;

在《Python爬虫代理池搭建》一文中我们已经使用Python的 requests 模块简单实现了一个IP代理池搭建,但是爬取速度较慢。由于西刺代理、快代理和云代理等网站需要爬取的IP代理列表页多达上千页,使用此种方法来爬取其实并不适合。

本文将以快代理网站的IP代理爬取为例,示例如何使用 Scrapy-Redis 来爬取代理IP。

三、搭建代理池

scrapy 项目的目录结构如下:

详解基于Scrapy的IP代理池搭建

items.py

# -*- coding: utf-8 -*-
import re
import scrapy
from proxy_pool.settings import PROXY_URL_FORMATTER

schema_pattern = re.compile(r'http|https$', re.I)
ip_pattern = re.compile(r'^([0-9]{1,3}.){3}[0-9]{1,3}$', re.I)
port_pattern = re.compile(r'^[0-9]{2,5}$', re.I)


class ProxyPoolItem(scrapy.Item):
  # define the fields for your item here like:
  # name = scrapy.Field()
  '''
    {
      "schema": "http", # 代理的类型
      "ip": "127.0.0.1", # 代理的IP地址
      "port": "8050", # 代理的端口号
      "original":"西刺代理",
      "used_total": 11, # 代理的使用次数
      "success_times": 5, # 代理请求成功的次数
      "continuous_failed": 3, # 使用代理发送请求,连续失败的次数
      "created_time": "2018-05-02" # 代理的爬取时间
    }
  '''
  schema = scrapy.Field()
  ip = scrapy.Field()
  port = scrapy.Field()
  original = scrapy.Field()
  used_total = scrapy.Field()
  success_times = scrapy.Field()
  continuous_failed = scrapy.Field()
  created_time = scrapy.Field()

  # 检查IP代理的格式是否正确
  def _check_format(self):
    if self['schema'] is not None and self['ip'] is not None and self['port'] is not None:
      if schema_pattern.match(self['schema']) and ip_pattern.match(self['ip']) and port_pattern.match(
          self['port']):
        return True
    return False

  # 获取IP代理的url
  def _get_url(self):
    return PROXY_URL_FORMATTER % {'schema': self['schema'], 'ip': self['ip'], 'port': self['port']}

kuai_proxy.py

# -*- coding: utf-8 -*-
import re
import time
import scrapy
from proxy_pool.utils import strip, logger
from proxy_pool.items import ProxyPoolItem


class KuaiProxySpider(scrapy.Spider):
  name = 'kuai_proxy'
  allowed_domains = ['kuaidaili.com']
  start_urls = ['https://www.kuaidaili.com/free/inha/1/', 'https://www.kuaidaili.com/free/intr/1/']

  def parse(self, response):
    logger.info('正在爬取:< ' + response.request.url + ' >')
    tr_list = response.css("div#list>table>tbody tr")
    for tr in tr_list:
      ip = tr.css("td[data-title='IP']::text").get()
      port = tr.css("td[data-title='PORT']::text").get()
      schema = tr.css("td[data-title='类型']::text").get()
      if schema.lower() == "http" or schema.lower() == "https":
        item = ProxyPoolItem()
        item['schema'] = strip(schema).lower()
        item['ip'] = strip(ip)
        item['port'] = strip(port)
        item['original'] = '快代理'
        item['created_time'] = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        if item._check_format():
          yield item
    next_page = response.xpath("//a[@class='active']/../following-sibling::li/a/@href").get()
    if next_page is not None:
      next_url = 'https://www.kuaidaili.com' + next_page
      yield scrapy.Request(next_url)

middlewares.py

# -*- coding: utf-8 -*-

import random
from proxy_pool.utils import logger


# 随机选择 IP 代理下载器中间件
class RandomProxyMiddleware(object):

  # 从 settings 的 PROXIES 列表中随机选择一个作为代理
  def process_request(self, request, spider):
    proxy = random.choice(spider.settings['PROXIES'])
    request.meta['proxy'] = proxy
    return None


# 随机选择 User-Agent 的下载器中间件
class RandomUserAgentMiddleware(object):
  def process_request(self, request, spider):
    # 从 settings 的 USER_AGENTS 列表中随机选择一个作为 User-Agent
    user_agent = random.choice(spider.settings['USER_AGENT_LIST'])
    request.headers['User-Agent'] = user_agent
    return None

  def process_response(self, request, response, spider):
    # 验证 User-Agent 设置是否生效
    logger.info("headers ::> User-Agent = " + str(request.headers['User-Agent'], encoding="utf8"))
    return response

pipelines.py

# -*- coding: utf-8 -*-

import json
import redis
from proxy_pool.settings import REDIS_HOST,REDIS_PORT,REDIS_PARAMS,PROXIES_UNCHECKED_LIST,PROXIES_UNCHECKED_SET

server = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT,password=REDIS_PARAMS['password'])

class ProxyPoolPipeline(object):

  # 将可用的IP代理添加到代理池队列
  def process_item(self, item, spider):
    if not self._is_existed(item):
      server.rpush(PROXIES_UNCHECKED_LIST, json.dumps(dict(item),ensure_ascii=False))

  # 检查IP代理是否已经存在
  def _is_existed(self,item):
    added = server.sadd(PROXIES_UNCHECKED_SET, item._get_url())
    return added == 0

settings.py

# -*- coding: utf-8 -*-
BOT_NAME = 'proxy_pool'

SPIDER_MODULES = ['proxy_pool.spiders']
NEWSPIDER_MODULE = 'proxy_pool.spiders'

# 保存未检验代理的Redis key
PROXIES_UNCHECKED_LIST = 'proxies:unchecked:list'

# 已经存在的未检验HTTP代理和HTTPS代理集合
PROXIES_UNCHECKED_SET = 'proxies:unchecked:set'

# 代理地址的格式化字符串
PROXY_URL_FORMATTER = '%(schema)s://%(ip)s:%(port)s'

# 通用请求头字段
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
  'Accept-Encoding': 'gzip, deflate, br',
  'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
  'Connection': 'keep-alive'
}

# 请求太频繁会导致 503 ,在此设置 5 秒请求一次
DOWNLOAD_DELAY = 5 # 250 ms of delay

USER_AGENT_LIST = [
  "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
  "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
  "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
  "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
  "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
  "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
  "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
  "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
  "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
  "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
  'proxy_pool.middlewares.RandomUserAgentMiddleware': 543,
  #  'proxy_pool.middlewares.RandomProxyMiddleware': 544,
}

ITEM_PIPELINES = {
  'proxy_pool.pipelines.ProxyPoolPipeline': 300,
}

PROXIES = [
  "https://171.13.92.212:9797",
  "https://164.163.234.210:8080",
  "https://143.202.73.219:8080",
  "https://103.75.166.15:8080"
]

######################################################
##############下面是Scrapy-Redis相关配置################
######################################################

# 指定Redis的主机名和端口
REDIS_HOST = '172.16.250.238'
REDIS_PORT = 6379
REDIS_PARAMS = {'password': '123456'}

# 调度器启用Redis存储Requests队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 确保所有的爬虫实例使用Redis进行重复过滤
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 将Requests队列持久化到Redis,可支持暂停或重启爬虫
SCHEDULER_PERSIST = True

# Requests的调度策略,默认优先级队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

utils.py

# -*- coding: utf-8 -*-
import logging

# 设置日志输出格式
logging.basicConfig(level=logging.INFO,
          format='[%(asctime)-15s] [%(levelname)8s] [%(name)10s ] - %(message)s (%(filename)s:%(lineno)s)',
          datefmt='%Y-%m-%d %T'
          )
logger = logging.getLogger(__name__)

# Truncate header and tailer blanks
def strip(data):
  if data is not None:
    return data.strip()
  return data

到此这篇关于详解基于Scrapy的IP代理池搭建的文章就介绍到这了,更多相关Scrapy IP代理池搭建内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python使用pygame模块编写俄罗斯方块游戏的代码实例
Dec 08 Python
使用python验证代理ip是否可用的实现方法
Jul 25 Python
Python实现多级目录压缩与解压文件的方法
Sep 01 Python
python+ffmpeg批量去视频开头的方法
Jan 09 Python
Python3多线程基础知识点
Feb 19 Python
Python Matplotlib实现三维数据的散点图绘制
Mar 19 Python
Linux上使用Python统计每天的键盘输入次数
Apr 17 Python
Python计算不规则图形面积算法实现解析
Nov 22 Python
解决pycharm中opencv-python导入cv2后无法自动补全的问题(不用作任何文件上的修改)
Mar 05 Python
基于python纯函数实现井字棋游戏
May 27 Python
使用numpy实现矩阵的翻转(flip)与旋转
Jun 03 Python
Python可视化动图组件ipyvizzu绘制惊艳的可视化动图
Apr 21 Python
Python 创建守护进程的示例
Sep 29 #Python
Python 解析xml文件的示例
Sep 29 #Python
Python 字典一个键对应多个值的方法
Sep 29 #Python
python 获取字典特定值对应的键的实现
Sep 29 #Python
Python3 pyecharts生成Html文件柱状图及折线图代码实例
Sep 29 #Python
Python爬取微信小程序通用方法代码实例详解
Sep 29 #Python
详解如何修改python中字典的键和值
Sep 29 #Python
You might like
PHP初学者最感迷茫的问题小结
2010/03/27 PHP
php获取发送给用户的header信息的方法
2015/03/16 PHP
PHP children()函数讲解
2019/02/03 PHP
PHP基于openssl实现非对称加密代码实例
2020/06/19 PHP
jQuery Trim去除字符串首尾空字符的实现方法说明
2014/02/11 Javascript
jquery显示loading图片直到网页加载完成的方法
2015/06/25 Javascript
如何用angularjs制作一个完整的表格
2016/01/21 Javascript
JavaScript+html5 canvas实现本地截图教程
2020/04/16 Javascript
jQuery添加options点击事件并传值实例代码
2016/05/18 Javascript
javascript中的深复制详解及实例分析
2016/12/29 Javascript
vue下载excel的实现代码后台用post方法
2019/05/10 Javascript
原生js实现商品筛选功能
2019/10/28 Javascript
深入讲解Python中面向对象编程的相关知识
2015/05/25 Python
Python饼状图的绘制实例
2019/01/15 Python
Pythony运维入门之Socket网络编程详解
2019/04/15 Python
pandas DataFrame索引行列的实现
2019/06/04 Python
Python pandas RFM模型应用实例详解
2019/11/20 Python
python3中利用filter函数输出小于某个数的所有回文数实例
2019/11/24 Python
pytorch对梯度进行可视化进行梯度检查教程
2020/02/04 Python
Python 3.8 新功能来一波(大部分人都不知道)
2020/03/11 Python
使用Keras预训练好的模型进行目标类别预测详解
2020/06/27 Python
Pycharm 2020.1 版配置优化的详细教程
2020/08/07 Python
python实现简单的tcp 文件下载
2020/09/16 Python
HTML5安全介绍之内容安全策略(CSP)简介
2012/07/10 HTML / CSS
一张图片能隐含千言万语之隐藏你的程序代码
2012/12/13 HTML / CSS
JustFab加拿大:女鞋、靴子、手袋和服装在线
2018/05/18 全球购物
Linux上比较文件的命令都有哪些
2013/09/28 面试题
《最大的“书”》教学反思
2014/02/14 职场文书
婚前保证书
2014/04/29 职场文书
“三支一扶”支教教师思想汇报
2014/09/13 职场文书
纪念九一八事变83周年国旗下讲话稿
2014/09/15 职场文书
反四风个人对照检查材料
2014/09/26 职场文书
浅谈spring boot使用thymeleaf版本的问题
2021/08/04 Java/Android
Python函数式编程中itertools模块详解
2021/09/15 Python
聊聊mysql都有哪几种分区方式
2022/04/13 MySQL
Meta增速拉垮,元宇宙难当重任
2022/04/29 数码科技