当前位置: 首页 > news >正文

【Redis07】Redis基础:Bitmap 与 HyperLogLog 相关操作

Redis基础学习:Bitmap 与 HyperLogLog 相关操作

继续进行 Redis 基础部分的学习,今天我们学习的是两种另外的数据类型。说是数据类型,但其实它们实际上使用的都是 String 类型做为底层基础,只不过是在存储的时候进行了一些特殊的操作。换句话说,这两种类型并不是真正意义上的“数据类型”,换成“数据操作”可能更合适一些。

Bitmap

先来看看 Bitmap ,这是个啥玩意?其实呀,这个就是 位图 ,Bit 就是 比特 的意思嘛。一提到 比特 是不是又得来二进制了?这个真没办法,学计算机的,任何工具都逃不掉的就是 二进制 相关的操作。不过在 Redis 中的操作还是比较简单的,毕竟我们主要的操作还是在程序业务端进行,Redis 只是一个数据库而已。

Bitmap 在 Redis 中可以存储大约 40 多亿条数据,或者说是40多亿个 0 和 1 位。估计不少小伙伴马上就能想到它的应用场景了,我们在最后再说。先来看看它的操作。

Bitmap 操作命令

设置和获取指定位置的位信息,直接使用 SETBIT 和 GETBIT。

127.0.0.1:6379> setbit a 8 1
(integer) 0
127.0.0.1:6379> setbit a 4 1
(integer) 0
127.0.0.1:6379> get a
"\b\x80"
127.0.0.1:6379> getbit a 4
(integer) 1
127.0.0.1:6379> getbit a 5
(integer) 0

注意到上面的例子,我们直接使用 GET 命令也可以获取到内容,前面就说过了,它本身就是使用 String 类型存储的。只不过使用位操作的话,我们要理解 8 位代表 1 个字节的问题。因此,设置一个 8 位的二进制代码才能表示出一个我们能看懂的字符。

127.0.0.1:6379> setbit b 1 1
(integer) 0
127.0.0.1:6379> setbit b 4 1
(integer) 0
127.0.0.1:6379> setbit b 5 1
(integer) 0
127.0.0.1:6379> setbit b 6 1
(integer) 0
127.0.0.1:6379> setbit b 7 1
(integer) 0
127.0.0.1:6379> get b
"O"
127.0.0.1:6379> setbit c 1 1
(integer) 0
127.0.0.1:6379> setbit c 2 1
(integer) 0
127.0.0.1:6379> setbit c 4 1
(integer) 0
127.0.0.1:6379> setbit c 6 1
(integer) 0
127.0.0.1:6379> setbit c 7 1
(integer) 0
127.0.0.1:6379> get c
"k"

看出上面的例子是什么意思了吗?第一个 b ,设置的其实是 01001111 ,转换成十进制是 79 ,对应的 ASC2 码就是大写英文 O 。另一个 c 也是一样的意思,01101011 的十进制是 107 ,表示的英文是 107 。如果要表示 Ok 这两个字母的话,就需要两个 8 位,也就是两个字节 01001111 01101011 。

127.0.0.1:6379> setbit d 1 1
(integer) 0
127.0.0.1:6379> setbit d 4 1
(integer) 0
127.0.0.1:6379> setbit d 5 1
(integer) 0
127.0.0.1:6379> setbit d 6 1
(integer) 0
127.0.0.1:6379> setbit d 7 1
(integer) 0
127.0.0.1:6379> get d
"O"
127.0.0.1:6379> setbit d 9 1
(integer) 0
127.0.0.1:6379> setbit d 10 1
(integer) 0
127.0.0.1:6379> setbit d 12 1
(integer) 0
127.0.0.1:6379> setbit d 14 1
(integer) 0
127.0.0.1:6379> setbit d 15 1
(integer) 0
127.0.0.1:6379> get d
"Ok"

好玩吧?中文也是一样的,只是三个字节的 UTF8 编码,需要三个 8 位的数据才能表示一个中文字,大家可以试试哦。

127.0.0.1:6379> set s 中
OK
127.0.0.1:6379> get s
"\xe4\xb8\xad"
127.0.0.1:6379> getbit s 0
(integer) 1
127.0.0.1:6379> getbit s 1
(integer) 1
127.0.0.1:6379> getbit s 2
(integer) 1
127.0.0.1:6379> getbit s 3
(integer) 0

“中”这个字直接 GET 返回的是16进制数据,将 e4b8ad 转换成二进制是 11100100 10111000 10101101 ,然后我们就可以使用 GETBIT 来验证是不是和我们手动转换出来的是一样的结果。

大家有兴趣的可以再多了解一下字符编码相关的知识,比如在 Redis 中使用的 UTF8 ,为什么单个字节第一位只能是 0 ,必须是 0xxxxxxx 这样的,中文也都是固定的 1110xxxx 10xxxxxx 10xxxxxx 这种格式,很有意思哦。

位操作

既然是位操作,那么 与、或、异或、非 操作肯定要有啦。

127.0.0.1:6379> bitop or dd b c
(integer) 1
127.0.0.1:6379> get dd
"o"
127.0.0.1:6379> bitop and dd b c
(integer) 1
127.0.0.1:6379> get dd
"K"
127.0.0.1:6379> bitop xor dd b c
(integer) 1
127.0.0.1:6379> get dd
"$"
127.0.0.1:6379> bitop not dd b
(integer) 1
127.0.0.1:6379> get dd
"\xb0"

查询第一个bit位位置

通过 BITPOS 命令,可以查询到指定的 key 中,第一个出现的位数据的位置。

127.0.0.1:6379> BITPOS a 1
(integer) 4
127.0.0.1:6379> BITPOS a 0
(integer) 0

第一条命令是查询第一个 1 出现的位置,第二条命令是查询第一个 0 出现的位置。它还有第二个参数,这个参数是一个偏移量,不过需要注意的是,它指的是字节的偏移量,不是位的偏移量。

127.0.0.1:6379> BITPOS d 1 0
(integer) 1
127.0.0.1:6379> BITPOS d 1 1
(integer) 9
127.0.0.1:6379> BITPOS d 1 2
(integer) -1

统计数量

BITCOUNT 可以统计指定 key 中 1 出现的次数。

127.0.0.1:6379> BITCOUNT d
(integer) 10
127.0.0.1:6379> BITCOUNT d 1 0 2
(error) ERR syntax error
127.0.0.1:6379> BITCOUNT d 1 0 1
(error) ERR syntax error
127.0.0.1:6379> BITCOUNT d 1 2
(integer) 5
127.0.0.1:6379> BITCOUNT d 0 2
(integer) 10
127.0.0.1:6379> BITCOUNT d 2 3
(integer) 0

它还有两个参数,同样也是字节偏移量和长度。

位域操作

位域这个东西我就不太懂了,只是给个例子,这一块深入学过 C 的同学应该会比较了解。

127.0.0.1:6379> setbit e 1 1
(integer) 0
127.0.0.1:6379> get e
"@"
127.0.0.1:6379> BITFIELD e incrby i5 100 1 get u4 0
1) (integer) 1
2) (integer) 4
127.0.0.1:6379> get e
"@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"

Bitmap 经典例子

好了,Bitmap 的基本操作就是上面那些,介绍的比较简单,不过也确实都不复杂。但前提是要对二进制和进制之间的转换以及字符编码要有一定的了解。如果我们只是为了去显示字符串的位,那就有点大材小用了,其实 Bitmap 有个非常强悍的能力,也就是运用 BITCOUNT 去进行数据统计。

包括官网上给出的也是类似这样的例子,统计登录用户数。

最开始我们已经知道,一个 key 可以保存40多亿个位,那么我们可以把用户id当作位索引,然后某个用户今天登录了,就给它的位设置为 1 ,然后就可以 BITCOUNT 快速统计出今天有多少用户登录了系统,速度相当快哦。

127.0.0.1:6379> setbit user_login_20220509 100010 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 25525 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 8782 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 9846526547 1
(error) ERR bit offset is not an integer or out of range
127.0.0.1:6379> setbit user_login_20220509 98465265 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 399999999 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 3999999999 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 4294967295 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 4294967296 1
(error) ERR bit offset is not an integer or out of range
127.0.0.1:6379> BITCOUNT user_login_20220509
(integer) 7

HyperLogLog

既然说到统计了,那么 HyperLogLog 这个操作类型就不得不提了。它是一种概率数据结构,用于计算唯一事物的集合数量。同时,它是以内存换精度的,也就是说,它能极大的节约内存,但是会有精度丢失的问题。最坏情况下,也就是内存占用最大的情况下,它也只需要 12K 的内存容量就可以存储非常巨大的数据量,精度的丢失会在 1% 以内。它也可以实现上面 Bitmap 中统计的例子,我们先来看看相关的操作命令。

127.0.0.1:6379> pfadd hll a b c d e f g
(integer) 1
127.0.0.1:6379> pfcount hll
(integer) 7
127.0.0.1:6379> pfadd hll a b c d e f g h i j k
(integer) 1
127.0.0.1:6379> pfcount hll
(integer) 11

PFADD 添加数据,PFCOUNT 获取数量。基本的操作命令就这两个,是不是无敌了。除了这两个外,还有一个合并的命令,类似于集合的并集操作。

127.0.0.1:6379> PFADD pfa a b c d e f
(integer) 1
127.0.0.1:6379> PFADD pfb d c a e g i
(integer) 1
127.0.0.1:6379> pfadd pfc b d f i l m n o
(integer) 1
127.0.0.1:6379> PFMERGE pfmerge pfa pfb pfc
OK
127.0.0.1:6379> pfcount pfmerge
(integer) 12

另外,它也是以 String 为基本存储类型的,所以用 GET 也能看到内容。

127.0.0.1:6379> get hll
"HYLL\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00Fm\x80I\xe8\x80L\"\x80D<\x848\x80B=\x80K\x83\x80B\xed\x84A\xfc\x8cG\x8e\x80Bm\x80BZ"

好了,接下来说下 HyperLogLog 和其它方式统计的对比,以下是网上找到的相关文章获取到的资料。

如果我们使用 SET 来进行基数统计,那么假设每一个元素的 32Bit(2^24 ≈ 1600万; 2^32 ≈ 42亿) , 假设存储1亿个不重复的元素那么我们需要 100 000 000 * 32 /8/1024/1024 ≈ 381MB。

如果我们用 Bitmap 来进行基数统计,每个元素对应一位(bit),假设我们存储1亿个不重复的元素那么我们需要 100 000 000 /8/1024/1024 ≈ 12MB。

然而我们使用 HyperLogLog ,一个键占用的内容空间是12KB,并且这个键可以处理海量数据。

它们三个都可以应用在统计不重复元素的场景:

  • HyperLogLog:海量数据,可以忍受 0.81% 误差的场景,其实大数据处理的时候都会有误差。

  • Bitmap:数据量不大,不能忍受误差,元素连续的(自增的用户主键id)。

  • SET:数据量不大,不能忍受误差,元素没有规律,并且需要返回实际的单个元素。

总结

今天的内容挺好玩吧,Bitmap 和 HyperLogLog 最常用的其实都是一个不重复数据统计的场景,但是又各有优势。在日常的工作中如果有类似的应用场景,完全就可以使用这两种数据操作来试试了。

扩展知识:布隆过滤器

布隆过滤器(Bloom Filter),听说过没?面试有没有被坑过?跟你说,布隆过滤器的基础知识就是 Bitmap ,HyperLogLog 也是它的类似实现之一。啥叫布隆过滤器?就是能够快速地查找某一个元素是否存在于指定的集合中,最典型的做法就是使用二进制位来进行操作,这不就是 Bitmap 嘛。

当然,完整的布隆过滤器的实现还是要更复杂一些,它一般会有三个 Hash 函数,生成三个不同位置,只有当三个位置全部命中时,才会认为指定的数据已经存在。从这一点上来说,其实就是在有限的空间内可以存放更多的数据。但它也会出现不同的数据三个 Hash 函数计算结果一致的概率,但我们可以增加更多的 Hash 函数,不过相应地性能也会降低。同样,它也会有精度问题,反正大概原理就是这样。布隆过滤器有一个非常经典的名言,那就是“我说你不在,那你一定不在;我说你存在,你有可能存在(也可能不存在)!”。在 packgist 上也能搜到纯 PHP 实现布隆过滤器的 Composer 包,大家可以自己下载源码学习一下哦!

相关文章:

【Redis07】Redis基础:Bitmap 与 HyperLogLog 相关操作

Redis基础学习&#xff1a;Bitmap 与 HyperLogLog 相关操作继续进行 Redis 基础部分的学习&#xff0c;今天我们学习的是两种另外的数据类型。说是数据类型&#xff0c;但其实它们实际上使用的都是 String 类型做为底层基础&#xff0c;只不过是在存储的时候进行了一些特殊的操…...

华为路由器 VRRP主备配置

组网需求 如下图所示&#xff0c;PC1通过SW1双归属到R1和R2。为保证用户的各种业务在网络传输中不中断&#xff0c;需在R1和R2上配置VRRP主备备份功能。 正常情况下&#xff0c;主机以R1为默认网关接入Internet&#xff0c;当R1故障时&#xff0c;R2接替R1作为网关继续进行工作…...

docker容器安装ES

1.拉取镜像 docker pull elasticsearch:6.5.42.修改别名 docker tag [容器ID] es65:6.5.42.启动应用 docker run -it -d -p 9200:9200 -p 9300:9300 --name es -e ES_JAVA_OPTS"-Xms128m -Xmx128m" es65:6.5.43.拷贝配置文件到宿主机 docker cp es:/usr/share/ela…...

Python Module — prompt_toolkit CLI 库

目录 文章目录目录prompt_toolkit示例化历史记录热键自动补全多行输入Python 代码高亮自定义样式prompt_toolkit prompt_toolkit 是一个用于构建 CLI 应用程序的 Python 库&#xff0c;可以让我们轻松地构建强大的交互式命令行应用程序。 自动补全&#xff1a;当用户输入命令…...

springboot mybatis-plus 调用 sqlserver 的 存储过程 返回值问题

问题&#xff1a; 在使用 mybatis-plus 调用sqlserver 存储过程 没有返回值 经过资料查找 注意点 此处使用Map传参&#xff0c;原因在于存储过程的返回值&#xff0c;通常在参数定义中实现&#xff0c;如In 入参、out 出参。 这样当执行后有结果返回时&#xff0c;则可以将结…...

【0180】PG内核读取pg_hba.conf并创建HbaLine记录(1)

文章目录 1. pg_hba.conf文件是什么?2. postmaster何时读取pg_hba.conf?2.1 pg内核使用pg_hba.conf完成客户端认证的原理2.2 读取pg_hba.conf的几个模块3. pg内核读取pg_hba.conf过程3.1 VFD机制获取文件描述符3.2 根据fd读取文件内容相关阅读: 【0178】DBeaver、pgAdmin I…...

【原型设计工具】​​上海道宁为您提供Justinmind,助力您在几分钟内形成原型,并现场测试,无需编写任何代码

Justinmind是用于 Web和应用程序的原型制作工具 在几分钟内形成原型 并在现场进行测试 无需编写任何代码 单击一下即可轻松在线获取您的设计 并与整个团队共享 享受高效的沟通和快速反馈 以实现持续改进和利益相关者的支持 开发商介绍 JustinMind是由西班牙JustinMind公…...

计算机网络中---HDLP协议和PPP协议

目录 HDLC协议PPP协议HDLC协议和PPP协议HDLC协议 HDLC协议【高级数据链路控制协议】是一种数据链路层协议,用于在两个节点之间传输数据。以下是HDLC协议的重点知识: HDLC协议定义了一种标准的帧格式,包括起始标志、地址字段、控制字段、信息字段、校验字段和结束标志。HDLC…...

k8s之节点kubelet预留资源配置

k8s之kubelet预留资源配置1 前言2 预留资源Kube-reservedSystem-reservedEviction Thresholds实施节点可分配约束3 Pod优先级4 生产应用配置文件重启kubelet服务查看节点资源1 前言 最近k8s在使用过程中遇到这样一个问题 由于Pod没有对内存及CPU进行限制&#xff0c;导致Pod在…...

机器学习笔记之前馈神经网络(四)反向传播算法[数学推导过程]

机器学习笔记之前馈神经网络——反向传播算法[数学推导过程]引言回顾&#xff1a;感知机算法非线性问题与多层感知机反向传播算法(BackPropagation,BP\text{BackPropagation,BP}BackPropagation,BP)场景构建求解各权重更新量图示描述反向传播过程总结引言 上一节介绍了M-P\tex…...

vscode+elementui校园跑腿系统 nodejs+vue

本系统从用户的角度出发&#xff0c;结合当前的校园环境而开发的&#xff0c;在开发语言上是使用的Java语言&#xff0c;在框架上我们是使用的Vue框架&#xff0c;数据库方面使用的是MySQL数据库&#xff0c;开发工具为IDEA。 基于Vue的校园跑腿管理系统中的管理员配送用户都可…...

[蓝桥杯单片机8]定时器的简单应用

1、本实验内容 利用51单片机的定时/计数器T0的模式1实现间隔定时&#xff0c;每隔1秒L1指示灯闪烁一下&#xff0c;也就是点亮0.5秒&#xff0c;熄灭0.5秒&#xff1b;每隔2秒L8指示灯闪烁一下&#xff0c;即点亮1秒&#xff0c;熄灭1秒。2、基础知识 定时/计数器&#xff0c;是…...

node-HTTP协议

文章目录一. 概念二. 请求报文的组成三.HTTP请求行四.HTTP请求头五.HTTP的请求体六.响应报文的组成七.创建HTTP服务八.获取HTTP请求报文九.HTTP设置响应十.GET和POST请求的区别一. 概念 HTTP协议. 中文叫超文本传输协议; 是一种基于TCP/IP的应用层通信协议; 这个协议详细规定了…...

基于springboot+vue的地方美食分享网站

081-springboot基于vue的地方美食分享网站开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&am…...

【Android】之【Aplication】

一、Application简介 Application和Activity&#xff0c;Service一样是Android框架的一个系统组件&#xff0c;当Android程序启动时系统会创建一个Application对象&#xff0c;用来存储系统的一些信息。 Android系统自动会为每个程序运行时创建一个Application类的对象且只创建…...

社区之声|Grant Program支持Moonbeam生态壮大

在本次社区之声会议中&#xff0c;Moonbeam基金会解释生态系统Grant流程、一个由社区成员组成的圆桌讨论表达各自对此次Grant的看法&#xff0c;Moonbeam开发者关系工程师演示了如何在Snapshot对申请生态系统Grant项目的投票。观看视频回顾 请注意&#xff0c;内容仅供参考&am…...

GO实现Redis:GO实现Redis协议解析器(2)

本文实现Redis的协议层&#xff0c;协议层负责解析指令&#xff0c;然后将指令交给核心database执行echo database用来测试协议层的代码https://github.com/csgopher/go-redis RESP协议 RESP是客户端与服务端通信的协议&#xff0c;格式有五种&#xff1a;正常回复&#xff1…...

Geoserver 发布wmts服务,以及cesium加载发布的wmts服务

WMTS提供了一种采用预定义图块方法发布数字地图服务的标准化解决方案。WMTS弥补了WMS不能提供分块地图的不足。WMS针对提供可定制地图的服务&#xff0c;是一个动态数据或用户定制地图&#xff08;需结合SLD标准&#xff09;的理想解决办法。WMTS牺牲了提供定制地图的灵活性&am…...

【微信小程序】selectComponent(#id)失败得到是null分析

小程序中无法像网页中轻易的获取DOM元素&#xff0c;需要依靠 this.selectComponent(#id)this.selectAllComponents(#id) 本文主要针对 this.selectComponent 获取DOM元素失败的原因 下面开始正文 上图为我的业务代码&#xff0c;由图可知&#xff0c;通过for循环遍历渲染ca…...

JVM中引用计数法与可达性分析

目录 概要 如何判断对象已死&#xff1f; 引用计数算法 优点 缺点 举例说明 可达性分析 图例说明 GC Roots的对象包括以下几种 可达性分析回收过程 四大引用 回收方法区 方法区的垃圾收集主要回收两部分内容&#xff1a; 1. 废弃的常量 2. 不再使用的类型。 JVM是…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...