[TOC]
## 1. 分布式爬虫
使用第三方库scrapy-redis,GitHub地址:https://github.com/rmax/scrapy-redis
1. Scrapy多个spider不能共享待爬取队列Scrapy queue, 即Scrapy本身不支持爬虫分布式。
### 1.1 架构
![](https://box.kancloud.cn/977dc54d05df123c98601a5a474881b4_1534x1010.png)
结构变化是在请求队列方面,由redis统一管理请求队列,协调多个spider服务。redis提供数据存储(爬取到的数据和待爬取请求URL)、指纹去重、
分布式爬虫与scrapy框架的改变主要是下边:
1. 有请求过来了,有spider引擎交个Scheduler,Scheduler将请求交给redis进行验证(指纹队列),如果该URL没有爬取,则redis将URL放回给Scheduler
2. redis与爬虫相关的三个队列
"yaoq:dupefilter" :去重指纹队列
"yaoq:items" : 存储爬取到的数据队列
"yaoq:requests" : 待爬取
## 2. 实践
* scrapy单机爬虫时,我们主要自己编写两个spider类:CrawlSpider和Spider,在分布式中对应RedisCrawlSpider和RedisSpider
### 2.1 spider
1. 继承RedisCrawlSpider
2. 没有start_urls(在redis中给定,多个爬虫端去随机获取)
~~~
pip3 install scrapy-redis
~~~
~~~
__author__ = 'dailin'
from scrapy_redis.spiders import RedisCrawlSpider
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapyredis.items import Yaoq
class YaoQ(RedisCrawlSpider):
name = 'yaoq'
allowed_domains = ['yaoq.net']
rules = (
# 只提取复合规则的页面链接,不做分析,所以跟页面但是没有,follow是对网易深一层的爬取,false表示不提取连接,也不请求页面上的连接
Rule(LinkExtractor(allow=r'www.yaoq.net/thread.*\.html'), callback='parse_item', follow=False),
Rule(LinkExtractor(allow=r'www.yaoq.net/forum-95-\d+\.html'), follow=True)
)
def parse_item(self, response):
try:
item = Yaoq()
# print(response.text)
author = response.xpath("//div[@class='pti']//div[@class='authi']/a[1]/text()").extract()[0]
authorLocation = response.xpath("//div[@class='pti']//div[@class='authi']/a[1]/@href").extract()[0]
pubDate = response.xpath("//div[@class='pti']//div[@class='authi']//em[1]/text()").extract()[0]
# 提取所有文本
content = \
response.xpath("//div[@class='pcb']//div[@class='t_fsz']/table[1]//tr")[0].xpath('string(.)').extract()[0]
contentData = content.replace("\r\n", "")
title = response.xpath("//span[@id='thread_subject']/text()").extract()[0]
print(author)
print(authorLocation)
print(pubDate)
print(contentData)
print(title)
item['title'] = title
item['pubDate'] = pubDate
item['author'] = author
item['authorLocation'] = authorLocation
item['content'] = contentData
item['id'] = str(uuid.uuid1())
yield item
except BaseException as e:
print(e)
~~~
### 2.2 settings
~~~
# -*- coding: utf-8 -*-
# 指定使用scrapy-redis的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 指定使用scrapy-redis的去重
DUPEFILTER_CLASS = 'scrapy_redis.dupefilters.RFPDupeFilter'
# 指定排序爬取地址时使用的队列,
# 默认的 按优先级排序(Scrapy默认),由sorted set实现的一种非FIFO、LIFO方式。
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
# 可选的 按先进先出排序(FIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue'
# 可选的 按后进先出排序(LIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderStack'
# 在redis中保持scrapy-redis用到的各个队列,从而允许暂停和暂停后恢复,也就是不清理redis queues、
# 中断后可以继续爬取
SCHEDULER_PERSIST = True
# 只在使用SpiderQueue或者SpiderStack是有效的参数,指定爬虫关闭的最大间隔时间
# SCHEDULER_IDLE_BEFORE_CLOSE = 10
# 通过配置RedisPipeline将item写入key为 spider.name : items 的redis的list中,供后面的分布式处理item
# 这个已经由 scrapy-redis 实现,不需要我们写代码
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 指定redis数据库的连接参数
# REDIS_PASS是我自己加上的redis连接密码(默认不做)
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
#REDIS_PASS = 'redisP@ssw0rd'
# LOG等级
LOG_LEVEL = 'DEBUG'
#默认情况下,RFPDupeFilter只记录第一个重复请求。将DUPEFILTER_DEBUG设置为True会记录所有重复的请求。
DUPEFILTER_DEBUG =True
~~~
### 2.3 运行爬虫
#### 2.3.1 运行爬虫
~~~
scrapy runspider scrapyredis/spiders/YaoQ.py
~~~
> 此时爬虫处于等待状态(因为没有给定start_urls)
#### 2.3.2 在redis中给定start_urls
1. 此时redis中,没有任何key
![](https://box.kancloud.cn/c2ff518b974965e7ebd03e89f323e64b_543x76.png)
2. 给定起始url(就是想队列中放入URL,让爬虫去爬)
>1. 这个起始的URL在redis中对应的键名默认是`spidername:start_urls`的组合,例如spider名称为
> yaoqyaoq,对应的键名为yaoq:start_urls。
> 2. 也可以在程序中显示的指定(start_url名称任意,但是按照规范来较好)
~~~
lpush yaoq:start_urls 'http://www.yaoq.net/forum-95-1.html'
~~~
> 此时爬虫开始,其他爬取到的URL也会存储到redis中
![](https://box.kancloud.cn/7d53c273c684893362ee2d4a39c71f49_964x140.png)
> start_urls被取走,这个队列消失,此时redis中有三个和该spider相关的队列
![](https://box.kancloud.cn/7d53c273c684893362ee2d4a39c71f49_964x140.png)
> 1) yaoq:dupefilter:去重队列
> 2) yaoq:items: 数据
> 3) yaoq:requests :待爬取队列
#### 2.3.3 处理爬取到的数据
从redis读取爬取的数据,爬取到的数据默认键是 spidername:items ,例如yaoq:items
把数据存储到mysql(一直阻塞地消费yaoq:items队列)
~~~
# coding=utf-8
__author__ = 'dailin'
import json
import redis
import pymysql
def main():
# 指定redis数据库信息
rediscli = redis.StrictRedis(host='192.168.56.130', port=6379, db=0)
# 指定mysql数据库
mysqlcli = pymysql.connect(host='192.168.56.130', user='root', passwd='tuna',
db='crawl_data', port=3306, charset="utf8", use_unicode=True)
while True:
# FIFO模式为 blpop,LIFO模式为 brpop,获取键值
source, data = rediscli.blpop(["yaoq:items"])
item = json.loads(data)
try:
# 使用cursor()方法获取操作游标
cur = mysqlcli.cursor()
sql = "INSERT INTO yaoq (author, author_location, content, id, pub_date, title) VALUES (%s, %s, %s, %s, %s, %s)"
# 使用execute方法执行SQL INSERT语句
content = item['content']
print(content)
cur.execute(sql,(item['author'], item['authorLocation'], item['content'] ,item['id'], item['pubDate'], item['title']))
# 提交sql事务
mysqlcli.commit()
# 关闭本次操作
cur.close()
print("inserted %s" % item['title'])
except Exception as e:
print(e)
if __name__ == '__main__':
main()
~~~
- Docker
- 什么是docker
- Docker安装、组件启动
- docker网络
- docker命令
- docker swarm
- dockerfile
- mesos
- 运维
- Linux
- Linux基础
- Linux常用命令_1
- Linux常用命令_2
- ip命令
- 什么是Linux
- SELinux
- Linux GCC编译警告:Clock skew detected. 错误解决办法
- 文件描述符
- find
- 资源统计
- LVM
- Linux相关配置
- 服务自启动
- 服务器安全
- 字符集
- shell脚本
- shell命令
- 实用脚本
- shell 数组
- 循环与判断
- 系统级别进程开启和停止
- 函数
- java调用shell脚本
- 发送邮件
- Linux网络配置
- Ubuntu
- Ubuntu发送邮件
- 更换apt-get源
- centos
- 防火墙
- 虚拟机下配置网络
- yum重新安装
- 安装mysql5.7
- 配置本地yum源
- 安装telnet
- 忘记root密码
- rsync+ crontab
- Zabbix
- Zabbix监控
- Zabbix安装
- 自动报警
- 自动发现主机
- 监控MySQL
- 安装PHP常见错误
- 基于nginx安装zabbix
- 监控Tomcat
- 监控redis
- web监控
- 监控进程和端口号
- zabbix自定义监控
- 触发器函数
- zabbix监控mysql主从同步状态
- Jenkins
- 安装Jenkins
- jenkins+svn+maven
- jenkins执行shell脚本
- 参数化构建
- maven区分环境打包
- jenkins使用注意事项
- nginx
- nginx认证功能
- ubuntu下编译安装Nginx
- 编译安装
- Nginx搭建本地yum源
- 文件共享
- Haproxy
- 初识Haproxy
- haproxy安装
- haproxy配置
- virtualbox
- virtualbox 复制新的虚拟机
- ubuntu下vitrualbox安装redhat
- centos配置双网卡
- 配置存储
- Windows
- Windows安装curl
- VMware vSphere
- 磁盘管理
- 增加磁盘
- gitlab
- 安装
- tomcat
- Squid
- bigdata
- FastDFS
- FastFDS基础
- FastFDS安装及简单实用
- api介绍
- 数据存储
- FastDFS防盗链
- python脚本
- ELK
- logstash
- 安装使用
- kibana
- 安准配置
- elasticsearch
- elasticsearch基础_1
- elasticsearch基础_2
- 安装
- 操作
- java api
- 中文分词器
- term vector
- 并发控制
- 对text字段排序
- 倒排和正排索引
- 自定义分词器
- 自定义dynamic策略
- 进阶练习
- 共享锁和排它锁
- nested object
- 父子关系模型
- 高亮
- 搜索提示
- Redis
- redis部署
- redis基础
- redis运维
- redis-cluster的使用
- redis哨兵
- redis脚本备份还原
- rabbitMQ
- rabbitMQ安装使用
- rpc
- RocketMQ
- 架构概念
- 安装
- 实例
- 好文引用
- 知乎
- ACK
- postgresql
- 存储过程
- 编程语言
- 计算机网络
- 基础_01
- tcp/ip
- http转https
- Let's Encrypt免费ssl证书(基于haproxy负载)
- what's the http?
- 网关
- 网络IO
- http
- 无状态网络协议
- Python
- python基础
- 基础数据类型
- String
- List
- 遍历
- Python基础_01
- python基础_02
- python基础03
- python基础_04
- python基础_05
- 函数
- 网络编程
- 系统编程
- 类
- Python正则表达式
- pymysql
- java调用python脚本
- python操作fastdfs
- 模块导入和sys.path
- 编码
- 安装pip
- python进阶
- python之setup.py构建工具
- 模块动态导入
- 内置函数
- 内置变量
- path
- python模块
- 内置模块_01
- 内置模块_02
- log模块
- collections
- Twisted
- Twisted基础
- 异步编程初探与reactor模式
- yield-inlineCallbacks
- 系统编程
- 爬虫
- urllib
- xpath
- scrapy
- 爬虫基础
- 爬虫种类
- 入门基础
- Rules
- 反反爬虫策略
- 模拟登陆
- problem
- 分布式爬虫
- 快代理整站爬取
- 与es整合
- 爬取APP数据
- 爬虫部署
- collection for ban of web
- crawlstyle
- API
- 多次请求
- 向调度器发送请求
- 源码学习
- LinkExtractor源码分析
- 构建工具-setup.py
- selenium
- 基础01
- 与scrapy整合
- Django
- Django开发入门
- Django与MySQL
- java
- 设计模式
- 单例模式
- 工厂模式
- java基础
- java位移
- java反射
- base64
- java内部类
- java高级
- 多线程
- springmvc-restful
- pfx数字证书
- 生成二维码
- 项目中使用log4j
- 自定义注解
- java发送post请求
- Date时间操作
- spring
- 基础
- spring事务控制
- springMVC
- 注解
- 参数绑定
- springmvc+spring+mybatis+dubbo
- MVC模型
- SpringBoot
- java配置入门
- SpringBoot基础入门
- SpringBoot web
- 整合
- SpringBoot注解
- shiro权限控制
- CommandLineRunner
- mybatis
- 静态资源
- SSM整合
- Aware
- Spring API使用
- Aware接口
- mybatis
- 入门
- mybatis属性自动映射、扫描
- 问题
- @Param 注解在Mybatis中的使用 以及传递参数的三种方式
- mybatis-SQL
- 逆向生成dao、model层代码
- 反向工程中Example的使用
- 自增id回显
- SqlSessionDaoSupport
- invalid bound statement(not found)
- 脉络
- beetl
- beetl是什么
- 与SpringBoot整合
- shiro
- 什么是shiro
- springboot+shrio+mybatis
- 拦截url
- 枚举
- 图片操作
- restful
- java项目中日志处理
- JSON
- 文件工具类
- KeyTool生成证书
- 兼容性问题
- 开发规范
- 工具类开发规范
- 压缩图片
- 异常处理
- web
- JavaScript
- 基础语法
- 创建对象
- BOM
- window对象
- DOM
- 闭包
- form提交-文件上传
- td中内容过长
- 问题1
- js高级
- js文件操作
- 函数_01
- session
- jQuery
- 函数01
- data()
- siblings
- index()与eq()
- select2
- 动态样式
- bootstrap
- 表单验证
- 表格
- MUI
- HTML
- iframe
- label标签
- 规范编程
- layer
- sss
- 微信小程序
- 基础知识
- 实践
- 自定义组件
- 修改自定义组件的样式
- 基础概念
- appid
- 跳转
- 小程序发送ajax
- 微信小程序上下拉刷新
- if
- 工具
- idea
- Git
- maven
- svn
- Netty
- 基础概念
- Handler
- SimpleChannelInboundHandler 与 ChannelInboundHandler
- 网络编程
- 网络I/O
- database
- oracle
- 游标
- PLSQL Developer
- mysql
- MySQL基准测试
- mysql备份
- mysql主从不同步
- mysql安装
- mysql函数大全
- SQL语句
- 修改配置
- 关键字
- 主从搭建
- centos下用rpm包安装mysql
- 常用sql
- information_scheme数据库
- 值得学的博客
- mysql学习
- 运维
- mysql权限
- 配置信息
- 好文mark
- jsp
- jsp EL表达式
- C
- test