目標任務:將之前新浪網的Scrapy爬蟲項目,修改為基於RedisSpider類的scrapy-redis分布式爬蟲項目,將數據存入redis數據庫。
一、item文件,和之前項目一樣不需要改變
# -*- coding: utf-8 -*- import scrapy import sys reload(sys) sys.setdefaultencoding("utf-8") class SinanewsItem(scrapy.Item): # 大類的標題和url parentTitle = scrapy.Field() parentUrls = scrapy.Field() # 小類的標題和子url subTitle = scrapy.Field() subUrls = scrapy.Field() # 小類目錄存儲路徑 subFilename = scrapy.Field() # 小類下的子鏈接 sonUrls = scrapy.Field() # 文章標題和內容 head = scrapy.Field() content = scrapy.Field()
二、spiders爬蟲文件,使用RedisSpider類替換之前的Spider類,其余地方做些許改動即可,具體代碼如下:
# -*- coding: utf-8 -*- import scrapy import os from sinaNews.items import SinanewsItem from scrapy_redis.spiders import RedisSpider import sys reload(sys) sys.setdefaultencoding("utf-8") class SinaSpider(RedisSpider): name = "sina"
# 啟動爬蟲的命令 redis_key = "sinaspider:strat_urls" # 動態定義爬蟲爬取域范圍 def __init__(self, *args, **kwargs): domain = kwargs.pop('domain', '') self.allowed_domains = filter(None, domain.split(',')) super(SinaSpider, self).__init__(*args, **kwargs) def parse(self, response): items= [] # 所有大類的url 和 標題 parentUrls = response.xpath('//div[@id="tab01"]/div/h3/a/@href').extract() parentTitle = response.xpath('//div[@id="tab01"]/div/h3/a/text()').extract() # 所有小類的ur 和 標題 subUrls = response.xpath('//div[@id="tab01"]/div/ul/li/a/@href').extract() subTitle = response.xpath('//div[@id="tab01"]/div/ul/li/a/text()').extract() #爬取所有大類 for i in range(0, len(parentTitle)): # 爬取所有小類 for j in range(0, len(subUrls)): item = SinanewsItem() # 保存大類的title和urls item['parentTitle'] = parentTitle[i] item['parentUrls'] = parentUrls[i] # 檢查小類的url是否以同類別大類url開頭,如果是返回True (sports.sina.com.cn 和 sports.sina.com.cn/nba) if_belong = subUrls[j].startswith(item['parentUrls']) # 如果屬於本大類,將存儲目錄放在本大類目錄下 if(if_belong): # 存儲 小類url、title和filename字段數據 item['subUrls'] = subUrls[j] item['subTitle'] =subTitle[j] items.append(item) #發送每個小類url的Request請求,得到Response連同包含meta數據 一同交給回調函數 second_parse 方法處理 for item in items: yield scrapy.Request( url = item['subUrls'], meta={'meta_1': item}, callback=self.second_parse) #對於返回的小類的url,再進行遞歸請求 def second_parse(self, response): # 提取每次Response的meta數據 meta_1= response.meta['meta_1'] # 取出小類里所有子鏈接 sonUrls = response.xpath('//a/@href').extract() items= [] for i in range(0, len(sonUrls)): # 檢查每個鏈接是否以大類url開頭、以.shtml結尾,如果是返回True if_belong = sonUrls[i].endswith('.shtml') and sonUrls[i].startswith(meta_1['parentUrls']) # 如果屬於本大類,獲取字段值放在同一個item下便於傳輸 if(if_belong): item = SinanewsItem() item['parentTitle'] =meta_1['parentTitle'] item['parentUrls'] =meta_1['parentUrls'] item['subUrls'] = meta_1['subUrls'] item['subTitle'] = meta_1['subTitle'] item['sonUrls'] = sonUrls[i] items.append(item) #發送每個小類下子鏈接url的Request請求,得到Response后連同包含meta數據 一同交給回調函數 detail_parse 方法處理 for item in items: yield scrapy.Request(url=item['sonUrls'], meta={'meta_2':item}, callback = self.detail_parse) # 數據解析方法,獲取文章標題和內容 def detail_parse(self, response): item = response.meta['meta_2'] content = "" head = response.xpath('//h1[@id="main_title"]/text()') content_list = response.xpath('//div[@id="artibody"]/p/text()').extract() # 將p標簽里的文本內容合並到一起 for content_one in content_list: content += content_one item['head']= head[0] if len(head) > 0 else "NULL" item['content']= content yield item
三、settings文件設置
SPIDER_MODULES = ['sinaNews.spiders'] NEWSPIDER_MODULE = 'sinaNews.spiders' # 使用scrapy-redis里的去重組件,不使用scrapy默認的去重方式 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis里的調度器組件,不使用默認的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 允許暫停,redis請求記錄不丟失 SCHEDULER_PERSIST = True # 默認的scrapy-redis請求隊列形式(按優先級) SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 隊列形式,請求先進先出 #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" # 棧形式,請求先進后出 #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" # 只是將數據放到redis數據庫,不需要寫pipelines文件 ITEM_PIPELINES = { # 'Sina.pipelines.SinaPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400, } # LOG_LEVEL = 'DEBUG' # Introduce an artifical delay to make use of parallelism. to speed up the # crawl. DOWNLOAD_DELAY = 1 # 指定數據庫的主機IP REDIS_HOST = "192.168.13.26" # 指定數據庫的端口號 REDIS_PORT = 6379
執行命令:
本次直接使用本地的redis數據庫,將settings文件中的REDIS_HOST和REDIS_PORT注釋掉。
啟動爬蟲程序
scrapy runspider sina.py
執行程序后終端窗口顯示如下:
表示程序處於等待狀態,此時在redis數據庫端執行如下命令:
redis-cli> lpush sinaspider:start_urls http://news.sina.com.cn/guide/
http://news.sina.com.cn/guide/為起始url,此時程序開始執行。