基于 scrapy-redis 的通用分布式爬虫框架

spiderman

基于 scrapy-redis 的通用分布式爬虫框架

开源地址 https://github.com/TurboWay/spiderman


目录

demo采集效果

image
image
image

爬虫元数据

meta

cluster模式

cluster

standalone模式

standalone

附件下载

image-20210318161618307

kafka实时采集监控

mon

功能

  • 自动建表

  • 自动生成爬虫代码,只需编写少量代码即可完成分布式爬虫

  • 自动存储元数据,分析统计和补爬都很方便

  • 适合多站点开发,每个爬虫独立定制,互不影响

  • 调用方便,可以根据传参自定义采集的页数以及启用的爬虫数量

  • 扩展简易,可以根据需要选择采集模式,单机 standalone (默认) 或者 分布式cluster

  • 采集数据落地方便,支持多种数据库,只需在 spider 中启用相关的管道

    关系型

    • mysql
    • sqlserver
    • oracle
    • postgresql
    • sqlite3

    非关系型

    • hbase
    • mongodb
    • elasticsearch
    • hdfs
    • hive
    • datafile, 比如 csv
  • 反爬处理简易,已封装各种反爬中间件

    • 随机 UserAgent
    • 定制请求头 Headers
    • 定制 Cookies 池
    • 定制代理 ip
    • 在 scrapy 中使用 requests
    • Payload 请求
    • 使用 Splash 渲染 js

原理说明

  1. 消息队列使用 redis,采集策略使用广度优先,先进先出
  2. 每个爬虫都有一个 job 文件,使用 job 来生成初始请求类 ScheduledRequest,并将其推送到 redis;
    初始请求全部推到 redis 后,运行 spider 解析生成数据 并迭代新的请求到redis, 直到 redis 中的全部请求被消耗完
# scrapy_redis请求类
class ScheduledRequest:

    def __init__(self, **kwargs):
        self.url = kwargs.get('url')                 # 请求url
        self.method = kwargs.get('method', 'GET')   # 请求方式 默认get
        self.callback = kwargs.get('callback')  # 回调函数,指定spider的解析函数
        self.body = kwargs.get('body')  # body, method为post时, 作为 post表单
        self.meta = kwargs.get('meta')  # meta, 携带元数据,比如 pagenum
  1. item 类定义表名、字段名、排序号(自定义字段顺序)、注释说明(便于管理元数据)、字段类型(仅关系型数据库管道有效)
class zhifang_list_Item(scrapy.Item):
    #  define table
    tablename = 'zhifang_list'
    tabledesc = '列表'
    # define the fields for your item here like:
    # 关系型数据库,可以自定义字段的类型、长度,默认 VARCHAR(length=255)
    # colname = scrapy.Field({'idx': 1, 'comment': '名称', 'type': VARCHAR(255)})
    tit = scrapy.Field({'idx': 1, 'comment': '房屋标题'})
    txt = scrapy.Field({'idx': 2, 'comment': '房屋描述'})
    tit2 = scrapy.Field({'idx': 3, 'comment': '房屋楼层'})
    price = scrapy.Field({'idx': 4, 'comment': '房屋价格'})
    agent = scrapy.Field({'idx': 5, 'comment': '房屋中介'})
    # default column
    detail_full_url = scrapy.Field({'idx': 100, 'comment': '详情链接'})  # 通用字段
    pkey = scrapy.Field({'idx': 101, 'comment': 'md5(detail_full_url)'})  # 通用字段
    pagenum = scrapy.Field({'idx': 102, 'comment': '页码'})  # 通用字段
  1. 去重策略,默认不去重,每次采集独立,即每次启动 job 都会清空上一次未完成的 url,并且不保留 redis 中上一次已采集的 url 指纹。
    如需调整可以修改以下配置
  • job 文件(单个爬虫)
class zhifang_job(SPJob):

    def __init__(self):
        super().__init__(spider_name=zhifang_Spider.name)
        # self.delete()   # 如需去重、增量采集,请注释该行
  • spider 文件(单个爬虫)
    custom_settings = {
        ...,
        'DUPEFILTER_CLASS': 'scrapy_redis.dupefilter.RFPDupeFilter',
        'SCHEDULER_PERSIST': True, # 开启持久化
    }
   
    def get_callback(self, callback):
        # url去重设置:True 不去重 False 去重
        callback_dt = {
            'list': (self.list_parse, False),
            'detail': (self.detail_parse, False),
        }
        return callback_dt.get(callback)
  • 布隆过滤器。

当采集的数据量很大时,可以使用布隆过滤器,该算法占用空间小且可控,适合海量数据去重。
但是该算法会有漏失率,对爬虫而言就是漏爬。可以通过调整过滤器负载个数、内存配置、哈希次数以降低漏失率。
默认 1 个过滤器,256 M 内存,使用 7 个 seeds,这个配置表示漏失概率为 8.56e-05 时,可满足 0.93 亿条字符串的去重。当漏失率为 0.000112 时,可满足 0.98 亿条字符串的去重。调参与漏失率参考

    custom_settings = {
        ...,
        'DUPEFILTER_CLASS': 'SP.bloom_dupefilter.BloomRFDupeFilter',  # 使用布隆过滤器
        'SCHEDULER_PERSIST': True,  # 开启持久化
        'BLOOM_NUM': 1,  # 布隆过滤器负载个数,当内存达到限制时,可以增加负载个数
        'BLOOM_MEM': 256,  # 布隆过滤器内存大小(单位 M),内存最大 512 M (因为 redis string 最大只能 512 M)
        'BLOOM_K': 7,  # 布隆过滤器哈希次数,次数越少,去重越快,但是漏失率越高
    }
   
    def get_callback(self, callback):
        # url去重设置:True 不去重 False 去重
        callback_dt = {
            'list': (self.list_parse, False),
            'detail': (self.detail_parse, False),
        }
        return callback_dt.get(callback)

下载安装

  1. git clone https://github.com/TurboWay/spiderman.git; cd spiderman;
  2. 【不使用虚拟环境的话,可以跳过步骤23】virtualenv -p /usr/bin/python3 venv
  3. 【不使用虚拟环境的话,可以跳过步骤23】source venv/bin/activate
  4. pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
  5. 修改配置 vi SP/settings.py
  6. 运行demo示例 python SP_JOBS/zhifang_job.py

如何开发一个新爬虫

运行 easy_scrapy.py 会根据模板自动生成以下代码文件,并自动在编辑器打开 spidername_job.py 文件;

类别 路径 说明
job SP_JOBS/spidername_job.py 编写初始请求
spider SP/spiders/spidername.py 编写解析规则,产生新的请求
items SP/items/spidername_items.py 定义表名字段

以上代码文件编写完成后,直接执行 python SP_JOBS/spidername_job.py

或者动态传参(参数说明 -p 采集页数, -n 启用爬虫数量) python SP_JOBS/spidername_job.py -p 10 -n 1

如何进行补爬

运行 easy_scrapy.py 会根据模板自动生成以下代码文件,并自动在编辑器打开 spidername_job_patch.py 文件;

类别 路径 说明
job SP_JOBS/spidername_job_patch.py 编写补爬请求

以上代码文件编写完成后,直接执行 python SP_JOBS/spidername_job_patch.py

如何下载附件

提供两种方式下载:

  • 1、直接在 spider 中启用附件下载管道
  • 2、使用自定义的下载器 execute_download.py 传参下载

jpg/pdf/word...等各种各样的文件,统称为附件。
下载附件是比较占用带宽的行为,所以在大规模采集中,最好是先把结构化的表数据、附件的元数据入库,
保证数据的完整性,然后再根据需要,通过下载器进行附件下载。

如何扩展分布式爬虫

采集模式有两种(在 settings 控制): 单机 standalone(默认) 和 集群分布式

如果想切换成分布式爬虫,需要在 spiderman/SP/settings.py 中启用以下配置

<font color='red'> 注意:前提是 所有SLAVE机器的爬虫代码一致、python环境一致,都可以运行爬虫demo </font>

# 集群模式 False 单机 (默认);  True 分布式 需要配置下方的 slaves
CLUSTER_ENABLE = True
配置名称 意义 示例
SLAVES 【二选一】爬虫机器配置列表 [{'host': '172.16.122.12', 'port': 22, 'user': 'spider', 'pwd': 'spider'},
{'host': '172.16.122.13', 'port': 22, 'user': 'spider', 'pwd': 'spider'} ]
SLAVES_BALANCE 【二选一】爬虫机器配置(ssh负载均衡) {'host': '172.16.122.11', 'port': 2202, 'user': 'spider', 'pwd': 'spider'}
SLAVES_ENV 【可选】爬虫机器虚拟环境路径 /home/spider/workspace/spiderman/venv
SLAVES_WORKSPACE 【必填】爬虫机器代码工程路径 /home/spider/workspace/spiderman

如何管理爬虫元数据

运行 easy_meta.py 自动生成当前项目所有爬虫的元数据, 默认记录到sqlite meta.db, 可以在 setting 中自行配置;

# 爬虫 meta
META_ENGINE = 'sqlite:///meta.db'

元数据表meta字典如下:

字段名 类型 注释
spider varchar(50) 爬虫名
spider_comment varchar(100) 爬虫描述
tb varchar(50) 表名
tb_comment varchar(100) 表描述
col_px int 字段序号
col varchar(50) 字段名
col_comment varchar(100) 字段描述
author varchar(20) 开发人员
addtime varchar(20) 开发时间
insertime varchar(20) 元数据更新时间

如何配合kafka做实时采集监控

  1. 配置 kafka(修改 setting 的 KAFKA_SERVERS)
  2. 自定义监控规则(修改编写 kafka_mon.py , 并运行该脚本程序, 开始监控)
  3. 在 spider 中启用 kafka 管道(运行爬虫 job , 开始采集)

如何使用爬虫api

直接运行 api.py,然后可以通过 http://127.0.0.1:2021/docs 查看相关的 api 文档

注意事项

  1. 字段名称不能使用 tablename、isload、ctime、bizdate、spider 等字段,因为这些字段被作为通用字段,避免冲突
  2. items 文件每个字段建议添加注释,生成元数据时,会将注释导入到元数据表,便于管理爬虫

hive环境问题

在 windows 环境下,使用 python3 连接 hive 会有很多坑,所以使用 hdfs 管道时,hive 自动建表功能默认关闭,便于部署。
假如需要启用 hive 自动建表功能,请进行如下操作:

  1. pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
  2. pip install --no-deps thrift-sasl==0.2.1
  3. 验证环境,执行 SP.utils.ctrl_hive

如果执行成功,说明 hive 环境准备完毕,可以直接启用 hive 自动建表功能;如果遇到问题,可以参考 【大数据】windows 下python3连接hive

更新日志

日期 更新内容
20200803 1.使用更优雅的方式来生成元数据;
2.管道函数传参的写法调整;
3.附件表通用字段更名:下载状态 (isload => status)
20200831 1.解决数据入库失败时,一直重试入库的问题;
2.所有管道优化,入库失败时,自动切换成逐行入库,只丢弃异常记录
20201104 1.requests 中间件支持 DOWNLOAD_TIMEOUT、DOWNLOAD_DELAY
20201212 1.payload 中间件支持 DOWNLOAD_TIMEOUT、DOWNLOAD_DELAY;
2.get_sp_cookies 方法优化,使用轻量级的 splash 替换 selenium;
3.md 的原理部分增加去重策略的说明
20210105 1.增加布隆过滤器
20210217 1.elasticsearch 管道调整,兼容 elasticsearch7 以上版本,直接使用表名作为索引名
20210314 1.所有反爬中间件合并到 SPMiddleWare
20210315 1.使用更优雅的方式生成 job 初始请求;
2.headers 中间件优化,减少 redis 的内存占用;
3.删除 cookie 中间件,cookie 只是 headers 里面的一个值,可以直接使用 headers 中间件;
4.删除 Payload 中间件,Payload 请求可以直接使用 requests 中间件
5.增加 CookiesPool 中间件,用于需要多个账号随机切换采集的场景
20210317 1.增加可以脱离 scrapy 独立工作的、支持分布式的附件下载器
20210318 1.增加 api 服务
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容