爬虫知識点(scrapy_redis分散爬虫システム)

30007 ワード

Scrapyとscrapy-redisの違い
Scrapyは一般的な爬虫類フレームワークであるが、分散はサポートされていない.Scrapy-redisは、Scrapy分散爬虫類をより容易に実現するために、redisベースのコンポーネント(コンポーネントのみ)をいくつか提供している.
pip install scrapy-redis
Scrapy-redisでは、次の4つのコンポーネント(components)が用意されています.(4つのコンポーネントは、この4つのモジュールが適切に変更されることを意味します)
  • Scheduler
  • Duplication Filter
  • Item Pipeline
  • Base Spider

  • scrapy-redisアーキテクチャ
    上記の図に示すように、scrapy-redisはscrapyのアーキテクチャにredisを追加し、redisの特性に基づいて次のコンポーネントを拡張します.Scheduler :
    Scrapyはpythonの本来のcollectionを改造した.Deque(双方向キュー)は独自のScrapy queue(https://github.com/scrapy/queuelib/blob/master/queuelib/queue.pyただし、Scrapyは複数のspiderで共有できません.つまり、Scrapy自体が爬虫分布式をサポートしていません.scrapy-redisの解決は、このScrapy queueをredisデータベース(redisキュー)に変換し、同じredis-serverから爬虫するrequestを格納することで、複数のspiderを同じデータベースに読み込ませることができます.
    Scrapyにおいて「待ち行列」に直接関係するものは、スケジューラSchedulerであり、新たなrequestに対するエンキュー操作(Scrapy queueを加える)、次の登るrequest(Scrapy queueから取り出す)などの操作を担当する.待ち行列を優先順位に従って辞書構造を構築します.たとえば、次のようにします.
        {
               0 :   0
               1 :   1
               2 :   2
        }
    

    次にrequestの優先度に基づいて、どのキューに入れるかを決定し、列を出るときは優先度の小さい優先列で優先します.この比較的高度なキュー辞書を管理するために、Schedulerは一連の方法を提供する必要がある.しかし従来のSchedulerでは使用できなくなっているので、Scrapy-redisのschedulerコンポーネントを使用します.Duplication Filter
    Scrapyではこのrequestデリファクタ機能を集合で実現し,Scrapyでは既に送信されたrequest指紋を1つの集合に入れ,次のrequestの指紋を集合に持って行って照合し,その指紋が集合に存在する場合は,このrequestが送信されたことを説明し,なければ操作を継続する.このコアの重量判定機能はこのように実現されています.
        def request_seen(self, request):
            # self.request_figerprints          
            fp = self.request_fingerprint(request)
    
            #             
            if fp in self.fingerprints:
                return True
            self.fingerprints.add(fp)
            if self.file:
                self.file.write(fp + os.linesep)
    

    scrapy−redisでの重量除去はDuplication Filterコンポーネントによって実現され,redisのsetの重複しない特性によってDuplication Filter重量除去が巧みに実現された.scrapy-redisスケジューラはエンジンからrequestを受け取り、requestの指紋をredisのsetに保存して重複するかどうかをチェックし、重複しないrequest pushをredisのrequest queueに書きます.
    エンジン要求request(Spider発行)の場合、スケジューラはredisのrequest queueキュー⾥から優先度popに従って1つのrequestをエンジンに返し、エンジンはこのrequestをspider処理に送信する.Item Pipeline :
    エンジンは(Spiderが返した)登ったItemをItem Pipelineに、scrapy-redisのItem Pipelineは登ったItemをredisのitems queueに格納します.Item Pipelineを修正すると、keyに基づいてitems queueからitemを抽出し、items processesクラスタを実装するのに便利である.Base Spider
    scrapyの既存のSpiderクラスは使用されず、書き換えられたRedisSpiderはSpiderとRedisMixinの2つのクラスを継承し、RedisMixinはredisからurlを読み出すためのクラスである.
    Spider継承RedisSpiderを生成するとsetup_が呼び出されますredis関数です.この関数はredisデータベースに接続され、signals(信号)が設定されます.
  • spiderが空いているときのsignalはspiderを呼び出します.idle関数、この関数はschedule_next_request関数を呼び出し、spiderが常に生きている状態であることを保証し、DontCloseSpider異常を放出する.
  • itemを捕まえたときのsignalでitem_が呼び出されますscraped関数です.この関数はschedule_next_request関数を呼び出し、次のrequestを取得します.

  • Redisが実現する3つの機能:1:urlの指紋集合を要求する;
    2:responseのリクエストキュー
    3:一時的なitemデータ
    ゼロからRedis-scrapy分布式爬虫類を構築する
    Scrapy-Redis分散ポリシー:
    Windows 10、Mac OS X、Ubuntu 16.04、CentOS 7.2の4つのコンピュータがあるとします.いずれのコンピュータもMaster側またはSlaver側として使用できます.たとえば、次のようになります.
  • Master (コアサーバ):Windows 10を使用して、Redisデータベースを構築し、登る責任を負いません.url指紋の重み、Requestの割り当て、およびデータの格納
  • だけを担当します.
  • Slaver (爬虫プログラム実行端):Mac OS X、Ubuntu 16.04、CentOS 7.2を使用して爬虫プログラムの実行を担当し、実行中に新しいRequestをMaster
  • に提出する
  • まずSlaver側がMaster側からタスク(Request,url)を持ってデータをキャプチャし、Slaverがデータをキャプチャすると同時に、新しいタスクを生成したRequestがMaster処理にコミットされる.
  • Master側には1つのRedisデータベースしかありません.未処理のRequestのデマンドとタスクの割り当てを担当し、処理後のRequestを待ち行列に追加し、取得したデータを格納します.

  • Scrapy-Redisのデフォルトでは、タスクスケジューリングなどの作業がすでに完了しているため、RedisSpiderを継承し、redis_を指定するだけで、このポリシーが使用されています.keyでいいです.
    欠点は、Scrapy-RedisスケジューリングのタスクはRequestオブジェクトであり、情報量が比較的大きい(urlだけでなくcallback関数、headersなどの情報も含まれている)ため、結果として爬虫速度が低下し、Redisの大量の記憶領域が占有される可能性があるため、効率を保証するには一定のハードウェアレベルが必要になる.
    一、Redisのインストール
    Redisのインストール:http://redis.io/download
    インストールが完了すると、Redisインストールディレクトリの下にあるredisをコピーします.confは任意のディレクトリに、/etc/redis/redis.confに保存することをお勧めします(Windowsシステムは変更する必要はありません)
    二、構成ファイルredisを修正する.conf
    君のredisを開けろconfプロファイル、例:
  • 非Windowsシステム:sudo vi /etc/redis/redis.conf
  • Windowsシステム:C:\Intel\Redis\conf\redis.conf
  • Master端子redis.confにはbind 127.0.0.1と注記されており、Slave側はMaster側のRedisデータベースにリモート接続できます.
  • daemonize ynoは、Redisがデフォルトでデーモンとして実行されていないことを示す.すなわち、redis-server /etc/redis/redis.confが実行されると、Redis起動プロンプト画面が表示される.
  • daemonize yesはデフォルトでバックグラウンドで実行され、新しいターミナルウィンドウを再起動して他のコマンドを実行する必要はありません.個人の好みと実際のニーズを見てください.



  • 三、Slave端のリモート接続Master端をテストする
    テスト中、Master側Windows 10のIPアドレスは:192.168.199.108
  • Master側は、指定されたプロファイルに従ってredis-serverを起動します.例:
  • 非Windowsシステム:sudo redis-server /etc/redis/redis/conf
  • Windowsシステム: ( )モードでredis-server C:\Intel\Redis\conf\redis.confを実行してデフォルト構成を読み込むとよい.

  • Master側ローカルredis-cli:
  • を起動
  • slave側はredis-cli -h 192.168.199.108を起動する、-hパラメータは指定ホストに接続されたredisデータベース
  • を表す.
    注意:Slave端子はredis-serverを起動する必要はなく、Master端子は起動すればよい.Slave側がMaster側のRedisデータベースに読み込まれていれば,接続が成功し,分散が可能であることを示す.
    四、Redisデータベースデスクトップ管理ツール
    ここではRedis Desktop Managerをお勧めします.Windows、Mac OS X、Linuxなどのプラットフォームをサポートします.
    ダウンロード先:https://redisdesktop.com/download
    二、myspider_redis (class MySpider(RedisSpider))
    この爬虫類はRedisSpiderを継承し,分布式のキャプチャをサポートし,basic spiderを採用し,parse関数を書く必要がある.
    次にstart_がなくなりましたurlsが代わりにredis_key,scrapy-redisはkeyをRedisからpopし,要求されたurlアドレスとする.
    from scrapy_redis.spiders import RedisSpider
    
    
    class MySpider(RedisSpider):
        """Spider that reads urls from redis queue (myspider:start_urls)."""
        name = 'myspider_redis'
    
        #   redis-key   :
        redis_key = 'myspider:start_urls'
    
        #   :   allowd_domains(),__init__        ,        super()        
        def __init__(self, *args, **kwargs):
            # Dynamically define the allowed domains list.             #   
            domain = kwargs.pop('domain', '')
            self.allowed_domains = filter(None, domain.split(','))
    
            #             
            super(MySpider, self).__init__(*args, **kwargs)
    
        def parse(self, response):
            return {
                'name': response.css('title::text').extract_first(),
                'url': response.url,
            }
    

    注意:
    RedisSpiderクラスは、allowd_domainsstart_urlsを書く必要はありません.
  • scrapy-redisは、構築方法__init__()から爬虫類の爬虫領域範囲を動的に定義するか、allowd_domainsを直接書くかを選択することができる.
  • redis_を指定する必要がありますkey、すなわち爬虫類のコマンドを起動し、参照フォーマット:redis_key = 'myspider:start_urls'
  • 指定されたフォーマットに従って、start_urlsはMaster側のredis-cliでlpushをRedisデータベースに、RedisSpiderはデータベースでstart_を取得するurls.

  • 実行方法:
  • runspiderメソッドにより爬虫類のpyファイル(複数回に分けて実行することも可能)を実行し、爬虫類(達)は待機準備状態にある:scrapy runspider myspider_redis.py
  • Master側のredis-cliにpush命令を入力、参照フォーマット:$redis > lpush myspider:start_urls http://www.dmoz.org/
  • Slaver端爬虫類は要求を取得し、爬虫類を開始する.

  • setting
    # -*- coding: utf-8 -*-
    
    # Scrapy settings for youyuan project
    #
    # For simplicity, this file contains only settings considered important or
    # commonly used. You can find more settings consulting the documentation:
    #
    #     http://doc.scrapy.org/en/latest/topics/settings.html
    #     http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
    #     http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
    
    BOT_NAME = 'youyuan'
    
    SPIDER_MODULES = ['youyuan.spiders']
    NEWSPIDER_MODULE = 'youyuan.spiders'
    
    
    #    scrapy-redis      ,   scrapy     
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    #    scrapy-redis       ,   scrapy      
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    #       
    SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
    #     ,redis       
    SCHEDULER_PERSIST = True
    
    
    
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    #USER_AGENT = 'youyuan (+http://www.yourdomain.com)'
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = True
    
    # Configure maximum concurrent requests performed by Scrapy (default: 16)
    #CONCURRENT_REQUESTS = 32
    
    # Configure a delay for requests for the same website (default: 0)
    # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
    # See also autothrottle settings and docs
    #DOWNLOAD_DELAY = 3
    # The download delay setting will honor only one of:
    #CONCURRENT_REQUESTS_PER_DOMAIN = 16
    #CONCURRENT_REQUESTS_PER_IP = 16
    
    # Disable cookies (enabled by default)
    #COOKIES_ENABLED = False
    
    # Disable Telnet Console (enabled by default)
    #TELNETCONSOLE_ENABLED = False
    
    # Override the default request headers:
    #DEFAULT_REQUEST_HEADERS = {
    #   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    #   'Accept-Language': 'en',
    #}
    
    # Enable or disable spider middlewares
    # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
    #SPIDER_MIDDLEWARES = {
    #    'youyuan.middlewares.MyCustomSpiderMiddleware': 543,
    #}
    
    # Enable or disable downloader middlewares
    # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
    #DOWNLOADER_MIDDLEWARES = {
    #    'youyuan.middlewares.MyCustomDownloaderMiddleware': 543,
    #}
    
    # Enable or disable extensions
    # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
    #EXTENSIONS = {
    #    'scrapy.extensions.telnet.TelnetConsole': None,
    #}
    
    # Configure item pipelines
    # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
        'youyuan.pipelines.YouyuanPipeline': 300,
        'scrapy_redis.pipelines.RedisPipeline' : 400,
    }
    
    # Enable and configure the AutoThrottle extension (disabled by default)
    # See http://doc.scrapy.org/en/latest/topics/autothrottle.html
    #AUTOTHROTTLE_ENABLED = True
    # The initial download delay
    #AUTOTHROTTLE_START_DELAY = 5
    # The maximum download delay to be set in case of high latencies
    #AUTOTHROTTLE_MAX_DELAY = 60
    # The average number of requests Scrapy should be sending in parallel to
    # each remote server
    #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
    # Enable showing throttling stats for every response received:
    #AUTOTHROTTLE_DEBUG = False
    
    # Enable and configure HTTP caching (disabled by default)
    # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
    #HTTPCACHE_ENABLED = True
    #HTTPCACHE_EXPIRATION_SECS = 0
    #HTTPCACHE_DIR = 'httpcache'
    #HTTPCACHE_IGNORE_HTTP_CODES = []
    #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
    
    
    
    
     redi   item   item
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import redis
    import MySQLdb
    import json
    
    def process_item():
        #   redis     
        rediscli = redis.Redis(host = "127.0.0.1", port = 6379, db = 0)
    
        #   mysql     
        mysqlcli = MySQLdb.connect(host = "127.0.0.1", port = 3306, \
            user = "power", passwd = "60055969", db = "youyuan")
    
        offset = 0
    
        while True:
            #     redis pop  
            source, data = rediscli.blpop("yy:items")
            item = json.loads(data)
            try:
                #   mysql       ,    mysql  
                cursor = mysqlcli.cursor()
    
                cursor.execute("insert into beijing_18_25_mm (username, age, header_url, images_url, content, place_from, education, hobby, source_url, source, time, spidername) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", [item['username'], item['age'], item['header_url'], item['images_url'], item['content'], item['place_from'], item['education'], item['hobby'], item['source_url'], item['sourec'], item['time'], item['spidername']])
                #     
                mysqlcli.commit()
                #     
                cursor.close()
                offset += 1
                print offset
            except:
                pass
    
    if __name__ == "__main__":
        process_item()
    
    redisデータをmongodbに転送
    import redis
    import pymongo
    import json
    
    def process_item():
        #   redis     
        rediscli = redis.Redis(host = "127.0.0.1", port = 6379, db = "0")
    
        #   MongoDB     
        mongocli = pymongo.MongoClient(host = "127.0.0.1", port = 27017)
    
        #   mongodb     
        dbname = mongocli["youyuan"]
        #   mongodb   youyuan    
        sheetname = dbname["beijing_18_25_mm"]
        offset = 0
    
        while True:
            # redis          
            source, data = rediscli.blpop("yy:items")
            offset += 1
            #  json     Python  
            data = json.loads(data)
            #       sheetname  
            sheetname.insert(data)
            print offset
    
    if __name__ == "__main__":
        process_item()