Airtest 的使用

news2024/9/21 18:59:00

Airtest 介绍

Airtest Project 是网易游戏推出的一款自动化测试框架,其项目由以下几个部分构成

Airtest :  一个跨平台的,基于图像识别的 UI  自动化测试框架,适用于游戏和 App , 支持 Windows, Android 和 iOS 平台, 基于 Python 实现。

Poco : 一款基于 UI 组件识别的自动化测试框架,目前支持 Unity3D , cocos2dx, Android 原生 App , iOS 原生 App 和 微信小程序, 也可以在其他引擎中自行介入 poco-sdk 使用, 基于 Python 实现

AirtestIDE : 提供一个跨平台的 UI 自动化测试编辑器, 内置了 Airtest 和 Poco 的相关插件功能,能够快速,简单的编写 Airtest 和 Poco 代码

AirLab:  真机自动化云测试平台, 目前提供 Top 100 手机兼容性测试,海外云真机兼容性测试等服务

私有化手机集群技术: 从硬件到软件, 提供在企业内部私有化手机集群解决方案

总之,Airtest 建立了一个比较完善的自动化测试方案,我们利用它实现所见即所爬,个人认为比 Appium 更加简单易用。本节我们先了解一下 AirtestIDE 的基本使用

准备工作

确保安装好 AirtestIDE ,  Airtest Python  库 和 Poco Python 库

只使用 AirtestIDE 实现自动化模拟和数据爬取也是没问题的, 因为它里面已经内置了 Python 模块, Airtest Python 库和 Poco Python 库,并且提供了非常便捷的可视化点选和代码生成等功能,即使使用者没有任何 Python 基础, 也能自动化控制 App 和完成数据爬取

但是对于需要爬取大量数据和控制页面跳转的场景而言, 仅依靠可视化点选和自动生成代码来自动化控制 App , 其实是不灵活的。 进一步讲,如果我们加入一些代码逻辑,例如流程控制,循环控制语句,就可以爬取批量的数据了,这时候需要依赖 Airtest, Poco 以及一些自定义逻辑和第三方库

Airtest 的官方文档: https://airtest.doc.io.netease.com/tutorial/1_quick_start_guide

详细介绍了 Airtest 的安装方式,包括 AirtestIDE , Airtest Python 库和 Poco Python 库,所以这里建议同时安装

下载路径: Airtest Project (netease.com)

下载后解压, 然后打开 AirtestIDE.exe  会弹出一个登录提示,跳过即可

安装完 AirtestIDE 之后, 它还会安装一个 Python  环境, 这个环境中附带安装了 Airtest Python 库和 Poco Python 库, 不过这个被打包在 AirtestIDE 里面的环境,和系统里安装的 Python 环境并不是同一个,所以推荐直接使用 pip 工具将 Airtest Python 库和 Poco Python 库安装到系统环境中

pip install airtest

pip install pocoui

安装完成之后,在 AirtestIDE 中把默认的 Python 环境由 AirtestIDE 附带的 Python  环境更换成系统的 Python  环境。 

选项 ---- 设置 ---- 自定义 Python.exe  选择已有的 python 解释器即可

安装好之后,需要准备一台 Android 真机 或者这模拟器,真机需要通过 USB 线和电脑相连, 确保 adb  能够正常连接到手机

AirtestIDE 体验

我这里使用的是夜神模拟器,在 CMD 输入  adb devices

然后启动 AirtestIDE , 打开菜单中的 文件----新建脚本---  .air Airtest 项目, 新建一个脚本,选择一个路径,将脚本命名为  script.air   之后点击确定

正常情况下,已经链接上设备了,如果没有连接,刷新一下

我们可以点击页面中的屏幕对手机进行控制,如果出现了链接问题,可以参考官网

这时候可以点击 connect ,然后就可以看到手机屏幕了

至此,要确保所有的步骤都成功了,不然后面可能无法继续

我们来观察一下整个 AirtestIDE 页面,分为 左, 中, 右 三个部分, 一下内容为各组件介绍

左侧靠上的部分是 AirtestIDE 辅助窗, 可以通过一些点选操作实现基于图像识别的自动化配置

左侧中间偏上的部分是 Poco 辅助窗, 可以通过一些点选操作实现基于 UI 组件识别的自动化配置

中间靠上的部分是脚本编辑窗, 即代码编辑区域,可以通过 Airtest 辅助窗和 Poco 辅助窗自动生成代码,也可以自己编写代码,这个代码是基于 Python 语言的

中间靠下是 log 查看窗, 即日志区域, 会输出运行,调试的一些日志

右侧是设备窗,内容为手机屏幕,用鼠标点击这个屏幕,真机或模拟器的屏幕也会跟着变化,而且响应速度很快

Airtest 的图像识别与自动化控制

Airtest 可以基于图像识别来自动化控制 App , 本节我们就来体验一下这个功能。例如先点击作恶的 touch 按钮, 意思是点击屏幕的某个位置

这时 AirtestIDE 会提示我们在右侧手机屏幕上截图,这里我们截取的是 大众点评 的 APP 图标,会发现 script.air 脚本中出现了一行代码, 代码内容为 touch 方法, 其参数是我们刚截取的图片

然后点击右侧的手机屏幕上的 “大众点评” 图标, 进入这个 App  , 再点击左侧的 wait 按钮, 意思是等待指令内容加载出来,之后同样根据提示截图, 如截取首页左上角的美食图标

再点击左侧的 swipe 按钮, 意思是滑动屏幕

这时 AirtestIDE 会提示我们框选一个位置, 联想自己平时滑动屏幕的场景,手指一开始先放在一个位置,然后滑动,到某个位置停止。那么这时的第一步需要框选的位置就是手指一开始需要放置的位置,如 1  的地方 ---中间菜单栏 (菜单栏下方加载的内容会变化, 故选择相比之下更加通用不变的菜单栏作为识别目标) 框选完毕后, AirtestIDE 会提示我们点选一个滑动目标位置,这时选择点选上方的一个点即可, 如果中标 2 的地方,此时会发现 scrpt.air 脚本生成了一个 swipe 方法, 其第一个参数是我们框选的菜单栏图片, 第二个参数是一个 vector, 代表滑动的方向。

这样我们就通过一些可视化完成了自动化控制

最后我们再通过左侧的 keyevent 按钮添加两个键盘事件, 在已经生成的代码开头和结尾分别加一个 HOME 键盘事件, 代表进入首页和返回首页

 先让鼠标停留在需要添加事件的位置,然后点击 keyevent, 输入 HOME, 开头和结尾都是一样

现在总结一下我们实现自动化控制的流程

1. 进入手机首页

2. 点击 “大众点评”  App的图标

4. 等待左上角的 “美食” 图标加载出来

5. 向上滑动手机屏幕

6. 返回手机主页

接下来点击 script.air 脚本上方的运行按钮 (三角按钮), 会发现 AirtestIDE 可以驱动手机完成指定操作了,和我们期望的一样,点击,等待, 滑动操作顺序执行,切 log 查看窗会显示执行的具体过程

以上便是 Airtest 提供的基于图像识别来自动化控制 App 的过程,利用这项技术,我们不用编写任何代码就可以让手机自动操作

其实 script.air 脚本内部对应的就是 Python 代码, 只不过利用 AirtetIDE 封装了一层, 使得编写和操作更加简单了。 我们可以追踪一下源码, 在当前脚本的选项卡右击,在弹出的菜单选项中选择“打开当前项目目录” 就会看到源码内容

可以看到其中有一个 Python 脚本, 和 3 张刚才截取的图片,打开 Python 脚本

可以看到其内容和 AirtestIDE 中自动生成的代码基本一致,不同之处在于这里用了一个  Template 对象代替了图片, 该对象包含图片名,位置,分辨率三个参数,而 AirtestIDE 对图片进行了可视化, 使其更加直观

我们可以更具Python 环境运行这个脚本么?可以, 但是需要在代码开始的 auto_setup(__file__) 和 keyevent("HOME") 之间添加一行代码 init_device() , 调用 init_device 方法的作用完成一些手机初始化配置, 不做这一步可能会报错。 运行脚本后会产生同样的效果, 手机会被自动化控制执行一系列的操作, 同时控制台输出对应的操作日志

Airtest 的相关 API

上面的内容只是 Airtest Python 库的冰山一角,本节列举一些它提供的便捷 API 。 从刚才的 init_device 方法说起,这个方法是用来连接设备并初始化一些连接对象的。如果设置没有初始化则会先初始化设置,并把初始化后的设备当作当前设备

def init-device(platform="Android", uuid=None, **Kwargs)

用法示例:

device: Android = init_device('Android')

print(device)

<airtest.core.android.android.Android object at 0x00000234FE2EC510>

可以发现返回结果是一个 Android 对象, 这个 Android 对象实际上属于 airtest.core.android 包, 继承自 airtest.core.device.Device 类, 与之并列的对象还有 airtest.core.ios.ios.IOS    airtest.core.Linux.linux.Linux    airtest.core.win.win.Windows 等。这些对象都有一些用来操作设备的 API , 下面我们以 Android 对象的 API  为例总结一下

get_default_device :  获取默认设备

uuid :  获取当前设备 UUID

list_app :  列举设备上的所有 App

path_app : 打印出某个 App的完整路径

check_app: 检查某个APP 是否在当前设备上

start_app : 启动某个 APP

stop_app : 停止某个 APP

start_app_timing : 启动某个 APP 并计算启动时间

clear_app : 清空某个 APP 的全部数据

install_app : 安装某个 APP

install_multiple_app : 安装多个 APP

uninstall_app  : 卸载某个 APP

snapshot : 获取屏幕截图

shell : 获取 adb shell 命令执行结果

keyevent : 执行键盘操作

wake : 唤醒当前设备

home : 点击 HOME 键

text ; 向设备输入内容

touch : 点击屏幕上的某处

double_click :双击屏幕某处

swipe : 滑动屏幕, 由一点滑动至另一点

pinch ; 通过手指的捏合操作放大或缩小屏幕

logcat : 记录日志

getprop : 获取某个特定属性的值

get_ip_address : 获取 IP 地址

get_top_activity :获取当前 Activity 

get_top_activity_name_and_pid : 获取当前 Activity 的名称和进程号

get_top_activity_name :  获取当前 Ativity 的名称

is_keyboard_shown : 判断当前是否显示键盘了

is_locked : 判断设备是否锁定了

unlock : 解锁设备

get_display_info :  获取当前显示信息, 如屏幕宽高等

get_current_resolution ; 获取当前设备的分辨率

get_render_resolution : 获取当前渲染的分辨率

start_recording : 开始录制

stop_recording : 结束录制

adjust_all_screen :  调整屏幕的适配分辨率

下面做一些实例感受

from airtest.core.api import *
from airtest.core.android import Android
import logging

logging.getLogger("airtest").setLevel(logging.WARNING)

#  初始化设备
device: Android = init_device('Android')
# 是否上锁
is_locked = device.is_locked()
print(f'is_locked {is_locked}')

# 如果上锁, 就解锁
if is_locked:
    device.unlock()
# 唤醒设备
device.wake()
# 列举设备上的 APP
app_list = device.list_app()
print(f'app_list  {app_list}')
# 获取当前设备 UUID
uuid = device.uuid
print(f'uuid {uuid}')
# 获取当前显示信息, 如屏幕宽高等
display_info = device.get_display_info()
print(f'display_info {display_info}')
# 获取当前渲染的分辨率
resolution = device.get_render_resolution()
print(f'resolution {resolution}')
# 获取 IP 地址
ip_address = device.get_ip_address()
print(f'ip_address {ip_address}')
# :获取当前 Activity
top_activity = device.get_top_activity()
print(f'top_activity {top_activity}')
# 判断当前是否显示键盘了
is_keyboard_shown = device.is_keyboard_shown()
print(f'is_keyboard_shown {is_keyboard_shown}')

is_locked False
app_list  ['com.android.cts.priv.ctsshim', 'com.android.providers.telephony' ]
uuid 127.0.0.1:62001
display_info {'width': 720, 'height': 1280, 'density': 1.5, 'orientation': 0, 'rotation': 0, 'max_x': 720, 'max_y': 1280}
resolution (0.0, 0.0, 720.0, 1280.0)
ip_address 172.16.38.15
top_activity ('com.android.launcher3', '.launcher3.Launcher', '2509')
is_keyboard_shown False

从结果可以看出, 借助一些常用的 API , 我们就完成了唤醒手机和获取 APP 列表, UUID ,显示器信息, 分辨率, IP地址, 当前运行的 Activity , 是否显示键盘等操作

获取当前设备

Airtest 中有一个全局变量 G, 它的 DEVICE 属性代表当前的设备对象。这直接调用 device 方法即可获取当前设备,该方法定义如下

def device():

        return G.DEVICE

获取所有设备

print(G.DEVICE_LIST)
uri = 'Android://127.0.0.1:5037/127.0.0.1:62001'
device: Android = connect_device(uri)
print(G.DEVICE_LIST)

[]

[<airtest.core.android.android.Android object at 0x0000028A8B6CE250>]

uri 获取

DEVICE_LIST 是一个列表,元素是 Airtest , 当前已经连接的设备,需要注意的是,在没有调用 connect_device 方法的时候, DEVICE_LIST 是空的, 调用 connect_device 方法后,DEVICE_LIST 会自动添加已经连接的设备

执行命令

可以调用 shell 方法,传入 cmd 参数来执行命令行,直接调用 adb 命令就可以了

@logwrap
def shell(cmd):
    return G.DEVICE.shell(cmd)
uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
result = shell('cat /proc/meminfo')
print(result)

MemTotal:        3566528 kB
MemFree:         3061388 kB
MemAvailable:    3285952 kB
Buffers:             420 kB
Cached:           280936 kB
SwapCached:            0 kB
Active:           259808 kB
Inactive:         210492 kB

这样就获取了设备的内存信息

启动和停止

调用设备的 start_app 和 stop_app 方法,然后传入 App 的包名,即可启动和停止这个 App

@logwrap
def start_app(package, activity=None):
    G.DEVICE.start_app(package, activity)

@logwrap
def stop_app(package):
    G.DEVICE.stop_app(package)

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
package = 'com.goldze.mvvmhabit'
start_app(package)
sleep(10)
stop_app(package)

这里指定了 package 为 app5的包名,然后调用 start_app 方法启动了 app5, 等待 10 秒后, 调用 stop_app 方法停止了 app5  的运行

安装和卸载 app

调用设备的 install 和 uninstall 方法, 前者传入 APP 的保存路径,后者传入包名, 即可安装和卸载 app 

# 安装 app

@logwrap

def install(filepath, **kwargs):

        return G.DEVICE.install_app(filepath, **kwargs)

# 卸载 app

@logwrap

def uninstall(package):

        return G.DEVICE.uninstall_app(package)

截图

利用 snapshot 方法获取屏幕截图, 可以通过参数设置存储截图的文件名称和图片的质量等 

def snapshot(filename=None, msg=" ", quality= ST.SNAPSHOT_QUALITY)

用法示例

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
package = 'com.goldze.mvvmhabit'
G.DEVICE.start_app(package)
sleep(3)
snapshot('app5.png', quality=30)
G.DEVICE.stop_app(package)

唤醒和回到首页

调用设备的wake 和 home 方法,即可唤醒 APP 和回到首页,两个方法的定义如下

# 唤醒

@logwrap

def wake():

        G.DEVICE.wake()

# 回到首页

@logwrap

def home():

        G.DEVICE.home()

这两个方法不需要参数,直接调用即可

点击屏幕

调用 touch 方法点击屏幕, 可传入要点击的图片或绝对位置,还可以指定点击次数,声明如下

@logwrap

def touch(v, times=1, **kwargs)

例如,我们从手机上截一张需要点击的 app 的图片,然后把这张图片声明成一个 Template 对象传入 touch 方法

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
touch(Template('app.png'))

运行这段代码后,设备就会启动,然后点击这张图片

我们也可以传入绝对位置,具体位置以自己设备为准,需要测量

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
home()
touch((70, 645))

另外,touch 完全等同于 click 方法,如果需要双击,可以调用 double_click 方法,等同于 touch 的 times = 2 ,  click 方法的参数和 touch 的参数是一样的

滑动

调用 swipe 方法滑动屏幕, 可以传入起始位置和结束位置,两个位置都可以是图片或者绝对位置

声明如下

@logwrap

def swipe(v1, v2=None, vector=None, **kwargs)

例如我们想要控制手机向右滑动,可以实现如下代码

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
home()
swipe((200, 300), (900, 300))

放大缩小

放大缩小是调用 pinch 方法, 可以通过 in_or_out 参数指定放大还是缩小,还可以指定手指捏合的中心点位置和放大缩小的比例 ,该方法的声明如下

@logwrap

def pinch(in_or_out='in', center=None, percent=0.5)

用法如下

uri = 'android://127.0.0.1:5037/127.0.0.1:62001'
connect_device(uri)
home()
pinch(in_or_out='out', center=(300, 300), percent=0.4)

这里我们调用了  pinch 方法, 并且制定了放大动作 out , 同时指定了捏合的中心点和捏合比例

键盘事件

调用 keyevent 方法来按下某个键, 例如 HOME 键,返回键等。声明如下

def keyevent(keyname, **kwargs)

用法示例

keyevent('HOME')

表示按下HOME 键

输入内容

调用 text 方法来输入内容,前提是目标 Widget 需要处于 active 状态。声明如下

@logwrap

def text(text, enter=True, **kwargs)

调用该方法后,目标 Widget 就会输入相应的字符,输入完之后会执行一次确认(按回车键)

基于 Poco 的 UI 组件自动化

在某些场景下,基于图像的识别来自自动化控制 App 是比较方便的,但也存在一定的局限性。例如图像识别速度可能不快,以及 App 中的某些 UI 如果更换了,就无法和之前截图的图片匹配成功,这些很可能影响自动化测试流程

所以,这里再介绍一些基于 Poco 的 UI 组件自动化控制,说白了就是基于 UI 名称和属性选择器的自动化控制,有点类似于 Appium, Selenium 中的 XPath

新建一个脚本,命名为 script2.air , 右侧同样连接好手机,然后点击左侧 Poco 辅助窗, 选择 Android ,这时会提示我们更新代码,点击确定后脚本中自动添加了代码

from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

意思是导入了 Poco 包的 AndroidUiautomationPoco 模块,然后声明了一个 poco 对象。接下来就可以通过 poco 对象选择一些内容了。 例如点击左侧 UI 组件树中的 “大众点评“ 节点就会发现手机右侧屏幕上对应的 app 高亮显示了,在 Log 查看窗 还可以看到该节点对应的所有属性。这个操作有点像在浏览器开发者工具中选取网页源代码, 其中的 UI 组件树就相当于网页里的 HTML DOM 树

直接双击 “大众点评”  节点, script2.air 脚本就会出现对应的代码

poco("大众点评")

我们来看一下 poco 的 API ,这是一个 AndroidUiautomationPoco 对象

官方文档: https://poco.readthedocs.io/zh_CN/latest/source/poco.pocofw.html

其用法类似如下

poco = AndroidUiautomationPoco(...)

close_bin = poco('close', type='Button')

会发现 Poco 本身就是一个对象,但可以直接调用 UI 组件的名称, 这归根结底是因为实现了一个 __call__ 方法:

def __call__(self, name=None, **kw):

        if not name and len(kw) == 0:

                warnings.warn("Wildcard selector may cause performance trouble, Please give at least one condition to shrink range of results")

        return UIObjiectProxy(self, name, **kw)

可以看到 __call__ 方法第一个参数是 name  , 其他参数都以 kw  的形式传入,可以任意指定, 最后返回一个 UIObjectProxy 对象

回过头来, 我们看看 “大众点评”  这个节点的 name 参数值是什么, 这个在 Log查看窗 内显示的很清楚

可以看到其 name 就是 “大众点评” 而且整个 UI 树没有与其同名的节点, 于是可以直接调用 poco('大众点评')  选取这个节点,当然也可以任意指定 poco 的其他参数

poco("大众点评", type='android.widget.TextView')

poco("大众点评", text='大众点评')

poco("大众点评", text='大众点评', desc='大众点评')

这三种方法都能选取同样的节点

刚才说到 __call__  会返回一个 UIObjectProxy 对象, 现在我们来看一下这个对象的实现,其 API 链接为: https://poco.readthedocs.io/zh_CN/latest/source/poco.pocofw.html 从中可以看出它实现了 __getitem__ , __iter__, __len__ , child,   children ,  offspring 等方法,所以可以实现链式调用,索引操作和循环遍历

其中一些比较常用的方法如下

child :  选择子节点。 第一个参数是 name ,  即 UI 组件的名称, 如 android.widget.LinearLayout 等, 还可以传入一些属性辅助选择, 其返回结果也是 UIObjectProxy 对象

perent: 选择父节点。 该方法无需传入参数, 可以直接返回当前节点的父节点,返回同样是 UIObjectProxy 对象

sibling:  选择兄弟节点。 第一个参数是 name , 即 UI 组件的名称, 同样可以额外传入一些属性辅助选择,返回结果依然是 UIObjectProxy 对象

click, rclick ,  double_click, long_click : 分别是点击, 右击,双击, 长按。 UIObjectProxy 对象可以直接调用这几个方法, 参数 focus 用于指定点击的偏移量, sleep_interval 用于指定点击完成后的等待时间 (单位为 秒)

swipe :滑动操作。参数 direction 用于指导滑动方向, focus 用于指导滑动焦点的偏移量, duration 用于指导完成滑动所需的时间

wait , wait_for_appearance ; 等待某节点的出现。 参数 timeout 用于指定最长等待时间

attr : 获取节点的属性值, 参数 name 用于指定要获取的属性名, 如 visable ,  text , type, pos, size 等

get_text: 获取节点的文本值。 这个方法非常有用, 可以获取某个文本节点内部的文本数据。

下面调用 click 方法, 将代码改为

poco("大众点评").click()

这样就可以选中并点击  “大众点评”  节点, 点击之后,就进入 “大众点评” 这个 App ,然后可以设置一下等待条件, 等待某个节点加载出来, 证明已经进入 App 了 ,然后点击 左侧的 Poco Pause 按钮, 可以在右侧屏幕上点击想要查看的位置,左侧 UI 组件树就会自动定位到对应的节点, 同时 Log查看窗 会实时显示节点信息

双击左侧 UI 组件树中定位到的节点, script2.air 中又会增加如下内容

poco("com.dianping.v1:id/home_category_layout")

然后可以在后面加上等待时间

poco("com.dianping.v1:id/home_category_layout").wait_for_appearance(10)

代表等待 10 秒,如果加载不出来就报错

同样可以选中中间菜单栏的位置向上滑动

poco(desc="美食").swipe([0, -0.1])

这里往 swipe 方法的参数传入一个列表, 代表滑动方向, 列表第一个元素代表横向偏移量,第二个元素代表纵向偏移量, 由于我们要向上滑动, 因此第一个元素是 0 , 第二个元素是  -0.1

最后在代码开头和结尾添加键盘事件,回到首页,整理代码如下

# -*- encoding=utf8 -*-
__author__ = "86151"

from airtest.core.api import *
auto_setup(__file__)
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
keyevent('HOME')
poco("大众点评").click()
poco("com.dianping.v1:id/home_category_layout").wait_for_appearance(25)
poco(desc="美食").swipe([0, -0.1])
keyevent('HOME')

运行这段代码之后,手机上就会先进入桌面, 然后点击 “大众点评”  图标进入 APP , 等待相应内容加载出来之后, 向上滑动, 最后返回桌面

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

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

相关文章

蛋白质基础

氨基酸:必需氨基酸 条件必需氨基酸和非必需氨基酸 必需氨基酸:机体需要但自身不能合成&#xff0c;必须从食物中获取的氨基酸。共有八种&#xff0c;对婴儿&#xff0c;组氨酸也是必需氨基酸。 条件必需氨基酸:半胱氨酸和酪氨酸在体内分别由蛋氨酸和苯丙氨酸转变而来。若膳食中…

HTML 列表和容器元素——WEB开发系列10

HTML 提供了多种方式来组织和展示内容&#xff0c;其中包括无序列表、有序列表、分区元素 ​​<div>​​ 和内联元素 ​​<span>​​、以及如何使用 ​​<div>​​​ 进行布局和表格布局。 一、HTML 列表 1. 无序列表 (​​<ul>​​) 无序列表用于展…

Java流程控制06:for循环详解

教学视频链接&#xff1a;https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 Java中的‌for循环结构‌是一种基础…

Python与DIAdem联合开发

Python可以通过COM接口与NI的DIAdem软件集成&#xff0c;允许用户以编程方式自动生成和定制报告。这种方式使得报告生成更加灵活且可定制&#xff0c;尤其适用于需要定期生成大量报告或对报告内容有特定要求的场景。 1. 工作原理 Python与DIAdem的集成主要依赖于COM&#xff0…

二叉树的判断

二叉树的判断 判断一颗二叉树是不是搜索二叉树 &#xff08;左边的比根小&#xff0c;右边的比根大&#xff09; 中序遍历一下&#xff0c;如果是的话就一定是升序的 如何判断一颗二叉树是否是完全二叉树 1.遍历任意的节点时候&#xff0c;如果返回右孩子没有左孩子&#x…

【Linux学习】权限

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a;Linux学习 目录 &#x1f308;前言&#x1f525;Linux权限的概念&#x1f525;Linux权限管理01.文件访问者的分类02.文件类型和访问权限&#xff08;文件属性&#xff09;03.文件权限值得…

线程同步-条件变量

文章目录 引言条件变量初始化条件变量&#xff1a;pthread_cond_init销毁条件变量&#xff1a;pthread_cond_destroy条件等待&#xff1a;pthread_cond_wait唤醒等待&#xff1a;pthread_cond_signal、pthread_cond_broadcast认识条件变量接口使用 引言 有一个非常好的VIP自习…

树莓派3B运行rasa init和rasa shell遇到的tensorflow报错总结

终于在我的树莓派上安装rasa-1.4.0版本成功&#xff08;见《树莓派智能语音助手之聊天机器人-RASA》&#xff09;。不过&#xff0c;在初始化rasa的时候还是遇到了很多报错&#xff0c;在此总结&#xff0c;供朋友们参考。 1. ModuleNotFoundError: No module named ‘tensorf…

合肥网站制作服务

合肥网站制作服务&#xff1a;为您的企业搭建数字化桥梁 随着互联网的迅猛发展&#xff0c;越来越多的企业意识到拥有一个专业的网站对于提升品牌形象和扩大市场影响力的重要性。合肥作为安徽省的省会&#xff0c;经济发展迅速&#xff0c;许多企业渴望通过优质的网站制作服务&…

Windows采用VS2019实现Open3D的C++应用

1、参考链接 https://blog.csdn.net/qq_31254435/article/details/137799739 但是&#xff0c;我的方法和上述链接不大一样&#xff0c;我是采用VS2019进行编译的&#xff0c;方便在Windows平台上验证各种算法。 2、创建一个VS2019的C Console工程 #include <iostream>…

Unity 6 预览版正式发布

Unity 6 预览版发布啦&#xff0c;正式版本将于今年晚些时候正式发布&#xff01; 下载链接&#xff1a; https://unity.com/releases/editor/whats-new/6000.0.0 Unity 6 预览版是 Unity 6 开发周期的最后一个版本&#xff0c;在去年 11 月 Unite 大会上&#xff0c;我们宣…

应急响应-DDOS-典型案例

某单位遭受DDoS攻击事件如下 事件背景 2019年2月17日&#xff0c;某机构门户网站无法访问&#xff0c;网络运维人员称疑似遭受DDoS攻击&#xff0c;请求应急响应工程师协助。 事件处置 应急响应工程师在达到现场后&#xff0c;通过查看流量设备&#xff0c;发现攻击者使用僵…

日拱一卒(4)——leetcode学习记录:路经总和

一、任务&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶…

RCE与SQL漏洞的复现

eval长度限制突破 php eval函数参数限制在16个字符的情况下&#xff0c;如何拿到webshell呢 首先&#xff0c;我们还是先把环境搭好&#xff08;此次的所有漏洞环境我都部署在Ubuntu下,我的Ubuntu下的环境是nginxphp&#xff0c;至于Ubuntu下的nginxphp环境我也有博客也写到&a…

配置maven环境(全局)

全局设置 修改Maven 更改JRE 更改Java Compiler 修改完成后&#xff0c;Apply即可

一些有趣的XSS注入GAME

目录 1.ma,Spaghet 2.Jefff 3.Ugandan Knuckles 4.Ricardo Milos 5. Ligma 6.Ah Thats Hawt 7.Msfia 8.Ok,Boomer 1.ma,Spaghet innerHTML :设置 innerHTML 的值可以让你轻松地将当前元素的内容替换为新的内容。 HTML 5 中指定不执行由 innerHTML 插入的 <script&g…

【win/mac】矢量图形编辑软件Adobe Illustrator(AI) 2024版本下载与安装

一、Adobe AI 软件简介 什么是 Adobe AI 软件Adobe Illustrator&#xff08;简称 AI&#xff09;是一款功能强大的矢量图形编辑软件&#xff0c;广泛应用于平面设计、插画绘制、图标设计、包装设计等领域。它允许用户创建、编辑和处理各种复杂的矢量图形&#xff0c;具有极高的…

Metasploit 入门教程(非常详细)从零基础入门到精通,看完这一篇就够了!

一、引言 Metasploit 是一款强大的开源渗透测试工具框架&#xff0c;被广泛应用于网络安全领域。无论是安全研究人员、渗透测试工程师&#xff0c;还是对网络安全感兴趣的初学者&#xff0c;掌握 Metasploit 都是提升技能的重要一步。本教程将带您从零基础开始&#xff0c;逐步…

Linux软件编程学习第十三天

网络&#xff1a;1.协议&#xff1a;通信双方约定的一套标准 2.国际网络通信协议标准&#xff1a; 1.OSI协议&#xff1a; 应用层 发送的数据内容 表示层 数据是否加密 会话层 是否建立会话连接 传输层 …

HarmonyOS NEXT星河版零基础入门(3)

1. 系统弹出框 import { promptAction } from kit.ArkUI;Component struct weChat {State text:string build(){Column(){Text(this.text )Search({value:$$this.text})Button(改值).onClick(()>{promptAction.showToast({message:this.text,duration:10000})})}} } messa…