Python爬虫之Scrapy框架系列(24)——分布式爬虫scrapy_redis完整实战【XXTop250完整爬取】

目录:

  • 每篇前言:
  • 1.使用分布式爬取豆瓣电影信息
    • (1)settings.py文件中的配置:
    • (2)spider文件的更改:
    • (3)items.py文件(两个项目一致!):
    • (4)pipelines.py文件:
    • 分布式实现效果:
      • ①直接运行项目,发现在等待:
      • ②再开一个终端,做如下操作:
    • 总结:
    • 效果:
  • 2.解决一些小问题:
    • 2.1 解决爬空问题:(在两个项目中都进行以下操作!)
      • ①使用拓展程序(这个文件就是为了解决爬空而生的):
      • ②在settings.py文件中设置这个拓展程序:
  • 3. 关于分布式(Scrapy_redis)的总结:

每篇前言:

  • 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者

  • 🔥🔥本文已收录于Scrapy框架从入门到实战专栏:《Scrapy框架从入门到实战》
  • 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
  • 📝​📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
  • 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
  • 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!

在这里插入图片描述

1.使用分布式爬取豆瓣电影信息

  • (此处做了限制,只爬取四页电影数据共计100条,可去除限制爬取全部10页250条数据!)

**项目源码:
链接:https://pan.baidu.com/s/13akXDxNbtBeRTUzUB_2SNQ
提取码:bcuy
**

目标:在本机上使用两个完全一模一样的豆瓣项目,去使用分布式下载豆瓣电影top250电影信息!
在这里插入图片描述
其实,我们要进行修改的就只有settings.py文件以及爬虫文件,别的文件都不需要进行改动。

(1)settings.py文件中的配置:

  • (两个项目都做此配置)
#设置scrapy-redis
#1.启用调度将请求存储进redis
from scrapy_redis.scheduler import Scheduler
SCHEDULER="scrapy_redis.scheduler.Scheduler"#2.确保所有spider通过redis共享相同的重复过滤
from scrapy_redis.dupefilter import RFPDupeFilter
DUPEFILTER_CLASS="scrapy_redis.dupefilter.RFPDupeFilter"#3.指定连接到Redis时要使用的主机和端口     目的是连接上redis数据库
REDIS_HOST="localhost"
REDIS_PORT=6379# 不清理redis队列,允许暂停/恢复抓取    (可选)    允许暂停,redis数据不丢失     可以实现断点续爬!!!
SCHEDULER_PERSIST = True# 第二步:开启将数据存储进redis公共区域的管道!
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 100,    # 开启数据交给redis公共区域的管道'douban.pipelines.DoubanPipeline': 200,         # 存储本地txt文件的管道
}

(2)spider文件的更改:

  • (两个项目略有不同!)

总共四步:

  1. 导入RedisSpider类:(既然要使用它,肯定首先要导入!)
    from scrapy_redis.spiders import RedisSpider

  2. 继承使用RedisSpider类:(既然要使用它,就要继承去使用这个类)
    class DbSpider(RedisSpider):

  3. 既然将请求都放进了Redis里,那爬虫文件中就不再需要start_urls这个初始请求了:
    #start_urls = ['https://movie.douban.com/top250']

  4. 设置一个键,寻找起始的url:(这个键就会在redis中寻找初始的url,所以后面我们只需往redis里放请求即可!)
    redis_key="db:start_urls"

完整版爬虫文件:
第一个项目下的爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
import refrom ..items import DoubanItemfrom scrapy_redis.spiders import RedisSpider        # 1.导出RedisSpider类class DbSpider(RedisSpider):                        # 2.使用RedisSpider类name = 'db'allowed_domains = ['movie.douban.com']# start_urls = ['https://movie.douban.com/top250']   # 3.将请求放进redis里redis_key = "db:start_urls"                          # 4.设置一个键,寻找起始的urlpage_num = 0  # 类变量def parse(self, response):  # 解析和提取数据print('第一个项目:', response.url)print('第一个项目:', response.url)print('第一个项目:', response.url)# 获取电影信息数据# films_name=response.xpath('//div[@class="info"]/div/a/span[1]/text()').extract()node_list = response.xpath('//div[@class="info"]')  # 25个if node_list:  # 此判断的作用:在爬取到10页之后,就获取不到了!判断每次是否获取到数据,如果没有则返回空(即停止了)for node in node_list:# 电影名字film_name = node.xpath('./div/a/span[1]/text()').extract()[0]# 主演   拿标签内容,再正则表达式匹配con_star_name = node.xpath('./div/p[1]/text()').extract()[0]if "主" in con_star_name:star_name = re.findall("主演?:? ?(.*)", con_star_name)[0]else:star_name = "空"# 评分score = node_list.xpath('./div/div/span[@property="v:average"]/text()').extract()[0]# 使用字段名  收集数据item = DoubanItem()item["film_name"] = film_nameitem["star_name"] = star_nameitem["score"] = score# 形式:{"film_name":"肖申克的救赎","star_name":"蒂姆","score":"9.7"}detail_url = node.xpath('./div/a/@href').extract()[0]yield scrapy.Request(detail_url,callback=self.get_detail,meta={"info":item})# 此处几行的代码配合yield里传的参数meta={"num":self.page_num},共同作用实现:# 两个项目的共享变量page_num能正确变化,不导致冲突!!!if response.meta.get("num"):self.page_num = response.meta["num"]self.page_num += 1if self.page_num == 4:returnprint("page_num:", self.page_num)page_url = "https://movie.douban.com/top250?start={}&filter=".format(self.page_num * 25)yield scrapy.Request(page_url, callback=self.parse, meta={"num": self.page_num})# 注意:各个模块的请求都会交给引擎,然后经过引擎的一系列操作;但是,切记:引擎最后要把得到的数据再来给到# spider爬虫文件让它解析并获取到真正想要的数据(callback=self.parse)这样就可以再给到自身。else:returndef get_detail(self, response):item = DoubanItem()# 获取电影简介信息# 1.meta会跟随response一块返回  2.可以通过response.meta接收   3.通过updata可以添加到新的item对象info = response.meta["info"]  # 接收电影的基本信息item.update(info)  # 把电影基本信息的字段加进去# 将电影简介信息加入相应的字段里description = response.xpath('//div[@id="link-report-intra"]//span[@property="v:summary"]/text()').extract()[0]\.strip()item['description'] = descriptionyield item

第二个项目下的爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
import refrom ..items import DoubanItemfrom scrapy_redis.spiders import RedisSpider        # 1.导出RedisSpider类class DbSpider(RedisSpider):                        # 2.使用RedisSpider类name = 'db'allowed_domains = ['movie.douban.com']# start_urls = ['https://movie.douban.com/top250']   # 3.将请求放进redis里redis_key = "db:start_urls"                          # 4.设置一个键,寻找起始的urlpage_num = 0  # 类变量def parse(self, response):  # 解析和提取数据print('第二个项目:', response.url)print('第二个项目:', response.url)print('第二个项目:', response.url)# 获取电影信息数据# films_name=response.xpath('//div[@class="info"]/div/a/span[1]/text()').extract()node_list = response.xpath('//div[@class="info"]')  # 25个if node_list:  # 此判断的作用:在爬取到10页之后,就获取不到了!判断每次是否获取到数据,如果没有则返回空(即停止了)for node in node_list:# 电影名字film_name = node.xpath('./div/a/span[1]/text()').extract()[0]# 主演   拿标签内容,再正则表达式匹配con_star_name = node.xpath('./div/p[1]/text()').extract()[0]if "主" in con_star_name:star_name = re.findall("主演?:? ?(.*)", con_star_name)[0]else:star_name = "空"# 评分score = node_list.xpath('./div/div/span[@property="v:average"]/text()').extract()[0]# 使用字段名  收集数据item = DoubanItem()item["film_name"] = film_nameitem["star_name"] = star_nameitem["score"] = score# 形式:{"film_name":"肖申克的救赎","star_name":"蒂姆","score":"9.7"}detail_url = node.xpath('./div/a/@href').extract()[0]yield scrapy.Request(detail_url,callback=self.get_detail,meta={"info":item})# 此处几行的代码配合57行yield里传的参数meta={"num":self.page_num},共同作用实现:# 两个项目的共享变量page_num能正确变化,不导致冲突!!!if response.meta.get("num"):self.page_num = response.meta["num"]self.page_num += 1if self.page_num == 4:returnprint("page_num:", self.page_num)page_url = "https://movie.douban.com/top250?start={}&filter=".format(self.page_num * 25)yield scrapy.Request(page_url, callback=self.parse, meta={"num": self.page_num})# 注意:各个模块的请求都会交给引擎,然后经过引擎的一系列操作;但是,切记:引擎最后要把得到的数据再来给到# spider爬虫文件让它解析并获取到真正想要的数据(callback=self.parse)这样就可以再给到自身。else:returndef get_detail(self, response):item = DoubanItem()# 获取电影简介信息# 1.meta会跟随response一块返回  2.可以通过response.meta接收   3.通过updata可以添加到新的item对象info = response.meta["info"]  # 接收电影的基本信息item.update(info)  # 把电影基本信息的字段加进去# 将电影简介信息加入相应的字段里description = response.xpath('//div[@id="link-report-intra"]//span[@property="v:summary"]/text()').extract()[0]\.strip()item['description'] = descriptionyield item

(3)items.py文件(两个项目一致!):

# -*- coding: utf-8 -*-# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.htmlimport scrapyclass DoubanItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()#需要定义字段名  就像数据库那样,有字段名,才能插入数据(即存储数据)# films_name=scrapy.Field()   #定义字段名film_name=scrapy.Field()star_name=scrapy.Field()score=scrapy.Field()description = scrapy.Field()

(4)pipelines.py文件:

  • (两个项目存储本地txt文件名可改为不一样的,便于观察!)
# -*- coding: utf-8 -*-# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.htmlimport json
import pymysqlclass DoubanPipeline(object):def open_spider(self,spider):   #爬虫文件开启,此方法就开启self.f=open("films.txt","w",encoding="utf-8")       #打开文件def process_item(self, item, spider):        #会来25次,就会调用25次这个方法  如果按常规来写,文件就会被操作25次打开关闭#为了能写进text  json.dumps将dic数据转换为strjson_str=json.dumps(dict(item),ensure_ascii=False)+"\n"self.f.write(json_str)                              #爬虫文件开启时,文件就已经打开,在此直接写入数据即可!return itemdef close_spider(self,spider):  #爬虫文件关闭,此方法就开启self.f.close()                                      #爬虫文件关闭时,引擎已经将全部数据交给管道,关闭文件

分布式实现效果:

①直接运行项目,发现在等待:

分别在两个终端中开启两个scrapy项目:(注意:之前要开启redis数据库)

在这里插入图片描述
会发现,这俩项目都在等待,不会继续执行。这是因为没有给redis这个公共区域一个初始的请求,这俩项目都在周而复始的向redis要初始url,结果一直要不到!

在两个项目的settings.py文件中设置两个的日志不显示在控制台,而是存储到.log文件中。为了便于观察:

LOG_FILE="db.log"
LOG_ENABLED=False

②再开一个终端,做如下操作:

lpush db:start_urls https://movie.douban.com/top250

在这里插入图片描述
会发现我们的两个项目都会成功的跑起来:(而且总共获取数据刚好是四页的电影信息,共计100条)

在这里插入图片描述在这里插入图片描述

总结:

会发现,第一个项目运行会显示使用了parse函数,这也就说明在redis这个公共区域的start_urls请求被第一个项目抢到了,然后就会运行这个项目,
但是,在这个项目的爬虫文件代码执行的过程中会在25次循环中给引擎发送共25次url请求,引擎得到这25个request请求后会将它们都交给scheduler调度器,再通过调度器交给redis数据库这个公共区域。
然后,两个项目的scheduler调度器就会一起抢这公共区域里的请求,并在各自的爬虫程序运行过程中提交给redis别的请求,两个项目继续抢,直到爬空。这就实现了咱爬虫的分布式爬取数据!!!

在这里插入图片描述

效果:

  • (因为没有解决爬空,所以项目运行完并不会自己关闭,而且,哪怕项目运行完了,也会一直无限的爬空,就导致两个项目爬取的保存本地的数据不够100条,所以,在两个项目运行完在爬空的时候,强制关闭两个项目,就会发现数据是完整的了!!!)

两个项目下的获取存储到本地的txt文本内的电影信息共计刚好我们所要爬取的所有目标数据:四页共100部电影的信息。

2.解决一些小问题:

2.1 解决爬空问题:(在两个项目中都进行以下操作!)

①使用拓展程序(这个文件就是为了解决爬空而生的):

两个项目进行防爬空设置后,如果数据爬取完成,在指定时间内就会自动停止爬虫!!!
(文件名:extensions.py,放到settings.py同级目录里)

加入此拓展之后完整的项目代码:
链接:https://pan.baidu.com/s/1Naie1HsWCxS-1ntorT3_RQ
提取码:e30p

# -*- coding: utf-8 -*-# Define here the models for your scraped Extensions
import loggingfrom scrapy import signals
from scrapy.exceptions import NotConfiguredlogging = logging.getLogger(__name__)class RedisSpiderSmartIdleClosedExensions(object):def __init__(self, idle_number, crawler):self.crawler = crawlerself.idle_number = idle_numberself.idle_list = []self.idle_count = 0@classmethoddef from_crawler(cls, crawler):# first check if the extension should be enabled and raise# NotConfigured otherwiseif not crawler.settings.getbool('MYEXT_ENABLED'):raise NotConfiguredif not 'redis_key' in crawler.spidercls.__dict__.keys():raise NotConfigured('Only supports RedisSpider')# get the number of items from settingsidle_number = crawler.settings.getint('IDLE_NUMBER', 360)# instantiate the extension objectext = cls(idle_number, crawler)# connect the extension object to signalscrawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)crawler.signals.connect(ext.spider_idle, signal=signals.spider_idle)return extdef spider_opened(self, spider):spider.logger.info("opened spider {}, Allow waiting time:{} second".format(spider.name, self.idle_number * 5))def spider_closed(self, spider):spider.logger.info("closed spider {}, Waiting time exceeded {} second".format(spider.name, self.idle_number * 5))def spider_idle(self, spider):# 程序启动的时候会调用这个方法一次,之后每隔5秒再请求一次# 当持续半个小时都没有spider.redis_key,就关闭爬虫# 判断是否存在 redis_keyif not spider.server.exists(spider.redis_key):self.idle_count += 1else:self.idle_count = 0if self.idle_count > self.idle_number:# 执行关闭爬虫操作self.crawler.engine.close_spider(spider, 'Waiting time exceeded')

②在settings.py文件中设置这个拓展程序:

# Enable or disable extensions                  #扩展程序
# See https://docs.scrapy.org/en/latest/topics/extensions.html
EXTENSIONS = {# 'scrapy.extensions.telnet.TelnetConsole': None,'film.extensions.RedisSpiderSmartIdleClosedExensions':500,					#开启extensions.py这个拓展程序
}
MYEXT_ENABLED = True      # 开启扩展
IDLE_NUMBER = 3           # 配置空闲持续时间单位为 3个 ,一个时间单位为5s

注意:redis中存储的数据:

  • spidername:items
    list类型,保存爬虫获取到的数据item内容是json字符串。
  • spidername:dupefilter
    set类型,用于爬虫访问的URL去重内容是40个字符的url的hash字符串
  • spidername:start_urls
    list类型,用于接收redisspider启动时的第一个url
  • spidername:requests
    zset类型,用于存放requests等待调度。内容是requests对象的序列化字符串。

3. 关于分布式(Scrapy_redis)的总结:

()分布式爬虫
一.settings里的配置
# 启用调度将请求存储进redis
# 1.必须
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#2. 必须
# 确保所有spider通过redis共享相同的重复过滤。
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# 3.必须
# 指定连接到Redis时要使用的主机和端口。
REDIS_HOST = 'localhost'
REDIS_PORT = 6379二.spider文件更改from scrapy_redis.spiders import  RedisSpider #1 导出 RedisSpiderclass DbSpider(RedisSpider):  #2使用RedisSpider类# start_urls = ['https://movie.douban.com/top250/']  #3将要请求放在  公共区域 redis里面redis_key = "db:start_urls"#4  设置一个键  寻找起始url.redis数据库中 写入  start_urls      
lpush  db:start_urls   https://movie.douban.com/top250/四.解决爬空的问题
1.解决爬空的文件    extensions.py  主要是RedisSpiderSmartIdleClosedExensions
2.设置
MYEXT_ENABLED = True      # 开启扩展
IDLE_NUMBER = 3           # 配置空闲持续时间单位为 3个 ,一个时间单位为5s

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

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

相关文章

Vue el-table 合并单元格

一般常见的就是下图这种的单列&#xff0c;上下重复进行合并。 有时候可能也会需要多行多列的合并。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&qu…

3.19网络编程

select实现的TCP并发服务器 #include <myhead.h> #define SER_IP "192.168.141.134" #define SER_PORT 8888 int main(int argc, const char *argv[]) {// 1、创建一个套接字int sfd -1;sfd socket(AF_INET, SOCK_STREAM, 0);if (sfd -1){perr…

Division by Invariant Integers using Multiplication

在处理器中&#xff0c;整数除法的成本通常是整数乘法的几倍&#xff1a; 流水线式的组合乘法器通常在不到10个周期内完成操作&#xff1b;而对于整数除法则没有硬件支持&#xff0c;或者使用的迭代除法器比乘法器慢几倍。 表 1.1 比较了一些处理器上乘法和除法的时间。这张表…

php 页面中下载文件|图片

一、需求 页面中点击下载图片 二、实现 protected function pageLoad(){$filePath $_GET[file];$host $_SERVER[HTTP_HOST];$file http://.$host.$filePath;$fileName basename($filePath);$content file_get_contents($file);ob_clean();$suffixArr explode(., $file…

数据库系统概论-练手题集合【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的练手题&#xff0c;以供大家期末复习和考研复习的时候使用。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/三章 关系数据库和标准语言SQL 第四/五章 数据库安全性和完整性…

服务器硬件基础知识和云服务器的选购技巧

概述 服务器硬件基础知识涵盖了构成服务器的关键硬件组件和技术&#xff0c;这些组件和技术对于服务器的性能、稳定性和可用性起着至关重要的作用。其中包括中央处理器&#xff08;CPU&#xff09;作为服务器的计算引擎&#xff0c;内存&#xff08;RAM&#xff09;用于数据临…

zookeeper快速入门(合集)

zookeeper作为一个分布式协调框架&#xff0c;它的创建就是为了方便或者简化分布式应用的开发。除了服务注册与发现之外&#xff0c;它还能够提供更多的功能&#xff0c;但是对于入门来说&#xff0c;看这一篇就够了。后续会讲zookeeper的架构设计与原理&#xff0c;比如zookee…

【学习】软件测试中的二八定理是什么?如何提高测试的效率和质量

软件测试领域的二八定理&#xff0c;是指在软件测试过程中&#xff0c;通常只有20%的测试用例能够发现软件中80%的错误。这一现象表明&#xff0c;软件测试的效率和效果并不成正比&#xff0c;测试用例的数量并不等于发现错误的能力。因此&#xff0c;在软件测试过程中&#xf…

<Linux> 生产者消费者模型

目录 前言&#xff1a; 一、什么是生产者消费者模型 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;生产者消费者之间的关系 &#xff08;三&#xff09;生产者消费者模型特点 &#xff08;四&#xff09;生产者消费者模型的优点 二、基于阻塞队列实现生产…

前端标准 JsDoc 代码片段注释 ( 快速生成代码片段网站 )

{ "快速单行注释": {"prefix": "z","body": [" /**"," * $1"," * param { }"," * return { }"," */"],"description": "快速注释" } } 配置之后…

嵌入式科普(12)西门子PROFINET ERTECH 200P-3路演

目录 一、路演观后感/总结 二、分享两个公众号 2.1 HMS工业网络 2.2 Omdia 三、ERTECH 200P-3资料 四、ERTECH 200P-3路演分享 嵌入式科普(12)西门子PROFINET ERTECH 200P-3路演 一、路演观后感/总结 老罗砸西门子冰箱时候的傲慢 -> 路演的谦逊 国外工程师包吃住差旅…

Linux(openEuler)部署SpringBoot前后端分离项目(Nginx负载均衡)

假如数据库在本地&#xff0c;没有放在Linux中 1.先把数据库中root的主机改成% 2.项目中的数据库链接配置换成本机ip 3.打包 4.把打包好的jar包放到Linux中 一般把jar包放到opt下 5.把前端部分拷贝到Linux的nginx中 5.1在package.json中修改build的值为图中这样 5.2同时由于在…

动态规划(算法竞赛、蓝桥杯)--单调队列优化修建草坪

1、B站视频链接&#xff1a;E44 单调队列优化DP 修剪草坪_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; typedef long long LL; const int N1e510; int n,k,q[N]; LL w[N],f[N],sum;int main(){cin>>n>>k; k; //for(int i1;i<n;i){ci…

Vue多文件学习项目综合案例——小兔鲜,黑马vue教程

文章目录 一、项目截图二、主要知识点三、Main.js四、App.vue五、componentsXtxBanner.vueXtxFooter.vueXtxHeaderNav.vueXtxHotBrand.vueXtxNewGoods.vueXtxShortCut.vueXtxTopic.vue 六、stylesbase.csscommon.css 一、项目截图 二、主要知识点 把静态页面拆分成一个个vue组…

数据库简介与MySQL编译安装

1数据库基础 什么是数据库 数据库&#xff08;Database&#xff09;是一个有组织的数据存储系统&#xff0c;用于有效地存储、检索、管理和维护数据。数据库系统允许用户以结构化的方式存储和操作大量数据&#xff0c;并提供了一种可靠的方法来管理和维护这些数据&#xff0c…

数据库的基本概念与安装MySQL

一、数据库的基本概念 1、什么是数据&#xff1f; 描述事物的符合记录包括数字、文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储 2、什么是表&#xff1f; 将不同的记录组织在一起用来存储具体数据 3、什么是数据库 表的集合&#xff0c;是存储数…

2023年中国电商市场研究报告

研究范畴界定为中国国内2C电商市场 ⚠️ 关键点&#xff1a; 流量红利减少&#xff0c;电商市场进入存量增量 竞争的发展阶段&#xff1b;新兴电商平台不断挑战现有头部电商平台行业地位&#xff1b;消费者更加趋于理性&#xff0c;更加关注低价和服务&#xff1b;市场趋势&…

嵌入式和 Java选哪个?

今日话题&#xff0c;嵌入式和 Java 走哪个?对于嵌入式领域有浓厚兴趣的人&#xff0c;并不会比Java行业薪资低&#xff0c;处于上中游水平。特别是从2020年开始&#xff0c;嵌入式领域受益于芯片产业的兴起&#xff0c;表现出了强劲的增长势头。薪资水平受多方面因素影响。嵌…

AndroidLinux GPIO控制方法

目录 1 GPIO整体架构 2 user space 层 gpio使用方法 2.1 sysfs控制方法 2.1.1 kernel版本区别 2.1.2 /sys/class/gpio 2.1.3 /sys/bug/gpio/devices 2.2 chardev控制方法 2.2.1 chardev 示例代码 2.2.2 示例代码主要步骤描述 2.2.3 include/linux/gpio.h 全部代码 2.3…

mmz批量多页抓取数据-AES.CBC算法-爬虫

目标&#xff1a;mmz多页下载 方法&#xff1a;加一个for循环实现多页的下载 问题&#xff1a;浏览器传输服务器时对页码参数做了加密处理 解决方法&#xff1a; 1、判断加密算法模式&#xff08;mmz是AES-CBC算法&#xff09; 2、找到加密的key和iv 代码&#xff1a; i…