(scrapy_redis框架源码: https://github.com/rmax/scrapy-redis)

1 概念原理

    scrapy-redis是一个基于redis的分布式爬虫框架,用于在爬取大量请求数据的情况下,单个主机的处理能力不足问题.(可以解决单个机子的带宽限制,运行速度限制,以及分布式的节点出现问题,解决后可以再次启动,继续爬取)

    其大概原理是通过redis对爬取的请求进行存储和调度,对爬取产生的数据(items)存储(供后续处理使用,如保存到MySQL中),以实现数据对多爬取节点的共享.(scrapy框架的队列是保存在内存中的,无法被其他爬取节点使用)

    scrapy-redis对scrapy其中一些组件做了重建,主要为队列,调度,url去重三个组件,还添加保存数据到redis的管道文件.scrapy-redis的行流程:

    da4e102615b741102582b8fa8bb491c4.jpg-wh_

2 安装

    环境配置:

        python

        scrapy框架

        redis数据库

        redis --python操作redis模块 安装方法:pip install redis

     安装scrapy-redis:

        pip install scrapy-redis

3 实例运行

    本次实例是scrapy_redis源码包中example-project,首先确保redis数据库已正常运行

    a.修改settings.py文件

    #coding:utf8    
    # Scrapy settings for example project
    #
    # For simplicity, this file contains only the most important settings by
    # default. All the other settings are documented here:
    #
    #     http://doc.scrapy.org/topics/settings.html
    #
    SPIDER_MODULES = ['example.spiders']
    NEWSPIDER_MODULE = 'example.spiders'
    USER_AGENT = 'scrapy-redis (+
    
    #使用scrapy_redis框架url去重组件 
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    #使用scrapy_redis框架调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    
    #调度器是否可以暂停
    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"
    
    #使用scrapy_redis管道
    ITEM_PIPELINES = {
        'example.pipelines.ExamplePipeline': 300,
        'scrapy_redis.pipelines.RedisPipeline': 400,#使item数据保存redis上的管道文件
    }
    
    LOG_LEVEL = 'DEBUG'
    # Introduce an artifical delay to make use of parallelism. to speed up the
    # crawl.
    DOWNLOAD_DELAY = 1
    
    # redis服务器的 ip地址和端口号
    REDIS_HOST = '192.168.10.128'
    REDIS_PORT = 6379
    # 或者可以使用下面这种方式
    # 定义redis连接信息,如果定义redis_url 则redis_host 不生效
    # REDIS_URL = 'redis://:123@192.168.10.128/2'    # 最后2 是指定数据库索引
    
    CONCURRENT_ITEMS = 100
    CONCURRENT_REQUEST = 16
    
    #每个域名同时并发的数量
    CONCURRENT_REQUEST_PER_DOMAIN = 64

    b.修改items.py文件

      items.py加入:

    class HaoItem(Item):    
        title = Field()
        url = Field()
        crawled = Field()  #这是实例中已定义的第一个管道中需要用到
        spider = Field()    #这是实例中已定义的第一个管道中需要用到

    c.修改spider文件下的dmoz文件:

    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from example.items import HaoItem
    
    class DmozSpider(CrawlSpider):
        """Follow categories and extract links."""
        name = 'hao123'
        #allowed_domains = ['dmoz.org']
        start_urls = ['http://www.hao123.org/']
    
        rules = [
            Rule(LinkExtractor(
                restrict_css=()
            ), callback='parse_directory', follow=False),
        ]
    
        def parse_directory(self, response):
            hao = HaoItem()
            title = response.xpath('//title/text()').extract()[0]
            url = response.url
            hao['title'] = title
            hao['url'] = url
            yield hao

    d.建立main文件运行spider,然后查看数据库,数据库中的数据会不断发生变化

4 将scrapy单机修改成scrapy-redis分布式

    一般的话是先使用scrapy进行爬取测试,然后改成scrapy-redis分布式

    a. 修改setting.py文件

        redis的连接信息,调度器,队列,去重,管道 五部分

    b. 修改爬虫文件中的继承

        from scrapy_redis.spiders import RedisSpider,--spider #相对应对应的类

        from scrapy_redis.spiders import RedisCrawlSpider,--crawlspider #相对应对应的类

        设置redis_key="键" 例如:redis_key = 'liepin:start_url' #相当于start_url,项目

        启动时,需要向redis数据库中导入此键以及对应的值

    c. 启动所有slave(爬虫终端)

    d. 向redis中push数据

    e. 持久化,把提取到数据从redis中提取出来,存储到mysql等数据库中

        例如:    

#coding:utf8
import json
import redis
import MySQLdb
def main():
    # 指定redis数据库信息
    try:
        print '正在连接redis'
        rediscli = redis.StrictRedis(host='192.168.2.218', port=6379, db=5)
        print 'redis连接成功'
        # 指定mysql数据库
        print '正在连接mysql'
        mysqlcli = MySQLdb.connect('192.168.2.180', 'root', '123456', 'spider', charset='utf8')
        # mysqlcli = MySQLdb.connect('192.168.2.218', 'centos4', '123456', 'spider', charset='utf8')
        print 'mysql连接成功'
    except Exception,e:
        print '数据库连接失败'
        print str(e)
        exit()
    while True:
        source, data = rediscli.blpop(["lie_pin:items"])
        # print source # redis里的键
        # print data # 返回的数据
        item = json.loads(data)
        try:
            # 使用cursor()方法获取操作游标
            cur = mysqlcli.cursor()
            # 使用execute方法执行SQL INSERT语句
            sql = '''insert into job values(null,"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s")on duplicate key update ''' \
                  '''area=values(area),salary=values(salary),exp=values(exp),edu=values(edu),num=values(num),time=values(time),otherq=values(otherq),welfare=values(welfare)''' \
                  ''',info=values(info),local=values(local),co_url=values(co_url),co_type=values(co_type)''' % \
                  (item['name'], item['co_name'], item['area'], item['salary'],item['exp'],item['edu'], item['num'], item['time'],
                   item['otherq'],item['welfare'],item['info'], item['local'], item['co_url'], item['co_type'],'liepin')
            cur.execute(sql)
            # 提交sql事务
            mysqlcli.commit()
            #关闭本次操作
            cur.close()
            print "inserted %s" % item['name']
        except Exception,e:
            print '插入失败'
            print str(e)
if __name__ == '__main__':
    main()