7-爬虫-中间件和下载中间件(加代理,加请求头,加cookie)、scrapy集成selenium、源码去重规则(布隆过滤器)、分布式爬虫

0 持久化(pipelines.py)使用步骤

1 爬虫中间件和下载中间件
1.1 爬虫中间件(一般不用)
1.2 下载中间件(代理,加请求头,加cookie)
1.2.1 加请求头(加到请求对象中)
1.2.2 加cookie
1.2.3 加代理

2 scrapy集成selenium

3 源码去重规则(布隆过滤器)
3.1 布隆过滤器

4 分布式爬虫

持久化(pipelines.py)使用步骤

# 1 scrapy 框架,安装,创建项目,创建爬虫,运行爬虫
# 2 scrapy架构
# 3  解析数据1 response对象有css方法和xpath方法-css中写css选择器     response.css('')-xpath中写xpath选择   response.xpath('')2 重点1-xpath取文本内容'.//a[contains(@class,"link-title")]/text()'-xpath取属性'.//a[contains(@class,"link-title")]/@href'-css取文本'a.link-title::text'-css取属性'img.image-scale::attr(src)'3 重点2.extract_first()  取一个.extract()        取所有
# 4 继续爬取- 下一页的地址:Request(url=next, callback=self.parse)- 详情地址:Request(url=url, callback=self.detail_parser)-额外去写detail_parser内的解析# 5 数据传递-解析中有数据---》下个解析中还能拿到Request(url=url, callback=self.detail_parser,meta={'item':item})----》给了Response对象的meta属性# 6 配置文件-基础配置-高级配置--》提高爬虫效率# 7 持久化---》把数据保存到磁盘上:文件,mysql-管道-使用步骤-1 写个类:items.py,里面写字段class CnblogItem(scrapy.Item):name = scrapy.Field()author = scrapy.Field()url = scrapy.Field()img = scrapy.Field()desc_content = scrapy.Field()# 文本详情text = scrapy.Field()-2 配置文件配置(管道,配置多个,存在多个位置)ITEM_PIPELINES = {"scrapy_demo.pipelines.CnblogPipeline": 300,"scrapy_demo.pipelines.CnblogMysqlPipeline": 200,}-3 爬虫解析中:yield item-3 pipelines.py中写类:open_spider,close_spider,process_item

在这里插入图片描述

1 爬虫中间件和下载中间件

1.1 爬虫中间件(一般不用)

# 第一步:写个爬虫中间件类class ScrapyDemoSpiderMiddleware:@classmethoddef 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# 走架构图第1步,会触发这里def process_spider_input(self, response, spider):# Called for each response that goes through the spider# middleware and into the spider.# Should return None or raise an exception.return None# 架构图,第1,7步走这里def process_spider_output(self, response, result, spider):# Called with the results returned from the Spider, after# it has processed the response.# Must return an iterable of Request, or item objects.for i in result:yield idef process_spider_exception(self, response, exception, spider):# Called when a spider or process_spider_input() method# (from other spider middleware) raises an exception.# Should return either None or an iterable of Request or item objects.pass# 架构图第一步def process_start_requests(self, start_requests, spider):# Called with the start requests of the spider, and works# similarly to the process_spider_output() method, except# that it doesn’t have a response associated.# Must return only requests (not items).for r in start_requests:yield rdef spider_opened(self, spider):spider.logger.info("Spider opened: %s" % spider.name)# 2 配置文件配置SPIDER_MIDDLEWARES = {"scrapy_demo.middlewares.ScrapyDemoSpiderMiddleware": 543,
}

1.2 下载中间件(代理,加请求头,加cookie)

class ScrapyDemoDownloaderMiddleware:@classmethoddef 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 sdef process_request(self, request, spider):# - return None: 继续执行当次请求,继续走下一个中间件---》如果中间件没了---》执行下载# - return Response :直接把Response返回给引擎,引擎交给爬虫去解析# - return Request :把request返回给引擎,引擎给调度器,等待下一次被爬取# - 直接抛异常: 触发process_exception执行return Nonedef process_response(self, request, response, spider):# Must either;# - return Response:正常爬取完---》给引擎---》引擎给爬虫去解析# - return Request: 爬取失败--》给引擎--》引擎给调度器--》等待下次爬取# - 抛异常  :走到process_exceptionreturn responsedef process_exception(self, request, exception, spider):# Called when a download handler or a process_request()# (from other downloader middleware) raises an exception.# Must either:# - return None: continue processing this exception# - return a Response object: stops process_exception() chain# - return a Request object: stops process_exception() chainpassdef spider_opened(self, spider):spider.logger.info("Spider opened: %s" % spider.name)

# faker 模块 :随机生成假数据
# pip install fake_useragent:随机生成请求头###  加referer,加token 加 user-agent
def process_request(self, request, spider):#### 加请求头print(request.headers)request.headers['referer'] = 'http://www.lagou.com'request.headers['token'] = 'asdfasdf.asdfads.asfdasfd'# user-agent--->写死了---》想随机请求头from fake_useragent import UserAgentua = UserAgent()request.headers['User-Agent'] = str(ua.random)print(request.headers)return None

    def process_request(self, request, spider):print(request.cookies)request.cookies['name']='lqz'return None

1.2.3 加代理

# 在下载中间件的def process_request(self, request, spider):写代码# 第一步:-在下载中间件写process_request方法def get_proxy(self):import requestsres = requests.get('http://127.0.0.1:5010/get/').json()if res.get('https'):return 'https://' + res.get('proxy')else:return 'http://' + res.get('proxy')def process_request(self, request, spider):#request.meta['proxy'] = self.get_proxy()request.meta['proxy'] = 'http://192.168.11.11:8888'return None# 第二步:代理可能不能用,会触发process_exception,在里面写def process_exception(self, request, exception, spider):print('-----',request.url)  # 这个地址没有爬return request

2 scrapy集成selenium

# 使用scrapy默认下载器---》类似于requests模块发送请求,不能执行js,有的页面拿回来数据不完整# 想在scrapy中集成selenium,获取数据更完整,获取完后,自己组装成 Response对象,就会进爬虫解析,现在解析的是使用selenium拿回来的页面,数据更完整
# 集成selenium 因为有的页面,是执行完js后才渲染完,必须使用selenium去爬取数据才完整# 保证整个爬虫中,只有一个浏览器器
# 只要爬取 下一页这种地址,使用selenium,爬取详情,继续使用原来的# 第一步:在爬虫类中写
from selenium import webdriver
class CnblogsSpider(scrapy.Spider):bro = webdriver.Chrome() # 使用无头bro.implicitly_wait(10)def close(spider, reason):spider.bro.close() #浏览器关掉# 第二步:在中间件中def process_request(self, request, spider):# 爬取下一页这种地址---》用selenium,但是文章详情,就用原来的if 'sitehome/p' in request.url:spider.bro.get(request.url)from scrapy.http.response.html import HtmlResponseresponse = HtmlResponse(url=request.url, body=bytes(spider.bro.page_source, encoding='utf-8'))return responseelse:return None

3 源码去重规则(布隆过滤器)

# 如果爬取过的地址,就不会再爬了,scrapy 自带去重# 调度器可以去重,研究一下,如何去重的---》使用了集合# 要爬取的Request对象,在进入到scheduler调度器排队之前,先执行enqueue_request,它如果return False,这个Request就丢弃掉,不爬了----》如何判断这个Request要不要丢弃掉,执行了self.df.request_seen(request),它来决定的-----》RFPDupeFilter类中的方法----》request_seen---》会返回True或False----》如果这个request在集合中,说明爬过了,就return True,如果不在集合中,就加入到集合中,然后返回False# 调度器源码
from scrapy.core.scheduler import Scheduler# 这个方法如果return True表示这个request要爬取,如果return False表示这个网址就不爬了(已经爬过了)def enqueue_request(self, request: Request) -> bool:# request当次要爬取的地址对象if self.df.request_seen(request):# 有的请情况,在爬虫中解析出来的网址,不想爬了,就就可以指定# yield Request(url=url, callback=self.detail_parse, meta={'item': item},dont_filter=True)# 如果符合这个条件,表示这个网址已经爬过了 return Falsereturn True# self.df 去重类 是去重类的对象 RFPDupeFilter--》配置文件配置的-在配置文件中如果配置了:DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'表示,使用它作为去重类,按照它的规则做去重-RFPDupeFilter的request_seendef request_seen(self, request: Request) -> bool:# request_fingerprint 生成指纹fp = self.request_fingerprint(request) #request当次要爬取的地址对象#判断 fp 在不在集合中,如果在,return Trueif fp in self.fingerprints:return True#如果不在,加入到集合,return Falseself.fingerprints.add(fp)return False# 传进来是个request对象,生成的是指纹-爬取的网址:https://www.cnblogs.com/teach/p/17238610.html?name=lqz&age=19-和        https://www.cnblogs.com/teach/p/17238610.html?age=19&name=lqz-它俩是一样的,返回的数据都是一样的,就应该是一条url,就只会爬取一次-所以 request_fingerprint  就是来把它们做成一样的(核心原理是把查询条件排序,再拼接到后面)-生成指纹,指纹是什么? 生成的指纹放到集合中去重-www.cnblogs.com?name=lqz&age=19-www.cnblogs.com?age=19&name=lqz-上面的两种地址生成的指纹是一样的# 测试指纹from scrapy.utils.request import RequestFingerprinterfrom scrapy import Requestfingerprinter = RequestFingerprinter()request1 = Request(url='http://www.cnblogs.com?name=lqz&age=20')request2 = Request(url='http://www.cnblogs.com?age=20&name=lqz')res1 = fingerprinter.fingerprint(request1).hex()res2 = fingerprinter.fingerprint(request2).hex()print(res1)print(res2)# 集合去重,集合中放 
# a一个bytes
# 假设爬了1亿条url,放在内存中,占空间非常大
a6af0a0ffa18a9b2432550e1914361b6bffcff1a
a6af0a0ffa18a9b2432550e191361b6bffc34f1a# 想一种方式,极小内存实现去重---》布隆过滤器
https://zhuanlan.zhihu.com/p/94668361

3.1 布隆过滤器

# 极小内存实现去重:
# 应用场景:爬虫去重,避免缓存穿透,垃圾邮件过滤# bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫url去重)
#布隆案例# from pybloom_live import ScalableBloomFilter
# bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
# url = "www.cnblogs.com"
# url2 = "www.liuqingzheng.top"
# bloom.add(url)
# print(url in bloom)
# print(url2 in bloom)from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1000)
url='www.baidu.com'
bf.add(url)
print(url in bf)
print("www.liuqingzheng.top" in bf)
from scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import RequestFingerprinter
from pybloom_live import ScalableBloomFilterclass MyPDupeFilter(BaseDupeFilter):fingerprints = ScalableBloomFilter(initial_capacity=100, error_rate=0.001,mode=ScalableBloomFilter.LARGE_SET_GROWTH)fingerprinter = RequestFingerprinter()def request_seen(self, request):print('zoule')fp = self.request_fingerprint(request)if fp in self.fingerprints:return Trueself.fingerprints.add(fp)return Falsedef request_fingerprint(self, request) -> str:return self.fingerprinter.fingerprint(request).hex()

4 分布式爬虫

# 原来scrapy的Scheduler维护的是本机的任务队列(待爬取的地址)+本机的去重队列(放在集合中)---》在本机内存中
# 如果把scrapy项目,部署到多台机器上,多台机器爬取的内容是重复的# 所以实现分布式爬取的关键就是,找一台专门的主机上运行一个共享的队列比如Redis,
然后重写Scrapy的Scheduler,让新的Scheduler到共享队列存取Request,并且去除重复的Request请求,所以总结下来,实现分布式的关键就是三点:#1、多台机器共享队列#2、重写Scheduler,让其无论是去重还是任务都去访问共享队列#3、为Scheduler定制去重规则(利用redis的集合类型)# scrapy-redis实现分布式爬虫-公共的去重-公共的待爬取地址队列#  使用步骤0 下载:pip2 install scrapy-redis1 把之前爬虫类,继承class CnblogsSpider(RedisSpider):2 去掉起始爬取的地址,加入一个类属性redis_key = 'myspider:start_urls'  # redis列表的key,后期我们需要手动插入起始地址3 配置文件中配置DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"  # scrapy redis去重类,使用redis的集合去重# 不使用原生的调度器了,使用scrapy_redis提供的调度器,它就是使用了redis的列表SCHEDULER = "scrapy_redis.scheduler.Scheduler"REDIS_HOST = 'localhost'                            # 主机名REDIS_PORT = 6379                                   # 端口ITEM_PIPELINES = {# 'mysfirstscrapy.pipelines.MyCnblogsPipeline': 300,'mysfirstscrapy.pipelines.MyCnblogsMySqlPipeline': 301,'scrapy_redis.pipelines.RedisPipeline': 400,}4 再不同多台机器上运行scrapy的爬虫,就实现了分布式爬虫5 写入到redis的列表中起始爬取的地址:列表key:myspider:start_urlsrpush myspider:start_urls https://www.cnblogs.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/190877.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【游戏开发算法每日一记】使用随机prime算法生成错综复杂效果的迷宫(C#和C++)

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:Uni…

无人机航迹规划:五种最新智能优化算法(COA、SWO、KOA、GRO、LO)求解无人机路径规划MATLAB

一、五种算法(LSO、SWO、KOA、GRO、LO)简介 1、小龙虾优化算法COA 小龙虾优化算法(Crayfsh optimization algorithm,COA)由Jia Heming 等人于2023年提出,该算法模拟小龙虾的避暑、竞争和觅食行为&#xf…

windows安装composer并更换国内镜像

第一步、官网下载 下载地址 Composer安装https://getcomposer.org/Composer-Setup.exe第二步、双击安装即可 第三步选择 php安装路径并配置path 第四步、 composer -v查看安装是否成功,出现成功界面 第五步、查看镜像地址并更换(composer国内可能较慢…

Java通过JNI技术调用C++动态链接库的helloword测试

JNI调用原理 原理就不细说了&#xff0c;其实就是写个库给Java调&#xff0c;可以百度一下Java JNI&#xff0c;下面是HelloWorld代码测试 编写一个本地测试类 package com.my.study.cpp_jni;/*** 测试Java调用C库* <p>使用命令javac -h . NativeTest.java自动生成C头…

CTFhub-RCE-读取源代码

源代码&#xff1a; <?php error_reporting(E_ALL); if (isset($_GET[file])) { if ( substr($_GET["file"], 0, 6) "php://" ) { include($_GET["file"]); } else { echo "Hacker!!!"; } } else {…

塑料质量检测是确保产品制造和装配过程的关键环节

激光塑料透光率检测是一种有效的塑料材料特性检测方法。在激光束通过上层透明材料后&#xff0c;被下层材料吸收。上层材料可以是透明的或者是有颜色的&#xff0c;但是必须能够保证有足够的激光通过。 塑料质量检测是确保产品制造和装配过程的关键环节。通过激光塑料透光率检测…

手机地磁传感器与常见问题

在手机中&#xff0c;存在不少传感器&#xff0c;例如光距感&#xff0c;陀螺仪&#xff0c;重力加速度&#xff0c;地磁等。关于各传感器&#xff0c;虽功能作用大家都有所了解&#xff0c;但是在研发设计debug过程中&#xff0c;却总是会遇到很多头疼的问题。关于传感器&…

SPSS时间序列分析:序列图

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

从0到0.01入门React | 006.精选 React 面试题

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

ubuntu上安装edge浏览器

1下载edge浏览器 官网下载 edge浏览器的linux版本可在上面的官网中寻找。 我选择的是Linux(.deb)。 2 安装 可在终端的edge安装包所在的路径下输入下面命令安装。 sudo dpkg -i edge安装包的名称.deb3 安装可能存在的问题 1dpkg:依赖关系问题使得edge-stable的配置工作不…

PSP - 蛋白质复合物结构预测 Template Pair 特征 Mask 可视化

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134333419 在蛋白质复合物结构预测中&#xff0c;在 TemplatePairEmbedderMultimer 层中 &#xff0c;构建 Template Pair 特征的源码&#xff0c…

Elastic Observability 8.11:ES|QL、APM 中的通用分析和增强的 SLOs

作者&#xff1a;Tom Grabowski, Katrin Freihofner, Israel Ogbole Elastic Observability 8.11 引入了 ES|QL for Observability&#xff08;技术预览版&#xff09;、Universal ProfilingTM 和 Elastic APM 集成&#xff0c;以及针对 Elastic Observability 的新 SLO &#…

ZYNQ_project:ram_dual_port

伪双端口ram&#xff1a;写端口&#xff1a;clk_w,en_A,we_A,addr_A,din_A;读端口:clk_r,en_B,addr_B;dout_B. 设计读写模块&#xff0c;写入256个数据&#xff0c;再读出256个数据。 输入时钟100Mhz&#xff0c;输出时钟50Mhz。 多bit数据&#xff0c;高速时钟域到低速时钟…

二十六、W5100S/W5500+RP2040树莓派Pico<WOL示例>

文章目录 1 前言2 简介2 .1 什么是Wake on LAN&#xff1f;2.2 Wake on LAN的优点2.3 Wake on LAN数据交互原理2.4 Wake on LAN应用场景 3 WIZnet以太网芯片4 Wake on LAN示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关…

具名挂载和匿名挂载

匿名卷挂载 &#xff1a; -v 的时候只指定容器内的路径 如下面这个&#xff1a;/etc/nginx 1.docker run -d -P --name nginx -v /etc/nginx nginx 2.查看所有卷 docker volume ls 这里发现&#xff0c;这就是匿名挂载&#xff0c;只指定容器内的路径&#xff0c;没有指定…

平安人寿基于 Apache Doris 统一 OLAP 技术栈实践

导读&#xff1a;平安人寿作为保险行业领军企业&#xff0c;坚持技术创新&#xff0c;以数据业务双轮驱动的理念和更加开放的思路来应对不断增长的数据分析和应用需求&#xff1b;以深挖数据价值、保障业务用数效率为目标持续升级大数据产品体系。自 2022 年起平安人寿开始引入…

某城高速综合管控大数据大屏可视化【可视化项目案例-04】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…

notes_质谱蛋白组学数据分析基础知识

目录 1. 蛋白组学方法学1.1 液相-质谱法1) 基本原理2) bottom-up策略的基本流程 1.2 PEA/Olink 2. 质谱数据分析2.1 原始数据格式2.2 分析过程1&#xff09;鉴定搜索引擎&#xff08;质谱组学&#xff09;重难点/潜在的研究方向 2&#xff09;定量3&#xff09;预处理 2.3 下游…

MySQL 批量修改表的列名为小写

1、获取脚本 SELECT concat( alter table , TABLE_NAME, change column , COLUMN_NAME, , lower( COLUMN_NAME ), , COLUMN_TYPE, comment \, COLUMN_COMMENT, \; ) AS 脚本 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA 数据库名 and TABLE_NAME表名-- 大写是up…

22.斐波那契数列数列前20项.

#include<stdio.h>int main(){int i,sum1; int a[100];a[0]0;a[1]1;for(i2;i<20;i){a[i]a[i-1]a[i-2]; sumsuma[i];}printf("斐波那契数列的前20项和为&#xff1a;%d",sum);return 0;}