scrapy_redis 分布式实现当当网图书爬取

通过scrapy_redis 实现分布式爬取当当图书案例

# -*- coding: utf-8 -*-
import copy
#用scrapy_redis 不需要继承这个类了继承下面的
import scrapy
from scrapy_redis.spiders import RedisSpider


class DangdangSpider(RedisSpider):

    name = 'dangdang'
    allowed_domains = ['dangdang.com']
    """  
    在scrapy_redis中不需要设置start_url只需要从redis——key对应的队列中获取request对象
    在启动程序的时候 spider并没有进行爬取  只有在请求队列中 出现请求对象的时候才开始工作
    start_urls = ['http://book.dangdang.com/']
    具体指令   $redis-cli -h 127.0.0.1-p 6379  主机和端口
    输入ping 回复pong 链接成功
    lpush dandang start_url
    程序开始爬取
    """
    redis_key = "dangdang"


    def parse(self, response):
        """
        进入当当的首页 以其左面的导航栏为请求的出发点 对书进行三层分类,
        fir_sort代表一级分类,首先xpath获取其一级分类的列表(同时也获取了书对应的一级分类的名字),对其中每个对象进行遍历获取其对应的下一级》》
        m_sort 代表二级分类,进入一级分类的某个元素,二级分类同样也是一个列表,对其中的标签对象进行遍历,获取每个对象的下一级》》
        thi_sort 三级分类 在三级分类中除了获取三级分类的名字外,要获取三级分类对应的图书列表页的url地址 并发送请求
        """
        #获取一级分类的列表并遍历
        fir_sort_list = response.xpath("//div[@class='level_one 'and position()<15]")
        for fir_sort in fir_sort_list:
            item = {}
            item["fir_sort"] = fir_sort.xpath("./dl/dt//text()").extract()
            item["fir_sort"] = [i.strip() for i in item["fir_sort"] if len(item["fir_sort"])>0]
            item["fir_sort"] = [i for i in item["fir_sort"] if i]
            m_sort_list = fir_sort.xpath("./div//dl[@class ='inner_dl']")
            for m_sort in m_sort_list:
                item["m_sort"] = m_sort.xpath("./dt//text()").extract()
                item["m_sort"] =list(filter(lambda x:len(x)>0,list(map(lambda x:x.strip(),item["m_sort"]))))
                thir_sort_list = m_sort.xpath("./dd/a")
                for thir_sort in thir_sort_list:
                    item["thi_sort"] = thir_sort.xpath("./@title").extract_first()
                    book_list_url = thir_sort.xpath("./@href").extract_first()
                    #获取该分类下的图书列表发送请求,为了不使item中的数据呗覆盖所以使用        
                    #deepcopy
                    yield scrapy.Request(
                        book_list_url,
                        callback = self.get_book_list,
                        #每次获取booklist都是将上面循环的结果拷贝下来发给下个函数,防止有些字段的数据被覆盖
                        meta={"item":copy.deepcopy(item)}
                    )



    def get_book_list(self,response):
        """
        从列表页中获取书的 书名,书价,以及书详情页的url地址 发送请求 在详情页中获取信息
        :param response:
        :return:
        """

        item = response.meta["item"]
        book_list = response.xpath("//div[@id='search_nature_rg']/ul[@class='bigimg']/li")
        for book in book_list:
            item["bk_name"] = book.xpath("./a/@title").extract_first()
            #detail在列表展示页面中 有些的没有所以需要请求详情页
            item["bk_price"] = book.xpath("./p[@class='price']/span[@class='search_now_price']/text()").extract_first()
            book_detail_url  = book.xpath("//a[@class='pic']/@href").extract_first()
            yield scrapy.Request(
                book_detail_url,
                callback = self.deal_book_detail,
                meta={"item":copy.deepcopy(item)}
            )
        #实现图书列表页的翻页,后获取信息
        next_page_url = response.xpath("//a[@title='下一页']").extract_first()
        #判断是否有下一页,或者是否是最后一页再决定是否发送请求
        if next_page_url:

            next_page_url = "http://category.dangdang.com/" + next_page_url
            yield scrapy.Request(
                next_page_url,
                callback = self.get_book_list,
                #精神洁癖0.0传给下一页的是上个函数深拷贝传过来的,不含有这个函数新添加字段的item
                meta = {"item":response.meta["item"]}

            )



    def deal_book_detail(self,response):
        item = response.meta["item"]
        item["author"] = response.xpath("//div[@class ='messbox_info']/span[1]//text()")
        if item["author"]:
            item["author"] = "".join(item["author"].extract())
            if ":" in item["author"]:
                item["author"] = item["author"].split(":")[1]
        item["publisher"] = response.xpath("//div[@class ='messbox_info']/span[@dd_name='出版社']/a/text()").extract_first()
        item["pub_date"] = response.xpath("//div[@class ='messbox_info']/span[contains(text(),'出版时间')]/text()").extract_first()
        if item["pub_date"]:
            item["pub_date"] = item["pub_date"].split(":")[1].strip()
        item["bk_image"] = response.xpath("//img[@id = 'largePic']/@src").extract_first()
        yield item

  有关settings文件中的配置


#确保所有的爬虫通过Redis去重【生成指纹存储到dangdang:dupefilter键对应的set中】
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

#启用Redis调度存储请求队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#不清除Redis队列、这样可以暂停/恢复 爬取,为false时候关闭程序redis清空
SCHEDULER_PERSIST = True

#指定用于连接redis的URL(可选)
#如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
REDIS_URL = "redis://127.0.0.1:6379"

#此外ITEM_PIPELINES中可以实现将数据存储到redis中

ITEM_PIPELINES = {
   'dangdang.pipelines.JdPipeline': 300,
   #调用这个管道会把我们爬取的数据 存入到redis数据库中
   #我们可以定义其他的管道   将数据做我们想要的操作
   'scrapy_redis.pipelines.RedisPipeline': 400,
}

 

最后布置在不同电脑上 在通过想redis 添加redis_key 对应的值就能实现分布式爬取

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值