基於 Scrapy-redis 兩種形式的分佈式爬蟲

基於 Scrapy-redis 兩種形式的分佈式爬蟲javascript

 

 

 

基於 Scrapy-redis 兩種形式的分佈式爬蟲

 

redis 分佈式部署

 

一、scrapy 框架是否能夠本身實現分佈式?

 
  • 答:不能夠,緣由有二:
    • 其一: 由於多臺機器上部署的 Scrapy 會各自擁有各自的調度器,這樣就使得多臺機器沒法分配 start_url 列表中的url, (多臺機器沒法共享同一個調度器
    • 其二: 多臺機器爬取到的數據沒法經過同一個管道對數據進行統一的數據持久化存儲。(多臺機器沒法共享同一個管道)
 

二、基於 Scrapy-redis 組件的分佈式爬蟲

 
  • scrapy-redis 組件中爲咱們封裝好了能夠被多臺機器共享的調度器和管道,咱們能夠直接使用並實現分佈式數據取。
  • 實現方式:
    • 2.一、基於該組件的 RedisSpider 類
    • 2.二、基於該組件的 RedisCrawlSpider 類
 

三、分佈式實現流程: 上述兩種不一樣方式的分佈式實現流程是統一的

 
  • 3.一、pip下載安裝 scrapy-redis 模塊:
In [ ]:
pip3 install scrapy-redis
 
  • 3.2 建立爬蟲 項目 和 爬蟲程序文件 命令:
In [ ]:
scrapy startproject redisChoutiPro       # 建立 爬蟲 工程
scrapy genspider -t crawl www.xxx.com    # 建立 爬蟲 程序文件
 
  • 3.2.1 項目配置文件 settings.py 基本配置:
In [ ]:
#一、項目名稱,默認的USER_AGENT由它來構成,也做爲日誌記錄的日誌名  默認
BOT_NAME = 'redisChoutiPro'

# 二、爬蟲應用路徑    默認
SPIDER_MODULES = ['redisChoutiPro.spiders']
NEWSPIDER_MODULE = 'redisChoutiPro.spiders'

#三、客戶端User-Agent請求頭  # 手動添加
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

#四、是否遵循爬蟲協議
ROBOTSTXT_OBEY = False    # 由 True 該爲 False

#五、是否支持cookie,cookiejar進行操做cookie,默認開啓   默認
#COOKIES_ENABLED = False

#六、Telnet用於查看當前爬蟲的信息,操做爬蟲等...使用telnet ip port ,而後經過命令操做   默認
#TELNETCONSOLE_ENABLED = False
#TELNETCONSOLE_HOST = '127.0.0.1'
#TELNETCONSOLE_PORT = [6023,]

#七、Scrapy發送HTTP請求默認使用的請求頭   默認
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}
 
  • 3.三、對爬蟲程序 文件中的相關屬性進行修改
In [ ]:
# 1. 導包:from scrapy_redis.spiders import RedisCrawlSpider
# 將爬蟲類的父類修改爲基於RedisSpider或者RedisCrawlSpider。
#       注意:若是原始爬蟲文件是基於Spider的,則應該將父類修改爲RedisSpider,
#              若是原始爬蟲文件是基於CrawlSpider的,則應該將其父類修改爲RedisCrawlSpider
# 2.將當前爬蟲文件的父類設置成RedisCrawlSpider
# 3.註釋或者刪除 start_urls 列表屬性 和 allowed_domains 列表屬性。
# 4.將起始url列表替換成 redis_key = 'xxx'(調度器隊列的名稱)屬性值爲scrpy-redis組件中調度器隊列的名稱
 
  • 3.四、redis 配置文件的配置
In [ ]:
    - 取消保護模式:protected-mode no  ,  yes 改成 no<br> 
    - 註釋 bind 綁定: #bind 127.0.0.1  # 表示 可讓其餘 IP 訪問 Redis 
 
  • 3.五、在配置文件中進行相關配置,開啓使用scrapy-redis組件中封裝好的管道
In [4]:
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}
 
  • 3.6 在配置文件中進行相關配置,開啓使用scrapy-redis組件中封裝好的調度器
In [ ]:
# 配置調度器(使用組件中封裝好的能夠被共享的調度器)
# 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis組件本身的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據
SCHEDULER_PERSIST = True  # 數據指紋
 
  • 3.7 在配置文件中進行爬蟲程序連接redis的配置:
In [ ]:
REDIS_HOST = 'redis服務的ip地址'
REDIS_PORT = 6379
REDIS_ENCODING = 'utf-8'
REDIS_PARAMS = {'password': '123456'}
 
  • 3.8 開啓redis服務器端:redis-server 配置文件: 命令:
In [ ]:
redis-server.exe redis.windows.conf
 
  • 3.9 開啓redis客戶端:redis-cli: 命令:
In [ ]:
# 客戶端: redis-cli.exe:
"""
        keys * : 顯示 db 中 全部的 數據
        flushall : 清空全部 key
        """
 
  • 3.10 cd 爬蟲程序目錄: 運行爬蟲文件:
In [ ]:
scrapy runspider 爬蟲程序文件名
 
  • 3.11 向調度器隊列中扔入一個起始url(在redis客戶端中操做):
In [ ]:
lpush  redis_key屬性值  起始頁面 url 
 

項目案例 1、

 

基於該組件的RedisCrawlSpider類爬蟲

 

需求: 爬取<抽屜新熱榜>中的和做者數據

 

爬蟲文件:

In [ ]:
# -*- coding: utf-8 -*-
# chouti.py 
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from redisChoutiPro.items import RedischoutiproItem


class ChoutiSpider(RedisCrawlSpider):            # 將繼承類 CrawlSpider 改成  RedisCrawlSpider
    name = 'chouti'                              # 爬蟲名
    # allowed_domains = ['www.xxx.com']          # 容許爬取的 域名, 分佈式註釋此屬性
    # start_urls = ['https://dig.chouti.com//']  # 向 redis 的列表中扔入的 URL
    redis_key = 'chouti'                         # 共享 調度器 隊列 的名稱
    # 規則解析器
    allow = LinkExtractor(allow=r'/all/hot/recent/\d+')
    """
     link_extractor: 分頁的 URL 的 正則匹配 提取規則
     callback: 回調函數
     follow: 是否讓 連接解析器  迭代提取連接
    """
    # 連接解析器
    rules = (
        Rule(link_extractor=allow, callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        div_list = response.xpath('//div[@class="item"]')
        for div in div_list:
            item = RedischoutiproItem()
            content = div.xpath('./div[4]/div[1]/a/text()').extract_first()            
            author = div.xpath('./div[4]/div[2]/a[4]/b/text()').extract_first()
            item['content'] = content
            item['author'] = author
            yield item  #  默認是 原生的 Scrapy 管道,
 

管道文件

In [ ]:
# -*- coding: utf-8 -*-
import scrapy
class IncrementDataItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    content = scrapy.Field()
    author = scrapy.Field()
 

爬蟲項目 配置文件 settings.py

In [ ]:
# -*- coding: utf-8 -*-
#==>第一部分:   基本配置<===

#一、項目名稱,默認的USER_AGENT由它來構成,也做爲日誌記錄的日誌名
BOT_NAME = 'redisChoutiPro'

# 二、爬蟲應用路徑
SPIDER_MODULES = ['redisChoutiPro.spiders']
NEWSPIDER_MODULE = 'redisChoutiPro.spiders'

#三、客戶端User-Agent請求頭
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

#四、是否遵循爬蟲協議
ROBOTSTXT_OBEY = False

#五、是否支持cookie,cookiejar進行操做cookie,默認開啓
#COOKIES_ENABLED = False

#六、Telnet用於查看當前爬蟲的信息,操做爬蟲等...使用telnet ip port ,而後經過命令操做
#TELNETCONSOLE_ENABLED = False
#TELNETCONSOLE_HOST = '127.0.0.1'
#TELNETCONSOLE_PORT = [6023,]

#七、Scrapy發送HTTP請求默認使用的請求頭
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

####第二部分    分佈式 配置
# 添加 開啓使用scrapy-redis組件中封裝好的  共享管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}
# 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"  # 指定過濾器
# 使用scrapy-redis組件本身的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據
SCHEDULER_PERSIST = True   #無關緊要, 數據指紋 ,對數據的惟一標識,涉及到增量式,
# 若 分佈式集羣 蕩機 了,當再次重啓繼續執行分佈式爬蟲代碼時,接着從蕩機以前的位置開始爬,
# 只會爬 沒有通過爬取的數據和最近更新的數據,
# 如何區分 數據是否 爬過:
    #  把 爬過的數據進行數據指紋 的設置

# 指定存儲數據的  redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
 

執行代碼:

In [ ]:
scrapy startproject redisChoutiPro       # 建立 爬蟲 工程
scrapy genspider -t crawl www.xxx.com    # 建立 爬蟲 程序文件
redis-server.exe redis.windows.conf      # 運行 redis 服務端 
redis-cli.exe                            # 運行 redis 客戶端
scrapy runspider chouti.py               # 運行 爬蟲程序
lpush  chouti  https://dig.chouti.com/   # 向 共享調度器隊列 中添加 起始  URL  
 

項目案例 2、

 

基於該組件的RedisSpider類爬蟲

 

需求: 爬取 <醫藥魔方>數據

 

爬蟲文件

In [ ]:
# -*- coding: utf-8 -*-
import scrapy
from scrapy_redis.spiders import RedisSpider
from redisSpider_Pro.items import RedisspiderProItem

class RedisspidertestSpider(RedisSpider):
    name = 'redisSpiderTest'
    # allowed_domains = ['www.xxx.com']
    # start_urls = ['http://www.xxx.com/']
    redis_key = 'data'    # 調度器隊列的名稱
    url = 'http://db.pharmcube.com/'
    pageNum = 1

    def parse(self, response):
        num = response.xpath('/html/body/div/table/tbody/tr[1]/td[2]/text()').extract_first()
        name = response.xpath('/html/body/div/table/tbody/tr[2]/td[2]/text()').extract_first()
        item = RedisspiderProItem()
        item['num'] = num
        item['name'] = name
        yield item

        if self.pageNum <= 10000:
            self.pageNum += 1
            new_url = self.url + str(self.pageNum)
            yield scrapy.Request(url=new_url, callback=self.parse)
 

項目配置文件: settings.py

In [ ]:
# -*- coding: utf-8 -*-
BOT_NAME = 'redisSpider_Pro'

SPIDER_MODULES = ['redisSpider_Pro.spiders']
NEWSPIDER_MODULE = 'redisSpider_Pro.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'redisSpider_Pro (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False


#管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}

# 使用scrapy-redis組件的去重隊列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis組件本身的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否容許暫停
SCHEDULER_PERSIST = True

#redis鏈接數據庫的參數
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
#編碼格式
# REDIS_ENCODING = ‘utf-8’
#用戶名密碼
# REDIS_PARAMS = {‘password’:’123456’}
相關文章
相關標籤/搜索