爬虫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# 点击最后一个
不去重:
dont_filter=True(request的参数)
yield Request('https://www.cnblogs.com'+next_url,dont_filter=True)# 这样就不去重!!!
3 下载中间件(middlewares.py)
作用在什么时候?
下载东西过来的时候触发
-使用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 布隆过滤器
查找效率:
哈希冲突--二叉树(因为深度没有这么深)--红黑树
缓存穿透:
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,