- 分布式爬虫优点:
- 可以充分利用多台机器的带宽
- 可以充分利用多台机器的ip地址(同一个局域网内用的还是一个,分布式没有用)
- 多台机器做,爬取效率更高
- 分布式爬虫需要解决的问题
- 分布式爬虫是好几台机器在同时运行,如何保证不同的机器爬取页面的时候不会出现重复爬取的问题
- 同样,分布式爬虫在不同的机器上运行,如何把数据爬完后保证保存在同一个老地方
scrapy-redis是一个组件不是框架,可以集成到scrapy框架中,使得爬虫可以进行分布式。可以充分的利用资源
pip install scrapy-redis
编写scraoy-redis分布式爬虫:
只要在基础的scrapy项目中修改以下三点就可以(基础的scrpay项目可以运行再修改为最佳)
- 将scrapy.Spider变成scrapy_redis.spider.RedisSpider;或者是从scrapy.CrawlSpider变成scrapy_redis.spider.RedisCrawlSpider
- 将爬虫的start_urls删掉增加一个redis_key="XXX"这个是为了以后指明从redis上的key读取url
- 在setting文件配置项中添加如下配置
#确保再爬取过程中去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#确保request存储到redis
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#数据不消失(暂停 重启)
SCHEDULER_PERSIST = True
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,#数据存储在redis的pipeline中共享一个存储
}
REDIS_HOST=''
REDIS_PORT=''
- 启动爬虫分为两个步骤
- scrapy crawl spider 让爬虫就绪
- 在redis中输入lpush XXX urls让爬虫从这个url开始爬取(xxx 为上方第二个步骤redis_key urls为开始爬取的url地址)
以下是利用分布式爬取房天下的租房信息:
item.py文件:
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class FangtianxiaItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
province=scrapy.Field()
city=scrapy.Field()
name=scrapy.Field()
price=scrapy.Field()
rooms=scrapy.Field()
area=scrapy.Field()
address=scrapy.Field()
sale=scrapy.Field()
origin_url=scrapy.Field()
class ESFItem(scrapy.Item):
province = scrapy.Field()
city = scrapy.Field()
name = scrapy.Field()
price = scrapy.Field()
rooms = scrapy.Field()
floor=scrapy.Field()
toward=scrapy.Field()
area = scrapy.Field()
address = scrapy.Field()
year=scrapy.Field()
unity=scrapy.Field()
origin_url = scrapy.Field()
spider.py文件(一部分修改成分布式的具体代码)
mport scrapy
import re
from fangtianxia.items import FangtianxiaItem,ESFItem
from scrapy_redis.spiders import RedisSpider#导入scrapy-redis包
class SfwSpider(RedisSpider):#修改继承的类
name = 'sfw'
allowed_domains = ['fang.com']
#start_urls = ['https://www.fang.com/SoufunFamily.html']
#修改起始url地址
redis_key = "fang:start_urls"#从这里读取url 随遗取即可
def parse(self, response):
trs=response.xpath("//div[@class='outCont']//tr")
province=None
for tr in trs:
tds=tr.xpath(".//td[not(@class)]")
province_td=tds[0]
province_text=province_td.xpath(".//text()").get()
province_text=re.sub(r"\s","",province_text)
if province_text:
province=province_text
if province=='其它':
#如果爬取的是海外城市则不进行爬取
continue
city_td=tds[1]
city_links=city_td.xpath(".//a")
for city_link in city_links:
city=city_link.xpath(".//text()").get()
city_url=city_link.xpath('.//@href').get()
module=city_url.split('.')
one = module[0]
mid = module[1]
last = module[2]
newhouse_url=one + '.newhouse.' + mid + '.' + last + 'house/s/'
esf_url=one+'.esf.'+mid+'.'+last
if 'bj' in one:
newhouse_url='https://newhouse.fang.com/house/s/'
esf_url='https://esf.fang.com/'
yield scrapy.Request(url=newhouse_url,callback=self.parse_newhouse,meta={'info':(province,city)})
yield scrapy.Request(url=esf_url,callback=self.parse_esf,meta={'info':(province,city)},dont_filter=True)
def parse_newhouse(self,response):
pass
def parse_esf(self,response):
pass
setting.py文件(针对scrapy-redis自u改的代码)
#确保再爬取过程中去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#确保request存储到redis
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#使用队列的形式
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#数据不消失(暂停 重启)
SCHEDULER_PERSIST = True
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
#也可自加一些pipelines文件
}
#redis的ip地址和端口
REDIS_HOST='XXXXX'
REDIS_PORT='xxx'
LOG_LEVEL = 'DEBUG'
完整代码已分享与github上请自行浏览fork