目录

一、前提  返回目录

经常会遇到登录系统时候需要输入动态验证码的情况,但是自动化如何识别图片然后登陆系统

需要用到pytesseract识别验证码图片以及PIL图像处理方法

import pytesseract
from PIL import Image, ImageEnhance

二、获取验证码  返回目录

1、思路

  • 步骤①:定位图片的元素,并且截取当前浏览器的页面图片
  • 步骤②:获取验证码坐标点,以及验证码图片、浏览器、截图的长和宽
  • 步骤③:截取截图里的验证码图片,获得的验证码图片并保存
  • 步骤④:获得验证码code

2、实践方法

以下步骤都在getCodeImg方法里面:

    def getCodeImg(self):
"""获取验证码"""

1)步骤①

imgPath:浏览器截图图片路径

savePath:保存验证码图片路径

加了一个点击验证码图片方法,目的是为了后面重新获取验证码用的。

        # 步骤①:
basePath = Fun().upPath() + "/utils/img/"
imgPath = basePath + "code.png"
savePath = basePath + "saveCode.png"
# 定位图片元素
imgElement = self.webDriverWait(Loc.codeImg_loc)
# 点击验证码图片
imgElement.click()
# 截取当前页面的图并放到目录里
self.driver.save_screenshot(imgPath)

2)步骤②

获取验证码坐标是为了下面计算验证码占据整个浏览器的百分比。

        # 步骤②:
# 获取验证码x,y轴,x&y代表左上角的坐标点
imgLocation = imgElement.location
print(f"图片坐标点:{imgLocation}")
# 获取验证码长、宽
imgSize = imgElement.size
print(f"图片长、宽:{imgSize}")
# 获取浏览器的长、宽
windowSize = self.driver.get_window_size()
print(f"浏览器长、宽:{windowSize}")
# 打开截图
openImg = Image.open(imgPath)
# 获取保存截图的长、宽(宽:2700, 高:1950)
screenImgSize = openImg.size
print(f"保存截图的长、宽:{screenImgSize}")

3)步骤③

计算图片四个边距在真实浏览器的百分比,用这个百分比乘以浏览器截图的长和宽,得出截图里面的验证码大概位置,然后再自己进行调整截图里的边距大小。

最后再把验证码图片进行图片处理,灰色度和增强对比度等等,提高获取验证码图片的识别率。

        # 步骤③:截取截图的验证码图片
# 图片左边距占据整个浏览器的百分比
left = imgLocation['x']/windowSize['width']
# 图片上边距占据整个浏览器的百分比
top = imgLocation['y']/windowSize['height']
# 图片右边距占据整个浏览器的百分比
right = (imgLocation['x'] + imgSize['width'])/windowSize['width']
# 图片下边距占据整个浏览器的百分比
bottom = (imgLocation['y'] + imgSize['height'])/windowSize['height'] # 需要截取的坐标
screenLocation = (
left * screenImgSize[0],
top * screenImgSize[1]+150,
right * screenImgSize[0],
bottom * screenImgSize[1]+150
)
# 打开截图并截取区域并保存
img = openImg.crop(screenLocation)
img = img.convert('L') # 转换模式:L | RGB      # 提高识别率(见下面)
# enhancer = ImageEnhance.Color(img)
# enhancer = enhancer.enhance(0)
# enhancer = ImageEnhance.Brightness(enhancer)
# enhancer = enhancer.enhance(2)
# enhancer = ImageEnhance.Contrast(enhancer) # 增强对比度
# enhancer = enhancer.enhance(8)
# enhancer = ImageEnhance.Sharpness(enhancer)
# img = enhancer.enhance(20) img = ImageEnhance.Contrast(img) # 增强对比度
img = img.enhance(2.0)
img.save(savePath)

不过识别率还是比较低的,下面有种可以提高识别率的方法,但是见仁见智,实测有时很快有时很慢。

参考文章:

pytesseract 识别率低提升方法

【python图像处理】图像的增强(ImageEnhance类详解)

4)步骤④

获取的验证码可能不是我们想要的,会出现中间有大小空格、换行情况,需要替换掉

    def remove(self,string):
"""字符串去除空格或换行""" str = string.replace(" ", "")  #大空格
str = str.replace("", "")     #小空格
str = str.replace('\n', "")    #换行符
return str def getCodeImg(self):
"""获取验证码""" # 步骤④:获得code验证码
code = pytesseract.image_to_string(img).strip()
print(f"提取的验证码为:【{self.remove(code)}】")
return self.remove(code)

三、获取4位验证码  返回目录

1、思路

  • 因为实际效果我们要获取4位验证码
  • 虽然上一步骤获取到了验证码,但是还是会出现不足4位或者超过4位的验证码
  • 需要进行判断筛选,只要4位的验证码

2、实践方法

先判断是否满足4位,不满足的话while循环重新获取验证码,满足4位跳出循环,并return出来

    def getCode(self):
"""获取4位数验证码""" # 循环前获取code字数
code = self.getCodeImg()
print(f"验证码位数:【{len(code)}】位")
while len(code) != 4:
# 重新获取验证码
code = self.getCodeImg()
# print(f"验证码位数:【{len(code)}】位")
# if len(code) != 4:
# print("验证码不是4位数!")
print(f"输出4位验证码为:{code}")
return code

四、判断验证码是否正确  返回目录

1、思路

  • 虽然我们获取了4位验证码,但是因为识别率的问题,获取的验证码仍然不对,导致提示验证码错误

  • 我们还得重新获取一遍验证码,直到获取成功能够登陆为止

2、实践方法

判断页面如果有错误提示,则重新获取4位验证码

    def checkCode(self):
"""判断验证码是否正确""" try:
errorMsg = self.get_text(Loc.errorMsg_loc)
if errorMsg == "验证码错误":
self.inputCodeAction()
except:
print("验证码正确,进入首页!")

五、输入验证码登录  返回目录

1、思路

  • 封装一个Action方法,把之前的各种方法封装在一起
  • 实现输入验证码登录
  • 此封装方法只是在获取验证码的类里,输入用户名和密码的方法在另一个类里

2、实践方法

    def inputCodeAction(self):
"""输入验证码登录""" code = self.getCode()             # 获取4位验证码
self.el_clear_sendKeys(Loc.code_loc, code) # 清空验证码输入框并输入验证码
self.el_click(Loc.loginButton_loc)      # 点击登录按钮
self.checkCode()                 # 判断验证码是否正确

六、登录页面类  返回目录

登录页面类,封装输入用户名、密码、验证码、登录的操作方法

class LoginPage(BasePage):
"""登录页面""" def login_action(self,username,password):
"""登录操作"""
self.el_sendKeys(Loc.username_loc, username) # 输入用户名
self.el_sendKeys(Loc.password_loc, password) # 输入密码
GetCode(self.driver).inputCodeAction() # 输入验证码并登录

七、完整的获取验证码类代码  返回目录

import pytesseract
from PIL import Image, ImageEnhance
from page_object.page.basePage import BasePage
from page_object.utils.functions import Functions as Fun
from page_object.locator.loginPageLoc import LoginPageLoc as Loc class GetCode(BasePage): def remove(self,string):
"""字符串去除空格或换行""" str = string.replace(" ", "")
str = str.replace("", "")
str = str.replace('\n', "")
return str def getCodeImg(self):
"""获取验证码""" # 步骤①:
basePath = Fun().upPath() + "/utils/img/"
imgPath = basePath + "code.png"
savePath = basePath + "saveCode.png"
# 定位图片元素
imgElement = self.webDriverWait(Loc.codeImg_loc)
# 点击验证码图片
imgElement.click()
# print(f"点击【{next(iter(Fun()))}】次验证码图片")
# 截取当前页面的图并放到目录里
self.driver.save_screenshot(imgPath) # 步骤②:
# 获取验证码x,y轴,x&y代表左上角的坐标点
imgLocation = imgElement.location
print(f"图片坐标点:{imgLocation}")
# 获取验证码长、宽
imgSize = imgElement.size
print(f"图片长、宽:{imgSize}")
# 获取浏览器的长、宽
windowSize = self.driver.get_window_size()
print(f"浏览器长、宽:{windowSize}")
# 打开截图
openImg = Image.open(imgPath)
# 获取保存截图的长、宽(宽:2700, 高:1950)
screenImgSize = openImg.size
print(f"保存截图的长、宽:{screenImgSize}") # 步骤③:截取截图的验证码图片
# 图片左边距占据整个浏览器的百分比
left = imgLocation['x']/windowSize['width']
# 图片上边距占据整个浏览器的百分比
top = imgLocation['y']/windowSize['height']
# 图片右边距占据整个浏览器的百分比
right = (imgLocation['x'] + imgSize['width'])/windowSize['width']
# 图片下边距占据整个浏览器的百分比
bottom = (imgLocation['y'] + imgSize['height'])/windowSize['height'] # 需要截取的坐标
screenLocation = (
left * screenImgSize[0],
top * screenImgSize[1]+150,
right * screenImgSize[0],
bottom * screenImgSize[1]+150
)
# 打开截图并截取区域并保存
img = openImg.crop(screenLocation)
img = img.convert('L') # 转换模式:L | RGB # enhancer = ImageEnhance.Color(img)
# enhancer = enhancer.enhance(0)
# enhancer = ImageEnhance.Brightness(enhancer)
# enhancer = enhancer.enhance(2)
# enhancer = ImageEnhance.Contrast(enhancer) # 增强对比度
# enhancer = enhancer.enhance(8)
# enhancer = ImageEnhance.Sharpness(enhancer)
# img = enhancer.enhance(20) img = ImageEnhance.Contrast(img) # 增强对比度
img = img.enhance(2.0)
img.save(savePath) # 步骤④:获得code验证码
code = pytesseract.image_to_string(img).strip()
print(f"提取的验证码为:【{self.remove(code)}】")
return self.remove(code) def getCode(self):
"""获取4位数验证码""" # 循环前获取code字数
code = self.getCodeImg()
print(f"验证码位数:【{len(code)}】位")
while len(code) != 4:
# 重新获取验证码
code = self.getCodeImg()
print(f"验证码位数:【{len(code)}】位")
if len(code) != 4:
print("验证码不是4位数!")
print(f"输出4位验证码为:{code}")
return code def checkCode(self):
"""判断验证码是否正确""" try:
errorMsg = self.get_text(Loc.errorMsg_loc)
if errorMsg == "验证码错误":
self.inputCodeAction()
except:
print("验证码正确,进入首页!") def inputCodeAction(self):
"""输入验证码登录""" code = self.getCode()
self.el_clear_sendKeys(Loc.code_loc, code)
self.el_click(Loc.loginButton_loc)
self.checkCode()

八、附录:静态图片文字提取  返回目录

1、思路:

  • 首先获取图片的src地址
  • 然后进行GET接口请求,获取图片的二进制数据
  • 再创建一个图片文件,把二进制数据写到图片里
  • 最后读取图片,获取验证码code

2、实践方法

获取图片地址,有两种情况,一种是通过解析页面获取src,一种是通过页面元素获取src

1)获取src

①通过前端页面代码获取src

这种方式比较简单,直接通过前端页面代码获取src

    headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36'
}
# 打开登录页面地址
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
# 获取前端页面代码
page_text = requests.get(url=url, headers=headers).text
# 解析字符串格式的HTML文档对象
tree = etree.HTML(page_text)
# 解析出页面中图片的地址
cod_img_src ='https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]

注:etree.HTML()可以用来解析字符串格式的HTML文档对象,将传进去的字符串转变成_Element对象。作为_Element对象,可以方便的使用getparent()、remove()、xpath()等方法。

②页面元素获取src

当获取不到前端代码时候,可以用Selenium自动化获取元素属性值

    headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36'
}
# 打开浏览器
driver = webdriver.Chrome()
# 浏览器打开登录地址
driver.get("https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx")
# CSS定位元素,获取src属性值
src = driver.find_element_by_css_selector("#imgCode").get_attribute("src")

2)获取图片二进制数据

cod_data = requests.get(url=src,headers=headers).content

打印结果:

b'GIF89a7\x00\x16\x00\xf7\x00\x00\x00\x00\x00\x00\x003\x00\x00f\x00\x00\x99\x00\x00\xcc\x00\x00\xff\x00+\x00\x00+3\x00+f\x00+\x99\x00+\xcc\x00+\xff\x00U\x00\x00U3\x00Uf\x00U\x99\x00U\xcc\x00U\xff\x00\x80\x00\x00\x803\x00\x80f\x00\x80\x99\x00\x80\xcc\x00\x80\xff\x00\xaa\x00\x00\xaa3\x00\xaaf\x00\xaa\x99\x00\xaa\xcc\x00\xaa\xff\x00\xd5\x00\x00\xd53\x00\xd5f\x00\xd5\x99\x00\xd5\xcc\x00\xd5\xff\x00\xff\x00\x00\xff3\x00\xfff\x00\xff\x99\x00\xff\xcc\x00\xff\xff3\x00\x003\x0033\x00f3\x00\x993\x00\xcc3\x00\xff3+\x003+33+f3+\x993+\xcc3+\xff3U\x003U33Uf3U\x993U\xcc3U\xff3\x80\x003\x8033\x80f3\x80\x993\x80\xcc3\x80\xff3\xaa\x003\xaa33\xaaf3\xaa\x993\xaa\xcc3\xaa\xff3\xd5\x003\xd533\xd5f3\xd5\x993\xd5\xcc3\xd5\xff3\xff\x003\xff33\xfff3\xff\x993\xff\xcc3\xff\xfff\x00\x00f\x003f\x00ff\x00\x99f\x00\xccf\x00\xfff+\x00f+3f+ff+\x99f+\xccf+\xfffU\x00fU3fUffU\x99fU\xccfU\xfff\x80\x00f\x803f\x80ff\x80\x99f\x80\xccf\x80\xfff\xaa\x00f\xaa3f\xaaff\xaa\x99f\xaa\xccf\xaa\xfff\xd5\x00f\xd53f\xd5ff\xd5\x99f\xd5\xccf\xd5\xfff\xff\x00f\xff3f\xffff\xff\x99f\xff\xccf\xff\xff\x99\x00\x00\x99\x003\x99\x00f\x99\x00\x99\x99\x00\xcc\x99\x00\xff\x99+\x00\x99+3\x99+f\x99+\x99\x99+\xcc\x99+\xff\x99U\x00\x99U3\x99Uf\x99U\x99\x99U\xcc\x99U\xff\x99\x80\x00\x99\x803\x99\x80f\x99\x80\x99\x99\x80\xcc\x99\x80\xff\x99\xaa\x00\x99\xaa3\x99\xaaf\x99\xaa\x99\x99\xaa\xcc\x99\xaa\xff\x99\xd5\x00\x99\xd53\x99\xd5f\x99\xd5\x99\x99\xd5\xcc\x99\xd5\xff\x99\xff\x00\x99\xff3\x99\xfff\x99\xff\x99\x99\xff\xcc\x99\xff\xff\xcc\x00\x00\xcc\x003\xcc\x00f\xcc\x00\x99\xcc\x00\xcc\xcc\x00\xff\xcc+\x00\xcc+3\xcc+f\xcc+\x99\xcc+\xcc\xcc+\xff\xccU\x00\xccU3\xccUf\xccU\x99\xccU\xcc\xccU\xff\xcc\x80\x00\xcc\x803\xcc\x80f\xcc\x80\x99\xcc\x80\xcc\xcc\x80\xff\xcc\xaa\x00\xcc\xaa3\xcc\xaaf\xcc\xaa\x99\xcc\xaa\xcc\xcc\xaa\xff\xcc\xd5\x00\xcc\xd53\xcc\xd5f\xcc\xd5\x99\xcc\xd5\xcc\xcc\xd5\xff\xcc\xff\x00\xcc\xff3\xcc\xfff\xcc\xff\x99\xcc\xff\xcc\xcc\xff\xff\xff\x00\x00\xff\x003\xff\x00f\xff\x00\x99\xff\x00\xcc\xff\x00\xff\xff+\x00\xff+3\xff+f\xff+\x99\xff+\xcc\xff+\xff\xffU\x00\xffU3\xffUf\xffU\x99\xffU\xcc\xffU\xff\xff\x80\x00\xff\x803\xff\x80f\xff\x80\x99\xff\x80\xcc\xff\x80\xff\xff\xaa\x00\xff\xaa3\xff\xaaf\xff\xaa\x99\xff\xaa\xcc\xff\xaa\xff\xff\xd5\x00\xff\xd53\xff\xd5f\xff\xd5\x99\xff\xd5\xcc\xff\xd5\xff\xff\xff\x00\xff\xff3\xff\xfff\xff\xff\x99\xff\xff\xcc\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\xfc\x00,\x00\x00\x00\x007\x00\x16\x00\x00\x08\xff\x00\x95\x11\x13Hp\xa0\xc1\x82\x08\x0f*L\xc8p\xa1\xc3}\x10#J\xfc$1\x93\xb2}\x99$\xee\xa3\x08\xd1\xa2\xc6\x8f 5\x12\xe3\xf8\xf1\x13\xb1\x8f\x03\xf7)\xcb\x08\xf1dD\x96!cJ\x14\x08\xd2%H\x8b\'W\xb6\x84x\xd1\xa6L\x8c\x175z\xd4H2d\xce\xa3\x1c]\xc2\x04\xb92\xe8G\x9a\x115a)\xb0i\x1f1P\xbe\x06\xb8x`\xb0\xd1\x80\x16-\xe0\x10K6P\x930\x16V\xd0\x02\xe9\xa8\xefO\x951\x9a \xea\xf3S\xa5\xaa\xc4\x91\x11\x93\xed\x1bD\xe0\x0eDw\x04\\\xec\x88\x06\xb1\x17X\xb0,\xe0\xc4%\xc6\xa8\x85\x15\xc7hYh\x1ak\xa5\x8a\x15Md\x93\xfd\xa1BI\'O\x97\x93\x95\xb9 PU\x9f\x0b\x17\x10"\xea\xc3\xd2"\xd4\xbeF-t\xa8\x94\xe4X\x8d\\@V\xba|j[y\x0c\xe1\xb6U\x96\t\x15\xa8\x17b\x16\x17F\x88aA\x1d\x13\xb6m}\x81X\xc4\xb1J\xfc\x0f\x0bJ\xfa\x00U\xd9^:\x15\x15\xbb\x11\xa1F\xff\xc4Bz\xb4\x8b\xc9\xa0\xee\xea\xbb\xd2b\xc6>M\xfa\x1ek\x1a\x96\xfe\xaa-+cx3\xaar%\x1ap\xe1w\xa5\'\x91 \x05\x04\x16\x98\x05-\r\xa3\x8c&\xca\xb8s\xd8)\xfb\xf4\xf2X}\x11\xf1b\x85\x0e\xc9h\xa7\x89[\xf0yW\xd5HGiD\x9e\x0b\x05h2\x9ak;\xed\xa3\xe0$\xed\x11\x93I\x0b\xfdAD\x962\xc2\xe0\xc7[(\xedX\xa6L\x15\x7fLvWq\x11\x1dW\x00a\x8e\xb8\xa0\x03aA1\xb8\x0f=\xec\xcdwEb61\xc6\x02\x19\xc9T\xd6Y!U\x88\xb1YU8\xa9\xe4\xd3>\xe4\xd9\x05\x98\x0bj\xe8\xe5\xa0\x0b\xd1\x103\x0cX\xa7\xacgE\x99\x0cfRe\x15\x9ad\xe2\x16\x1c\xc3\xe4\xe3\x16\x87>e"\xa0\x8a\xca\x1c\x07\x04A\x88\xb4@\x80&\x93\xb1\xe7W;`M\xd6\x0b\x0b\xd2\x11g\xc5\xa4\x98Y)\xd0.\xdb}w\x97x\x10\x91\x07GT\xa7\xfd\xa0\xcc0\xb0\x1d\x16\xd6X\x8c=\x99\xd6\xa4\x97\x8d\xfa\x87\x15v\r\x89\xb3\'\x8ax\xa5$\x11_q\x0cc\xd5>\xbe\xb4\xe0\x02\x84\xfbPr\x98]\xe9)\xf3\xe8\xaa9\xa8\xa6\xdd$\xa0\x0c\x14\xc9\x96\x80~\x16\x93S\xd3\xa2\xeaSA\xa0\xd07P\xb3-\x05\xb5`Dx\xfd\xa4\x11B\n2\x88\x19\x83_\xf2\xa4+D\x02\x1a4S\xba\x1aaF\x0c\x83\xc3\x9c+\xd1\xba\xe2\xde\xab\xd2P\x11)h\x94I\xf9R\x9b\xef>\xdc^\x84\xaf\x97&5\x05"A\xda\x8e\xda\xac@&5K/\xc4c\xd1\xdb\xec\xc2\nV|\xd5U\xe5\x12\x13\x10\x00;'

3)数据写入到图片中,并识别验证码

    # 读取二进制数据写入到图片中
with open('./code.jpg', 'wb') as fp:
fp.write(cod_data)
# 识别图片中的文字
text = pytesseract.image_to_string(Image.open(r'./code.jpg'))

3、总结

但是这种通过接口方式获取的验证码并不是你想要的,因为每请求一次接口,图片就会发生变化,所以只能适用于静态不变的图片比较合适。

参考文章:《Pytesseract的安装与使用

【Python】Selenium自动化测试之动态识别验证码图片方法(附静态图片文字获取)的更多相关文章

  1. Python+Selenium自动化-设置等待三种等待方法

    Python+Selenium自动化-设置等待三种等待方法   如果遇到使用ajax加载的网页,页面元素可能不是同时加载出来的,这个时候,就需要我们通过设置一个等待条件,等待页面元素加载完成,避免出现 ...

  2. python selenium自动化测试之路(1)--分层测试概念、selenium工具介绍

    1.分层自动化测试概念 传统的自动化市场更关注产品UI层的自动化测试,而分层的自动化测试倡导产品开发的不同阶段都需要自动化测试 大多公司与研发团队其实是忽略了单元测试与集成测试阶段的自动化测试工作,所 ...

  3. Python+selenium自动化测试之浏览器基础操作

    **​​前言** 本文主要讲解webdriber框架,Selenium 就像真实用户所做的一样,Selenium 测试可以在 Windows.Linux 和 Macintosh上的 Internet ...

  4. Python&Selenium自动化测试之PO设计模式

    一.摘要 Page Object模式,后面简称PO,他是一种设计思想,在上一章节中,曾经列举了一些在编写自动化测试过程中随着代码量的增加导致的大量代码难以维护.难以扩展.可读性极差等灾难性的事件:那么 ...

  5. python+selenium自动化测试之登录

    selenium_login.py import unittest from selenium import webdriver class LoginTest(unittest.TestCase): ...

  6. python接口自动化(Cookie_绕过验证码登录)

     python接口自动化(Cookie_绕过验证码登录) 有些登录的接口会有验证码,例如:短信验证码,图形验证码等,这种登录的验证码参数可以从后台获取(或者最直接的可查数据库) 获取不到也没关系,可以 ...

  7. python selenium自动化点击页面链接测试

    python selenium自动化点击页面链接测试 需求:现在有一个网站的页面,我希望用python自动化的测试点击这个页面上所有的在本窗口跳转,并且是本站内的链接,前往到链接页面之后在通过后退返回 ...

  8. selenium自动化测试之整合测试报告

    selenium自动化测试之整合测试报告 标签(空格分隔): 整合报告 如下截图我们添加一个文件叫做:latest_report.py文件, import time import os import ...

  9. Python+Selenium爬取动态加载页面(2)

    注: 上一篇<Python+Selenium爬取动态加载页面(1)>讲了基本地如何获取动态页面的数据,这里再讲一个稍微复杂一点的数据获取全国水雨情网.数据的获取过程跟人手动获取过程类似,所 ...

  10. Python+Selenium爬取动态加载页面(1)

    注: 最近有一小任务,需要收集水质和水雨信息,找了两个网站:国家地表水水质自动监测实时数据发布系统和全国水雨情网.由于这两个网站的数据都是动态加载出来的,所以我用了Selenium来完成我的数据获取. ...

随机推荐

  1. 【资料分享】RK3568核心板规格书(4x ARM Cortex-A55(64bit),主频1.8GHz)

    1 核心板简介 创龙科技SOM-TL3568是一款基于瑞芯微RK3568J/RK3568B2处理器设计的四核ARM Cortex-A55全国产工业核心板,每核主频高达1.8GHz/2.0GHz.核心板 ...

  2. CF1864C 题解

    \(x = 2^k\) 是好做的,每次以 \(2^{k-1}\) 为因数即可. 对于其他情况,考虑每次让 \(x\) 减去其二进制下最低位的 \(1\) 直至变成 \(2^k\). 这种策略下显然每个 ...

  3. win10 搭建 npm 环境

    前言 最近,根据CSDN和博客园等文章的帮助下,搭建了一个npm的环境,现在将搭建过程记录下来,留作参考. 搭建过程 下载nodejs,我是使用的zip包安装的,安装包官网地址https://node ...

  4. 如何在 Windows 使用 Podman Desktop 取代 Docker Desktop

    Podman Desktop 是 Docker Desktop 的免费替代品,是本地开发使用的另一个绝佳选择.它提供了类似的功能集,同时保持完全开源,让您避免使用 Docker 产品的许可问题.在本文 ...

  5. Mac Vue-cli脚手架搭建

    安装node环境 官网地址:http://nodejs.cn/download/ 我选择版本:v16.16.0 修改npm镜像地址 # 查看镜像地址 npm config get registry # ...

  6. Mybatis ResultMap复杂对象一对多查询结果映射之collection

    Mybatis复杂对象一对多映射配置ResultMap的collection collection:一对多查询结果映射,比如user有多个订单 表结构 项目结构图 pom.xml <?xml v ...

  7. [rCore学习笔记 02]Ubuntu 22虚拟机安装

    写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 Ubu ...

  8. Linux安装 JDK (CentOS 7)

    Linux安装 JDK 一.Linux安装软件的方式 第一种:二进制发布包安装: 软件已经针对具体平台编译打包发布,只要解压,修改配置即可 第二种: rpm安装 : 软件已经按照redhat的包管理规 ...

  9. 有向图_节点间路径路径--python数据结构

    字典创建有向图,查找图节点之间的路径,最短路径,所有路径 """ 参考文档: https://www.python.org/doc/essays/graphs/ &quo ...

  10. 【JavaScript高级04】作用域和作用域链

    1,作用域 作用域表示的是变量的有效区域,JavaScript中作用域分为全局作用域和函数作用域(在es6之前没有块作用域).其确定时间为编写成功之后就已经确定好了. 作用域的作用是用来隔离变量,不同 ...