爬虫5

1 数据存储到mongodb(pipeline持久化)

深度优先和广度优先

​ scrapy中通过队列和栈和优先级队列实现该方案

把地址存入数据库:如下4个地方需要操作

​ settings配置,之后才能调度到pipeline中去执行

cnblogs.py
    def parse(self, response):
        item_list  = response.css('#post_list .post_item')
        for item in item_list:
            article_url = item.css('.post_item_body a::attr(href)').extract_first()
            article_name = item.css('.titlelnk::text').extract_first()# 取a标签里面的内容用a::text
            commit_count = item.css('.article_comment a::text').extract_first()
            auther_name = item.css('.post_item_foot a::text').extract_first()

            article_item = ArticleItem()
            article_item['article_url']=article_url# 这里只能写[]取值,因为他(ArticleItem)没有写getattr和settattr方法!!
            article_item['article_name']=article_name
            article_item['commit_count']=commit_count
            article_item['auther_name']=auther_name
            yield article_item
            # yield Request(url, callback=self.parse_detail)# 注意这个callback不写,默认就回调到parse这个地方,我们可以指定回调的地方!!

        next_url=response.css('.pager a:last-child::attr(href)').extract_first()
        print('https://www.cnblogs.com'+next_url)
        yield Request('https://www.cnblogs.com'+next_url)
items.py
class ArticleItem(scrapy.Item):
    article_name = scrapy.Field()
    article_url = scrapy.Field()
    auther_name = scrapy.Field()
    commit_count = scrapy.Field()
pipelines.py
from pymongo import MongoClient

class ArticleMongodbPipeline(object):
    def process_item(self, item, spider):
        # 1、链接
        client = MongoClient('localhost', 27017)
        # 2、use 数据库
        db = client['db2']  # 等同于:client.db1
        # 3、查看库下所有的集合
        # print(db.collection_names(include_system_collections=False))
        # 4、创建集合
        table_user = db['userinfo']  # 等同于:db.user
        table_user.save(dict(item))
        # return item

settings

在setting中配置pipeline
ITEM_PIPELINES = {
'myscrapy.pipelines.ArticleMongodbPipeline': 300,   #数字越小越先执行
'myscrapy.pipelines.ArticleFilePipeline': 100,
}# 这里是对应pipelines.py的类名写的!!!!

2 去重规则(系统自带去重)

针对yeild的url链接可能是重复的

先采用MD5加密,减少内存占用,并且对可能对如下出现MD5值不一样的情况,系统也做了处理,使得一致。

-把url放到集合中
    -存在缺陷:
    1 可能很长,占内存非常大————md5值()
    2 	www.baidu.com?name=lqz&age=18
    	www.baidu.com?age=18&name=lqz
-BloomFilter去重(了解)
# 备注
源码入口:
from scrapy.dupefilters import RFPDupeFilter# 点击最后一个

image-20191129190340945

不去重:

​ dont_filter=True(request的参数)

yield Request('https://www.cnblogs.com'+next_url,dont_filter=True)# 这样就不去重!!!

3 下载中间件(middlewares.py)

image-20191129200632036

作用在什么时候?

​ 下载东西过来的时候触发

-使用cookie
-使用代理池
-集成selenium
-使用:
	-写一个类:MyscrapyDownloaderMiddleware
		-process_request
			加代理,加cookie,集成selenium。。。。
			return None,Response,Resquest
		-process_response
	-在setting中配置		-
		 DOWNLOADER_MIDDLEWARES = {'myscrapy.middlewares.MyscrapyDownloaderMiddleware': 543}
#自己写
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
        # 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 会放到调度器重新调度.这个地方写的不太好,他又走中间件,又进入循环了。因此代理完全可以卸载cnblogs中!
        return request

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

4 爬虫中间件

-写一个类MyscrapySpiderMiddleware
	-写一堆方法
-在setting中配置
	SPIDER_MIDDLEWARES = {
		'myscrapy.middlewares.MyscrapySpiderMiddleware': 543,
	}

5 信号

​ 在某个位置要执行一个功能

有内置信号,只需要给他关联函数。

		-写一个类:class MyExtension(object):
			-在from_crawler绑定一些信号
			-当scrapy执行到信号执行点的时候,会自动触发这些函数
		-在setting中配置	
			EXTENSIONS = {
			   'extentions.MyExtension': 100,
			}

6 布隆过滤器

查找效率:

​ 哈希冲突--二叉树(因为深度没有这么深)--红黑树

缓存穿透:

image-20191129144834918

7 分布式爬虫scrapy-redis

目的:提高爬虫效率,将项目部署在多个机器上

重复问题--统一做过滤,统一调度(redis)

原理:(将爬虫程序分布在多个机器上面,起始Url交给另个需要下载模块的redis来处理,并且不会爬取重复的链接)
	-pip3 install scrapy-redis
	-在setting中配置:
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        ITEM_PIPELINES = {
            'scrapy_redis.pipelines.RedisPipeline': 300
        }
		(-调度器:使用scrapy-redis提供的调度器
		-去重规则:使用scrapy-redis提供的去重规则
		-可选:
			-pipelines配置成scrapy-redis提供的持久化类)
        
	-在爬虫类中:
    	-继承RedisSpider
		-把start_url去掉(在redis中统一调度)
		-新增:redis_key = 'cnblogs:start_urls'

     CMD中:lpush cnblogs(这个是关联的redis_key):start_urls https://www.cnblogs.com。l代表从列表的左侧插入

# 使用注意事项
            
1. 使用的时候,刚开始会停住,因为起始url在redis中统一调度,因此在cmd打开redis-cli
2. 通过cmd命令行专跳到pip安装的项目路径下,才能打开redis!!设置统一起始url,
补充知识点(切换到其他盘符的路径下):

image-20191201200326049

posted @ 2019-12-01 20:45  xg1321  阅读(116)  评论(0编辑  收藏  举报