scrapy分布式爬虫scrapy_redis二篇
=============================================================== Scrapy-Redis分布式爬虫框架 =============================================================== 1.Scrapy-Rdis-project: example (Scrapy-Redis分布式爬虫框架----范例说明(Dmoz网站) ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1.创建项目--- scrapy startproject example | example/ | ├── scrapy.cfg | └── example | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py | └── spiders | ├── __init__.py | └── my_crawlspider.py/my_redisspider.py/my_rediscrawlspider.py # 3中类型的Scrap-Redis爬虫 | | 2.明确目标--- vim items.py | vim items.py | import scrapy | | class ExampleItem(scrapy.Item): | name = scrapy.Field() | description = scrapy.Field() | link = scrapy.Field() | crawled = scrapy.Field() | spider = scrapy.Field() | url = scrapy.Field() | | 3.编写自定义pipeline--- vim pipelines.py | vim pipelins.py | from datetime import datetime | class ExampPipeline(object): | def process_item(self,item,spider): | item['crawled'] = datetime.utcnow() # 调用datetime.utcnow()方法获取爬虫执行时的UTC时间 | item['spider'] = spider.name # 调用spider.name属性获取当前爬虫名(因为可能同时有多个爬虫在爬取,这样可以看到谁爬了哪些网页) | return item | | 4.注册自定义pipeline及Scrapy-Redis分布式爬虫相关设置--- vim settings.py | vim settings.py | #-----------Scrapy-Redis分布式爬虫相关设置如下------------- | DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件 | SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器 | SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可: | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求 | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack" | SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面) | | REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中 | REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1 | | #----------注册RedisPipeline/自定义pipeline------------------ | ITEM_PIPELINES = { | "example.pipelines.ExampPipeline":300, # 自定义pipeline视情况选择性注册(可选) | "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis) | } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item | | 5.制作爬虫(三种Scrapy-Redis爬虫--- CrawlSpider/RedisSpider/RedisCrawlSpider) | | | 类型一:基于CrawlSpider类的Scrapy-Redis分布式爬虫 (无需任何修改) | ================================================================================= | (1)生成爬虫--- scrapy genspider -t crawl my_crawlspider "dmoz.org" | (2)设置爬虫--- vim my_crawlspider.py | vim my_crawlspider.py | from scrapy.linkextractor import LinkExtractor | from scrapy.Spiders import CrawlSpider,Rule | | class My_crawlspider(CrawlSpider): | name = "my_crawlspider" | allowed_domains = ["dmoz.org"] | start_urls = ["http://www.dmoz.org/"] | links = LinkExtractor(restrict_css('.top-cat','.sub-cat','.cat-item')) | rules = [ | Rule(links,callback='parse_directory',follow=True), | ] | | def parse_directory(self,response): | for div in response.css('.title-and-desc'): | yield { 'name':div.css('.site-title::text').extract_first(), | 'description':div.css('.site-descr::text').extract_first(), # 直接将name/description/link存入Redis数据库 | 'link':div.css('.a::attr(href)').extract(), | } | (3)执行爬虫方法--- scrapy crawl my_crawlspider (与正常scrapy一样,无需redis_key,比较鸡肋并不是真正的多机器爬虫) | | | | 类型二:基于RedisSpider类的Scrapy-Redis分布式爬虫 (需要部分删改) | ================================================================================= | (1)生成爬虫--- scrapy genspider my_redisspider "dmoz.org" | (2)设置爬虫--- vim my_redisspider.py | vim my_redisspider.py | from scrapy_redis.Spiders import RedisSpider # 变化1:从scrapy_redis.Spiders中引入RedisSpider | | class My_redisspiderSpider(RedisSpider): # 变化2:爬虫类所继承的父类变为RedisSpider类 | name = 'my_redisspider' | redis_key = "my_redisspider:start_urls" # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls | | def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域 | domain = kwargs.pop('domain','') | self.allowed_domain = filter(None,domain,split(',')) | super(My_redisspiderSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同 | | def parse(self,response): | return { 'name':response.css('.site-title::text').extract_first(), | 'url':respons.url, # 直接将name/url存入Redis数据库 | } | (3)执行爬虫方法 # 变化5:爬虫执行方法变化 | <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider my_redisspider.py | <2>在master端redis上发号施令--- lpush my_redisspider:start_urls http://www.dmoz.org/ | | | | 类型三:基于RedisCrawlSpider类飞Scrapy-Redis分布式爬虫 (需要部分删改) | ================================================================================= | (1)生成爬虫--- scrapy genspider -t crawl my_rediscrawlspider "dmoz.org" | (2)设置爬虫--- vim my_rediscrawlspider.py | vim my_rediscrawlspider.py | from scrapy.linkextractor import LinkExtractor | from scrapy.Spiders import CrawlSpider,Rule | from scrapy_redis.Spiders import RedisCrawlSpider # 变化1:从scrapy_redis.Spiders中引入RedisCrawlSpider | | class My_rediscrawlspiderSpider(RedisCrawlSpider): # 变化2:爬虫类所继承的父类变为RedisCrawlSpider类 | name = 'my_rediscrawlspider' | redis_key = 'my_rediscrawlspider:start_urls' # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls | | rules =[ Rule(LinkExtractor(),callback='parse_page',follow=True), ] | | def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域 | domain = kwargs.pop('domain','') | self.allowed_domain = filter(None,domain,split(',')) | super(My_rediscrawlspiderSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同 | | def parse_page(self,response): | return { 'name':response.css('.site-title::text').extract_first(), | 'url':respons.url, # 直接将name/url存入Redis数据库 | } | (3)执行爬虫方法 # 变化5:爬虫执行方法变化 | <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider my_rediscrawlspider.py | <2>在master端redis上发号施令--- lpush my_rediscrawlspider:start_urls http://www.dmoz.org/ | | 小结: | 如果只想使用Redis的去重和保存功能 ---- 使用类型一 | 如果写分布式 ---- 根据情况选择类型二/类型三 | 如果写聚焦爬虫 ---- 选择类型三 | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- 2.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶1----有缘网:非分布式基于CrawlSpider的scrapy项目 ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1.创建项目--- scrapy startproject youyuan | youyuan/ | ├── scrapy.cfg | └── youyuan | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py | └── spiders | ├── __init__.py | └── yy.py | | 2.明确目标--- vim items.py | vim items .py | improt scrapy | | class YouyuanItem(scrapy.Item): | username = scrapy.Field() # 用户名 | age = scrapy.Field() # 年龄 | header_url = scrapy.Field() # 头像地址 | image_url = scrapy.Field() # 相册个图片地址 | content = scrapy.Field() # 内心独白 | place_from = scrapy.Field() # 籍贯 | education = scrapy.Field() # 教育 | hobby = scrapy.Field() # 爱好 | source_url = scrapy.Field() # 个人主页 | source = scrapy.Field() # 数据来源网站 | | 3.制作爬虫 | (1)生成爬虫--- scrapy genspider -t crawl yy "youyuan.com" | (2)设置爬虫--- vim yy.py | vim yy.py | import scrapy | from scrapy.linkextractor import LinkExtractor | from scrapy.Spiders import CrawlSpider,Rule | from youyuan.items import YouyuanItem | import re | | class YySpider(CrawlSpider): | name = "yy" | allowed_domains = ['youyuan.com'] | start_urls = ["http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/"] | | page_links = LinkExtractor(allow=(r'youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/')) # 获取每个显示页的连接 | person_links = LinkExtractor(allow =(r'youyuan.com/\d+-profile/')) # 获取个人主页 | rules = ( | Rule(page_links), # 没有callback/没有follow,默认follow=True继续跟进 | Rule(person_links,callback='parse_item'), # 有callback/没有follow,默认follow=False不继续跟进 | ) | def parse_item(self,response): | item = YouyuanItem() | item['username'] = self.get_username() | item['age'] = self.get_age() | item['header_url'] = self.get_header_url() | item['image_url'] = self.get_image_url() | item['content'] = self.get_content() | item['place_from'] = self.get_place_from() | item['education'] = self.get_education() | item['hobby'] = self.get_hobby() | item['source_url'] = self.get_source_url() | item['source'] = self.get_source() | | #-----所谓不再使用土枪土炮,看起来高大上的方法---------- | def get_username(self.response): | username = response.xpath('//dl[@class="person_cen"]//div[@class="main"]/strong/text()').extract() | if len(username): | username = username[0] | else: | username = "Null" | return username.strip() | def get_age(self,response): | age = response.xpath('//dl/[@class="person_cen"]//dd/p/text()').extract() | if len(age): | age = re.find_all(u"\d+岁",age[0])[0] | else: | age = "Null" | return age.strip() | def get_header_url(self,response): | header_url = response.xpath('//dl[@class="person_cen"]//dt/img/@src').extract() | if len (header_url): | header_url = header_url[0] | else: | header_url = "Null" | return header_url.strip() | def get_image_url(self,response): | image_url = response.xpath('//div[@class="ph_show"]/ul/li/a/img/@src').extract() | if len(image_url): | image_url = "|".join(image_url) # 这样生成这种形式:xxxx|xxxx|xxxx| | else: | image_url = "Null" | return image_url | def get_content(self,response): | content = response.xpath('//div[@class="pre_data"]/ul/li[2]//ol[1]/span/text()').extract() | if len(content): | content = content[0] | else: | content = "Null" | return content.strip() | def get_place_from(self,response): | place_from = response.xpath('//div[@class="pre_data"]/ul/li[2]//ol[2]/li[1]/span/text()').extract() | if len(place_from): | place_from = place_from[0] | else: | place_from = "Null" | return place_from.strip() | def get_education(self,response): | education = response.xpath('//div[@class="pre_data"]/ul/li[3]//ol[2]//li[2]/span/text()').extract() | if len(education): | education = education[0] | else: | education = "Null" | return education.strip() | def get_hobby(self,response): | hobby = response.xpath('//dl[@class="person_cen"]//ol/li/text()').extract() | if len(hobby): | hobby = ",".join(hobby).replace(" ","") | else: | hobby = "Null" | return hobby.strip() | | 4.编写item pipeline--- vim pipelines.py | vim pipelines.py | import json | | class YouyuanJsonPipeline(obeject): | def __init__(self): | self.f = open("youyuan.json","w") | def process_item(self,item,spider): | text = json.dumps(dict(item),ensure_ascii=False) + ",\n" | self.f.write(item) | def close_spider(self,spider): | self.f.close() | | 5.启动上述pipeline--- vim settings.py | vim settings.py | ITEM_PIPELINES = {"youyuan.pipelines.YouyuanJsonPipeline":300} | | 6.执行爬虫--- scrapy crawl yy | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- 3.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶2----有缘网:非分布式基于CrawlSpider的scrapy项目的数据存入本机Redis ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 说明:仅仅实在上述scrapy项目的基础上进行settings.py文件的点滴修改,增加一个RedisPipeline而已 <----并不属于Scrapy-Redis分布式 | youyaun/ | ├── scrapy.cfg | └── youyaun | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py <----仅对settings.py文件做部分添加修改 | └── spiders | ├── __init__.py | └── yy.py | | 5.settings.py添加部分信息--- 启用Scrapy-Redis的去重组件/调度器/取出请求方式,以及注册RedisPipeline组件(让数据存入Redis) | vim settings.py | DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件 | SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器 | SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可: | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求 | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack" | SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面) | | #REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,注释或不写默认将数据存放到本机的Redis中 | #REDIS_PORT = 6379 | | #----------注册RedisPipeline/自定义pipeline------------------ | ITEM_PIPELINES = { | "youyuan.pipelines.YouyuanJsonPipeline":300, # 自定义pipeline视情况选择性注册(可选) | "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis) | } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item | | 6.执行爬虫--- scrapy crawl yy | | # 注意: 在原始scrapy项目的基础上,在settings.py文件中添加上述几行设置,就可以将scrapy爬取的数据存放到本机redis中 | # 注意: 上述要想成功保存到本机Redis,有两个前提:本机必须(pip install scrapy-redis) 和本机redis必须启动(redis-server /etc/redis.conf) | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- 4.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架进阶3----有缘网:非分布式基于CrawlSpider的scrapy项目 ----> 改写为:RedisCrawlSpider类的Scrapy-Redis分布式爬虫项目 ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 说明:仅仅实在原始scrapy项目的基础上对settings.py/yy.py文件进行的点滴修改即可 | youyaun/ | ├── scrapy.cfg | └── youyaun | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py <----对settings.py文件做部分添加修改(使用Scrapy-Redis的去重组件/调度器/取出请求策略/允许暂停/指明远程Redis主机,并注册RedisPipeline组件) | └── spiders | ├── __init__.py | └── yy.py <----对爬虫文件进行部分修改(引入RedisCrawlSpider爬虫类/去掉allowed_domain/去掉start_urls/增加redis_key/改写init方法动态获取限制域) | | 1.修改设置文件--- vim settings.py | vim settings.py | DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件 | SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器 | SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可: | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求 | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack" | SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面) | | REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中 | REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1 | | #----------注册RedisPipeline/自定义pipeline------------------ | ITEM_PIPELINES = { | "youyuan.pipelines.YouyuanJsonPipeline":300, # 自定义pipeline视情况选择性注册(可选) | "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis) | } # 注意:自定义pipeline的优先级需高于Redispipeline,因为RedisPipeline不会返回item,所以如果RedisPipeline优先级高于自定义pipeline,那么自定义pipeline无法获取到item | | 2.修改爬虫文件--- vim yy.py | vim yy.py | import scrapy | from scrapy linkextractor import LinkExtractor | from scrapy.Spiders import CrawlSpider,Rule | from youyuan.items import YouyuanItem | import re | from scrapy_redis.Spiders import RedisCrawlSpider # 变化1:从scrapy_redis.Spiders中引入RedisCrawlSpider | | class YySpider(RedisCrawlSpider): # 变化2:爬虫类所继承的父类变为RedisCrawlSpider类 | name = "yy" | redis_key = "yyspider:start_urls" # 变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls | | def __init__(self,*args,**kwargs): # 变化4:重写__init__方法:动态获取限制域 | domain = kwargs.pop('domain','') | self.allowed_domain = filter(None,domain,split(',')) | super(YySpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同 | | page_links = LinkExtractor(allow=(r'youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/')) | person_links = LinkExtractor(allow =(r'youyuan.com/\d+-profile/')) | ....... | ....... # 后面的代码都相同 | | 3.爬虫的执行方式改变 | <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider yy.py | <2>在master端redis上发号施令--- lpush yyspider:start_urls http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- 5.Scrapy-Rdis-project: sina2 (Scrapy-Redis分布式爬虫框架----新浪分类资讯:非分布式基于scrapy.Spider的scrapy项目 ----> 改写为:RedisSpider类的Scrapy-Redis分布式爬虫项目 ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 改写该项目注意:由于改写后数据存放到Redis,所以需要去掉"目录存储路径"相关的代码 | | 1.创建项目--- scrapy startproject sina2 | sina2/ | ├── scrapy.cfg | └── sina2 | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py <----对settings.py文件做部分添加修改(使用Scrapy-Redis的去重组件/调度器/取出请求策略/允许暂停/指明远程Redis主机,并注册RedisPipeline组件) | └── spiders | ├── __init__.py | └── xinlang.py <----对爬虫文件进行部分修改(引入RedisCrawlSpider爬虫类/去掉allowed_domain/去掉start_urls/增加redis_key/改写init方法动态获取限制域) | | 2.明确目标--- vim items.py | vim items.py | import scrapy | | class SinaItem(scrapy.Item): | parent_title = scrap.Field() # 大类标题 | parent_url = scrap.Field() # 大类URL | sub_title = scrap.Field() # 小类标题 | sub_url = scrap.Field() # 小类URL | #sub_filename = scrapy.Field() # 小类目录存储路径(数据存放到Redis,不在需要这块代码) | son_url = scrap.Field() # 小类的子链接 | article_title = scrap.Field() # 文章标题 | article_content = scrap.Field() # 文章内容 | | 3.制作爬虫 | (1)生成爬虫--- scrapy genspider xinlang "sina.com.cn" | (2)设置爬虫--- vim xinlang.py | vim xinlang.py | import scrapy | import os | from sina.items import SinaItem | import sys | from scrapy_redis.Spiders import RedisSpider ###<-----变化1:从scrapy_redis.Spiders中引入RedisSpider | | reload(sys) | sys.setdefaultencoding('utf-8') | | class XinlangSpider(RedisSpider): ###<-----变化2:爬虫类所继承的父类变为RedisSpider类 | name = 'xinlang' | redis_key = "xinlangspider:start_urls" ###<-----变化3:多了一个对所有爬虫发号施令的redis_key,少了allowed_domain和start_urls | | def __init__(self,*args,**kwargs): ###<-----变化4:重写__init__方法:动态获取限制域 | domain = kwargs.pop('domain','') | self.allowed_domain = filter(None,domain,split(',')) | super(XinlangSpider,self).__init__(*args,**kwargs) # 注意super()里面的参数因爬虫类名不同而不同 | | def parse(self,response): | items = [] | parent_title_list = response.xpath('//div[@id=\"tab01\"]/div/h3/a/text()').extract() | parent_url_list = response.xpath('//div[@id=\"tab01\"]/div/h3/a/@href').extract() | sub_title_list = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/text()').extract() | sub_url_list = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/@href').extract() | | for i in range(0,len(parent_title_list)): | #parent_filename = "./Data/" + parent_title_list[i] # 创建大类的存放目录(若存在则不创建,若不存在则重新创建) | #if(not os.path.exists(parent_filename)): # (数据存放到Redis,不在需要这块代码) | # os.makedirs(parent_filename) | | for j in range(0,len(sub_url_list)): # 实例化SinaItem()并保存大类的URL和标题 | item = SinaItem() | item['parent_title'] = parent_title_list[i] | item['parent_url'] = parent_url_list[i] | if_belong = sub_url_list[i].startwith(item['parent_url']) # 判断小类URL是否以大类URL开头(即判断小类是否属于大类) | if (if_belong): | #sub_filename = parent_filename + "/" + sub_title_list[j] # 如果属于该大类,则判断小类存放目录是否存在,不存在则新建该目录 | #if (not os.path.exists(sub_filename)): # (数据存放到Redis,不在需要这块代码) | # os.makedirs(sub_filename) | item['sub_title'] = sub_title_list[j] # 保存小类的标题/URL/存放目录,并将目前所获取item信息追加到items列表中保存 | item['sub_url'] = sub_url_list[j] | item['sub_filename'] = sub_filename | items.append(item) | for item in items: # 逐一取出子类的url,并附带上meta信息(即item),将其加入请求队列,使用second_parse()函数处理其返回的响应 | yield scrapy.Request(url=item['sub_url'],meta={'meta_1':item},callback=self.second_parse) | | def second_parse(self,response): | meta_1 = response.meta['meta_1'] # 将meta对应的item信息赋值给meta_1(即,meta_1 = item) | son_url_list = response.xpath('//a/@href').extract() # 匹配获取返回的孙类的URL列表 | items = [] | for i in range(0,len(son_url_list)): # 循环取出孙类URL判断其是否属于某个大类(以大类的URL开头)和是否是文章(以.shml结尾),如果属于则将该孙类URL保存起来 | if_belong = son_url_list[i].endwith('.shtml') and sub_url_list[i].startwith(meta_1['parent_url']) | if (if_belong): | item = SinaItem() | item['parent_title'] = meta_1['parent_title'] | item['parent_url'] = meta_1['parent_url'] | item['sub_title'] = meta_1['sub_title'] | item['sub_url'] = meta_1['sub_url'] | #item['sub_filename'] = meta_1['sub_filename'] # (数据存放到Redis,不在需要这块代码) | item['son_url'] = son_url_list[i] | items.append(item) | for item in items: # 逐一取出孙类的url,并附带上meta信息(即第二次的item),将其加入请求队列,使用third_parse()函数处理其返回的响应 | yield scrapy.Request(url=item['son_url'],meta={'meta_2':item},callback=self.third_parse) | | def third_parse(self,response): | item = response.meta['meta_2'] # 将meta对应的(第二次获取更新的item信息)赋值给这里的item(即,item = item) | article_content = "" # 从孙类URL返回响应中匹配出文章标题和文章内容并保存进item | article_title_list = response.xpath('//hi[@id=\"main_title\"]/text()').extract() | article_content_list = response.xpath('//div[@id=\"artibody\"]/p/text()').extract() | for content_part in article_content_list: | article_content += content_part # 通过循环拼接成完整的文章内容 | item['article_title'] = article_title_list[0] | item['article_content'] = article_content | yield item # 将数据收集完整的item传递给pipeline处理 | | 4.编写item pipelines--- vim pipelines.py (忽略) # (数据存放到Redis,不再需要这块代码,不需要自定义pipeline保存数据到本地) | | 5.启用上述pipeline组件--- vim settings.py | vim settings.py | DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用Scrapy-Redis的去重组件,不再使用scrapy的去重组件 | SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用Scrapy-Redis的调度器,不再使用scrapy的调度器 | SCHEDULER_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 使用Scrapy-Redis的从请求集合中取出请求的方式,三种方式择其一即可: | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderQueue" # 分别按(1)请求的优先级/(2)队列FIFO/(3)栈FILO 取出请求 | #SCHEDULER_CLASS = "scrapy_redis.queue.SpiderStack" | SCHEDULER_PERSIST = True # 允许暂停,redis请求记录不会丢失(重启爬虫不会重头爬取已爬过的页面) | | REDIS_HOST = "200.200.200.200" # 这两项是Redis连接设置,如果注释或不写会默认将数据存放到本机的Redis中 | REDIS_PORT = 6379 # 注意:master端的Redis需要允许远程连接--配置中注释掉bind 127.0.0.1 | | ITEM_PIPELINES = { #"sina.pipelines.SinaSavePipeline":300, # (数据存放到Redis,不再需要这块代码,不需要自定义pipeline保存数据到本地) | "scrapy_redis.pipelines.RedisPipeline":400 # 将RedisPipeline注册到pipeline组件中(这样才能将数据存入Redis) | } | | 6.爬虫的执行方式改变 | <1>将项目代码复制到给台slave上(可更改爬虫名)并启动爬虫--- scrapy runspider xinlang.py | <2>在master端redis上发号施令--- lpush yyspider:start_urls http://news.sina.com.cn/guide/ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- 6.Scrapy-Rdis-project: youyuan (Scrapy-Redis分布式爬虫框架----将Redis中数据持久化存储到MongoDB/MySQL中----> 将有缘网分布式爬取到Redis中的数据转存到MongoDB/MySQL中) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 要将Scrapy-Redis项目爬取到Redis中的数据转存到Mongodb/MySQL中,只需要在项目一级目录下创建两个转存的脚本文件即可 | | 有缘网Scrapy-Redis项目树形图 | youyuan/ | ├── scrapy.cfg | ├── process_item_for_mongodb.py <----项目一级目录下创建将Redis数据转存到mongodb的python脚本 | ├── process_item_for_mysql.py <----项目一级目录下创建将Redis数据转存到mysql的python脚本 | └── youyuan | ├── __init__.py | ├── items.py | ├── middlewares.py | ├── pipelines.py | ├── settings.py | └── spiders | ├── __init__.py | └── yy.py | | | 1.将Redis数据转存到mongodb中 ----- vim process_item_for_mongodb.py | vim process_item_for_mongodb.py | #!/usr/bin/env python | #coding:utf-8 | | import redis | import pymongo | import json | def process_item(): | rediscli = redis.Redis(host='200.200.200.200',port=6379,db=0) # 创建Redis连接对象 | mongocli = pymongo.MongoClient(host='200.200.200.202',port=27017) # 创建MongoDB连接对象 | db_name = mongocli['youyuan'] # 利用MongoDB连接对象在MongoDB中创建名为youyuan的数据库对象 | sheet_name = db_name['beijing18-24'] # 利用该数据库对象在youyuan数据库中创建名为beijing18-24的表对象 | count = 0 | while True: | source,data = rediscli.blpop("yy:items") # 使用循环通过redis连接对象的blpop()方法,不断取出redis中的数据(blpop即FIFO,rlpop即FILO) | data = json.loads(data) # 将取出的json字符串类型的数据转化为python类型的对象 | sheet_name.insert(data) # 利用mongodb的表对象的insert()方法,向表中插入(刚才转化的python对象) | count += 1 | print "已经成功从redis转移" + str(count) + "条数据到mongodb" | | if __name__ == "__main__": | process_item() | | 注意:MongoDB中可以自动创建键,所以直接执行该脚本就可转移数据 | | | 2.将Redis数据转存到mysql中 ----- vim process_item_for_mysql.py | vim process_item_for_mysql.py | import redis | import MySQLdb | import json | def process_item(): | rediscli = redis.Redis(host='200.200.200.200',port=6379,db=0) # 创建Redis连接对象 | mysqlcli = MySQLdb.connect(host=') # 创建MySQL连接对象 | count = 0 | while True: | source,data = rediscli.blpop('yy:items') # 使用循环通过redis连接对象的blpop()方法,不断取出redis中的数据(blpop即FIFO,rlpop即FILO) | data = json.loads(data) # 将取出的json字符串类型的数据转化为python类型的对象 | try: | cursor = mysqlcli.cursor() # 利用mysql连接对象创建cursor操作游标,并使用该操作游标向Mysql表中插入数据,数据通过python对象获取其值 | cursor.execute("insert into beijing18-24 (username,age,spider,crawled) values(%s,%s,%s,%s)",[data['username'],data['age'],data['spider'],data['crawled']]) | mysqlcli.commit() # 插入完成后需提交事务 | cursor.close() # 关闭操作游标 | count += 1 | print "已经成功从redis转移" + str(count) + "条数据到mongodb" | except: | pass | | if __name__ == "__main__": | process_item() | | 注意:MySQL中不能自动创建字段,所以在执行该脚本前,需要自行在数据库中创建好响应的数据库/表/字段,然后才能执行该脚本,转移数据到MySQL中 | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
scrapy分布式爬虫scrapy_redis二篇的更多相关文章
- scrapy分布式爬虫scrapy_redis一篇
分布式爬虫原理 首先我们来看一下scrapy的单机架构: 可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页 ...
- 第三百五十六节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点
第三百五十六节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点 1.分布式爬虫原理 2.分布式爬虫优点 3.分布式爬虫需要解决的问题
- scrapy 分布式爬虫- RedisSpider
爬去当当书籍信息 多台机器同时爬取,共用一个redis记录 scrapy_redis 带爬取的request对象储存在redis中,每台机器读取request对象并删除记录,经行爬取.实现分布式爬虫 ...
- Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?
使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...
- 三十五 Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点
1.分布式爬虫原理 2.分布式爬虫优点 3.分布式爬虫需要解决的问题
- Scrapy分布式爬虫打造搜索引擎- (二)伯乐在线爬取所有文章
二.伯乐在线爬取所有文章 1. 初始化文件目录 基础环境 python 3.6.5 JetBrains PyCharm 2018.1 mysql+navicat 为了便于日后的部署:我们开发使用了虚拟 ...
- Centos7__Scrapy + Scrapy_redis 用Docker 实现分布式爬虫
原理:其实就是用到redis的优点及特性,好处自己查--- 1,scrapy 分布式爬虫配置: settings.py BOT_NAME = 'first' SPIDER_MODULES = ['fi ...
- 【Python3爬虫】爬取美女图新姿势--Redis分布式爬虫初体验
一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...
- 【Python3爬虫】学习分布式爬虫第一步--Redis分布式爬虫初体验
一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...
随机推荐
- 使用schemaExport自动生成表结构
一.Hibernate原生状态 ? 1 2 3 4 5 Configuration cfg = new Configuration().configure(); SchemaExport expo ...
- linux 下使用 tc 模拟网络延迟和丢包
1 模拟延迟传输简介 netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块.该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带 ...
- OpenShift实战(七):OpenShift定制镜像S2I
1.基础镜像制作 由于公司的程序是Java开发,上线发布使用的是maven,如果使用openshift自带的S2I,每次都会全量拉取代码(代码比较多,每次全量拉太慢),然后每次打包都会再一次下载mav ...
- Java中常用的数据结构类
结构体系图 List ArrayList.LinkedList.Vector有什么区别? ArrayList 只能装入引用对象(基本类型要转换为封装类): 线程不安全: 底层由数组实现(顺序表),因为 ...
- 6 Tools To Jump Start Your Video Content Marketing
http://www.forbes.com/sites/drewhendricks/2014/10/16/6-tools-to-jump-start-your-video-content-market ...
- udp服务端收发数据流程
1.创建服务端的socket以便开始通讯.2.绑定ip以及端口号,这样客户端才能找到这个程序.3.因为本地网卡不止一个所以尽量不写死,一般用""空来表示所有本地网卡.4.接下来开始 ...
- java基础- Collection和map
使用构造方法时,需要保留一个无参的构造方法 静态方法可以直接通过类名来访问,而不用创建对象. -- Java代码的执行顺序: 静态变量初始化→静态代码块→初始化静态方法→初始化实例变量→代码块→构造方 ...
- session熟知
Session 服务器端保存会话信息的技术. //如何获得session HttpSession session = request.getSession(); //如何操作session sessi ...
- 单片机开发——02工欲善其事必先利其器(Proteus软件安装破解)
在单片机开发工程中,博主经常通过模拟软件Proteus进行模拟仿真,将编译生成的"HEX"文件下载在单片机芯片中,然后进行后期的debug工作,当模拟仿真完成之后,进行硬件测试部分 ...
- Django 项目创建之前的环境搭建
安装虚拟管理环境 sudo apt install virtualenv 在项目中运行 virtualenv fruitenv 进入到环境中 source fruitenv/bin/activate ...