11_29

一。数据存储到mongodb

  爬取的数据如果需要存储到mongodb中,要通过item,定义一个存储类。再yield一个类。

  数据存储的时候需要进过pipelines,再到setting中配置。

from pymongo import MongoClient
class ArticleMongodbPipeline(object):
    def process_item(self, item, spider):

        # client = MongoClient('mongodb://localhost:27017/')
        #连接
        client = MongoClient('localhost', 27017)
        #创建article数据库,如果没有叫创建,如果有,叫使用
        db = client['article']
        # print(db)
        #如果有使用articleinfo,如果没有,创建这个表
        article_info=db['articleinfo']

        article_info.insert(dict(item))
        # article_info.save({'article_name':item['article_name'],'aritcle_url':item['article_url']})
        # 如果retrun None ,下一个就取不到item了,取到的就是None
数据库使用
# -*- coding: utf-8 -*-
import scrapy
from myscrapy.items import ArticleItem
from scrapy import Request
from scrapy.core.scheduler import Scheduler
from scrapy.dupefilters import RFPDupeFilter
class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'   #爬虫名,必须唯一
    allowed_domains = ['cnblogs.com']   #允许的域名,
    start_urls = ['https://www.cnblogs.com/']  #起始的url
    custom_settings={"BB":"xxxxx"}
    #深度优先,广度优先
    #爬虫去重:好多策略
    #爬虫起始入口 start_requests
    # def parse_detail(self,response):
    #     print(len(response.text))


    # def parse(self, response):
    #     div_list=response.css('.post_item')
    #     for div in div_list:
    #         url=div.css('.post_item_body a::attr(href)').extract_first()
    #         article_name=div.css('.titlelnk::text').extract_first()
    #         auther_name=div.css('.post_item_foot a::text').extract_first()
    #         commit_count=div.css('.post_item_foot span a::text').extract_first()
    #         article_item=ArticleItem()
    #         #把地址存入数据库,yield item对象
    #         article_item['article_name']=article_name
    #         article_item['article_url']=url
    #         article_item['auther_name']=auther_name
    #         article_item['commit_count']=commit_count
    #         yield article_item
    #
    #     next_url=response.css('.pager a:last-child::attr(href)').extract_first()
    #     print('https://www.cnblogs.com'+next_url)
    #     #dont_filter=True 表示不去重,默认去重
    #     yield Request('https://www.cnblogs.com'+next_url,dont_filter=True,cookies={'name':'xxx'})
    def parse(self, response):
        print(response)
        # print(response.url)
        # print(response.status)
        # print(response.body)
        # obj=Request('https://www.cnblogs.com' + next_url, dont_filter=True, cookies={'name': 'xxx'})
        # obj.meta["aa"]="aa"
        # yield obj
        # yield [Request,Request]
        yield Request('https://www.cnblogs.com')
spiders层

  如果用户需要存储到其他地方,只要再配置文件中继续配置,并将类写成如下规定,

class ArticleFilePipeline(object):
    # def __init__(self,host,port):
    def __init__(self):
        # self.mongo_conn=MongoClient(host,port)
        pass

    #如果这个类中有from_crawler,首先会执行from_crawler,aa=ArticleFilePipeline.from_crawler(crawler)
    #如果没有直接aa=ArticleFilePipeline()
    @classmethod
    def from_crawler(cls, crawler):
        print('')
        #mongodb的配置信息在setting中
        # host = '从配置文件中拿出来的'
        # port='从配置文件中拿出来的'
        #crawler.settings总的配置文件
        print('asdfasdfasfdasfd',crawler.settings['AA'])
        # return cls(host,port)
        return cls()

    def open_spider(self, spider):
        # print('----',spider.custom_settings['AA'])
        self.f = open('article.txt','a')


    def close_spider(self, spider):
        self.f.close()
        print('close spider')
自定义

  setting配置:

ITEM_PIPELINES = {
   'myscrapy.pipelines.ArticleMongodbPipeline': 300,   #数字越小越先执行
   'myscrapy.pipelines.ArticleFilePipeline': 100,
}

 

二。去重。

  scrapy中的去重方式RFPDupeFilter是将url进行排序,再获取md5,保证长度,最后每有一个请求就和这个集合进行对比。

  setting配置:

DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' #默认的去重规则帮我们去重,去重规则在内存中

  自定义去重

from scrapy.dupefilters import  BaseDupeFilter
#自己定制去重方案
class MyDupeFilter(BaseDupeFilter):
    """Request Fingerprint duplicates filter"""

    def __init__(self, path=None, debug=False):
       self.aa=set()



    def request_seen(self, request):
        if request.url in self.aa:
            return True

    def close(self, reason):
        if self.file:
            self.file.close()

    def log(self, request, spider):
        if self.debug:
            msg = "Filtered duplicate request: %(request)s (referer: %(referer)s)"
            args = {'request': request, 'referer': referer_str(request) }
            self.logger.debug(msg, args, extra={'spider': spider})
        elif self.logdupes:
            msg = ("Filtered duplicate request: %(request)s"
                   " - no more duplicates will be shown"
                   " (see DUPEFILTER_DEBUG to show all duplicates)")
            self.logger.debug(msg, {'request': request}, extra={'spider': spider})
            self.logdupes = False

        spider.crawler.stats.inc_value('dupefilter/filtered', spider=spider)
编写去重类

  再setting中配置。

  其他去重方法亦可以通过redis进行,或者布隆过滤器(比特位去重)

三。下载中间件

  中间件可以拦截各种请求,需要再setting中配置

class MyscrapyDownloaderMiddleware(object):
    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        return s

    def process_request(self, request, spider):

        print('来了')
        print(request.url)

        #headers可以改
        print(request.headers)
        #从cookie池中取出cookie赋值进去
        print(request.cookies)
        #使用代理
        # request.meta['download_timeout'] = 20
        # request.meta["proxy"] = 'http://192.168.1.1:7878'
        # print(request)
        # print(spider)
        # 返回值:None,Request对象,Response对象
        #None 继续走下一个下载中间件
        #Response 直接返回,进入spider中做解析
        #Request 回到调度器,重新被调度
        #可以使用selenium
        return HtmlResponse(url="www.baidu.com", status=200,  body=b'sdfasdfasfdasdfasf')

    def process_response(self, request, response, spider):
        print('走了')
        print(request)
        print(spider)
        print(type(response))
        return response

    def process_exception(self, request, exception, spider):
        print('代理%s,访问%s出现异常:%s' % (request.meta['proxy'], request.url, exception))
        import time
        time.sleep(5)
        #删除代理
        # delete_proxy(request.meta['proxy'].split("//")[-1])
        #重新获取代理,放进去
        # request.meta['proxy'] = 'http://' + get_proxy()
        #return request 会放到调度器重新调度
        return request

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)
View Code

  其中request是任务来时做的动作。respons是任务走是做的动作,

  可以再cookies中设置代理(meta)和cookies池(cookies)

  再spriders中返回的parse中的request中添加对象,就会在下载中间件中的request中获取。

  return None,Response,Resquest

四。爬虫中间件

  

#自己写爬虫中间件
class MyscrapySpiderMiddleware(object):

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        print('我进爬虫的解析')
        return None

    def process_spider_output(self, response, result, spider):
        print("我往外走")
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):

        pass

    def process_start_requests(self, start_requests, spider):
        print("process_start_requests")
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        print("spider_opened")
        spider.logger.info('Spider opened: %s' % spider.name)
View Code

五。信号

  信号与django中的信号差不多,大体步骤如下:

  1.写一个类:class MyExtension(object):

  2.在from_crawler绑定一些信号  

  3.当scrapy执行到信号执行点的时候,会自动触发这些函数

  4.在setting中配置

  EXTENSIONS = {
'extentions.MyExtension': 100,
}

  

from scrapy import signals

class MyExtension(object):

    @classmethod
    def from_crawler(cls, crawler):
        # val = crawler.settings.getint('MMMM')
        obj = cls()

        crawler.signals.connect(obj.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(obj.spider_closed, signal=signals.spider_closed)

        return obj

    def spider_opened(self, spider):
        print('=============>open')

    def spider_closed(self, spider):
        print('=============>close')
例子

六。哈希

  哈希就是一个字典。

  当数据进行存储的时候,就为其标记一个记号,放入链表中,当发送哈希冲突的时候,就将该点存储的数据变成一个地址,地址指向另一个列表。

  布隆过滤器。

  当访问不存在数据频繁的时候,redis就有可能被击穿,被击穿后就回直接访问数据库。

  所以,使用过滤器事先判断数据是否有,如果没有直接返回。

  redis自带布隆过滤器。

七。分布式爬虫。

  分布式爬虫的基本原理就是同一调度统一去重。分布到不同的机器上爬虫。

  安装:

pip3 install scrapy-redis

  1.设置setting中的调度器:

SCHEDULER = "scrapy_redis.scheduler.Scheduler"

  2.设置去重规则:

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

  3.配置指定的持久化类:

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}

  4.在爬虫类中,继承RedisSpider,把start_url去掉,新增:redis_key = 'cnblogs:start_urls'。

  cnblogs和输入到redis中有关。

  5.启动项目,redis中发送爬虫指令

-lpush cnblogs:start_urls https://www.cnblogs.com

  源码组成:

-connection.py redis的连接
-defaults.py 配置文件
-dupefilter.py 去重规则
-picklecompat.py 序列化相关
-pipelines.py 持久化
-queue.py 队列
-scheduler.py 调度器
-spiders.py 爬虫相关
-utils.py py2和py3兼容的

  

 

posted on 2019-12-02 14:47  一只萌萌哒的提莫  阅读(124)  评论(0编辑  收藏  举报