学了两星期requests入门爬虫后,又学了一星期Scrapy。。
然后,我就被迫强上分布式爬爬虫了。。
也是应了一直以来我的学习心得,赶着DDL硬着头皮逼出来的,往往是学得最快的一个阶段。
还记得那天下午开始搞redis,网上其他大佬写的代码太高级没看懂,只好对着官方文档做些基础操作。搞到那天晚上11点多,分布式爬虫才总算动起来了,当晚一个通宵就把分布式爬虫框架做出来给组员整合代码。。
事实上花了最多时间是在安装上。。
至今还在想我为什么会那么傻。。
为什么要在电脑上装那么多Python27。。
我要安装使用的是annaconda里自带的那个,结果我在CMD下装的东西总是跑到C盘里面那个去,包括之前的pip
更新也是,只要是在CMD下执行的python ...
命令全部被狗吃了似的。。刚才也是装到头皮发麻才去pycharm里面看了一下环境插件,才知道插件跑别的地方去了。环境变量都改了也没用,果断卸载,不带手软!
Redis官方并不支持Windows版本,但是官网上给出了一个微软提供的git地址。
下载Redis-x64-3.2.100.msi双击安装,安装的时候建议添加到环境变量,设置数据库大小我直接用了500M。
接下来,在cmd下输入redis-cli
就可以进行数据库操作了,这也说明redis安装成功了。
只要你不是像我一样存在多个python的情况,一般你是可以直接按下述安装成功的:
先下载包:git地址
然后解压到python目录下,在该目录下运行python setup.py install
安装完成!
有了前面的依赖支撑,现在你可以轻松地安装:
pip install scrapy-redis
好了,你省了几小时的时间了。。
Redis数据库安装完成后,我们可以通过CMD来控制,但更方便的还可以下载一个可视化管理工具RedisDesktopManager。
那么什么是Redis呢?接下来:
好吧,如果你真的很懒->我可以容忍你的小偷懒
划重点:
简而言之,这就是一个数据库。那为什么我们要使用这个数据库,这还得说明一下分布式爬虫以及生产者消费模型。
以上一篇中国经济网的应用场景为例,我们考虑一下两种运作方式:
一般来说,我们爬虫的运作效率是像下面这样的,单线程,一个父URL处理成多个子URL再爬取数据完才开始处理下一个父URL。
这种方式的好处就是URL序列严格排列,数据处理井然有序。
现在考虑这样一种操作,一个线程/爬虫只负责处理父URL成子URL,接下来就不管了;剩下的部分由另一个线程来处理子URL。
这种方式支持了多线程爬虫,使得爬虫可以更加有效率地进行。而在这个模型中,父URL处理成子URL的进程可以成为生产者,而处理子URL的线程则是消费者。
上述分布式模型可以具现化为如下:
通过分离生产者、消费者并引入一个缓冲的连接池,这就形成了一个分布式爬虫系统。
综上,其实Redis使用在分布式爬虫中的作用就是这一个缓冲的连接池。在爬取过程中,生产者线程把父URL解析得到子URL后写入Redis数据库,消费者线程从数据库捕食任务。
也许你会问,这样跟保存在本地文件双线程跑有什么区别?区别就在于服务器,服务器是允许其它Machine访问的,这就可以实现:在Master-Machine上运行生产者,产生的子URL作为中间产物被众多Slave-Machine上的消费者线程消耗。
好吧,正文开始hhh
首先还是大概看一遍官方文档吧:点我送官方文档哦
在这里建立Scrapy工程的时候就不要scrapy genspider example www.example.com
这一步了,scrapy startproject cecn
之后打开spiders文件夹,新建master.py和slave.py,接下来一样是在这里开始你的表演,不过根据基本法(官方文档)你还是要先写一些必要工作。
首先,settings.py中添加如下内容:
# 确保所有爬虫通过redis共享相同的重复过滤器
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 启用在redis中调度存储请求序列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 不要清理redis队列,允许暂停/恢复抓取
SCHEDULER_PERSIST = True
# 使用优先级队列调度请求
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
REDIS_URL = None # 一般情况可以省去
REDIS_HOST = 'localhost' # 也可以根据情况改成 localhost
REDIS_PORT = 6379
items.py中添加如下内容:
# 开头添加
from sched import scheduler
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst, Join
class CecnItem(scrapy.Item):
# [...as before...]
class CecnLoader(ItemLoader):
default_item_class = CecnItem
default_input_processor = MapCompose(lambda s: s.strip())
default_output_processor = TakeFirst()
description_out = Join()
pipelines.py和middlewares.py无需改动
接下来编写爬虫工程的核心——生产者/消费者
master.py
from scrapy_redis.spiders import RedisSpider
import redis
class MySpider(RedisSpider):
name = 'produce' # 生产者
redis_key = 'MySpider:main_urls' # 这个Key存放着父URL
# 监听端口,一旦redis_key中传入了值便生成response,并回调parse()方法
def __init__(self, *args, **kwargs):
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MySpider, self).__init__(*args, **kwargs)
def parse(self, response):
# response这个对象包含了这个父URL页面的各项属性和数据
son_xpath = '//' # 子URL的xpath表达式
urls = response.xpath(son_xpath)
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
for url in urls:
r.lpush('MySpider:start_urls', url)
slave.py
from scrapy_redis.spiders import RedisSpider
from cecn.items import CecnItem
import scrapy
class MySpider(RedisSpider):
name = 'consume' # 消费者
redis_key = 'MySpider:start_urls' # 存放二级页面
# 监听端口,一旦redis_key中传入了值便生成response,并回调parse()方法
def __init__(self, *args, **kwargs):
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MySpider, self).__init__(*args, **kwargs)
#信息提取,生成item
def parse(self, response):
# 页面元素定位
yeild uitem
完成这两个部分,就可以开始运行测试了,在两个CMD窗口中分别运行
scrapy crawl produce
& scrapy crawl consume
然后!!
。。。这两个爬虫就会一直停在那里listen…
这是因为此时MySpider:main_urls
这个键里面没有任何值,爬虫缺少一个起始点。
你可以尝试在Python控制台运行:
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
r.lpush(`MySpider:main_urls`, [a right main_url])
你就能看到你的两个爬虫都动起来啦!
蜘蛛从散养变成批发!