1、websocket

	1.websocket 与轮询
轮询:
不断向服务器发起询问,服务器还不断的回复
浪费带宽,浪费前后端资源
保证数据的实时性 长轮询:
1.客户端向服务器发起消息,服务端轮询,放在另外一个地方,客户端去另外一个地方去拿
2.服务端轮询,放在另外一个地方,直接推给客户端 释放客户端资源,服务压力不可避免,节省带宽资源
数据不能实时性 websocket:是一个新的协议 Socket-io
1.前后端hold住
2.建立长链接 彻底解决实时性
解决占用带宽的问题
解决资源 2.webscoket使用
from flask import Flask, request
from geventwebsocket.websocket import WebSocket
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler app = Flask(__name__) @app.route("/ws") # WS://127.0.0.1:9527/ws
def ws():
# request.environ["wsgi.websocket"] = <链接>
user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
while 1:
msg = user_socket.receive()
print(msg)
user_socket.send(msg) if __name__ == '__main__':
http_serv = WSGIServer(("0.0.0.0", 9527), app, handler_class=WebSocketHandler)
http_serv.serve_forever()
# app.run("0.0.0.0", 9527, debug=True) 即时通讯(IM):
群聊:
from flask import Flask, request,render_template
from geventwebsocket.websocket import WebSocket
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
import json app = Flask(__name__) user_socket_dict = {} # {jinwangba:<geventwebsocket.websocket.WebSocket object at 0x000000000B35CE18>} @app.route("/")
def index():
return render_template("index.html") @app.route("/ws/<nickname>") # WS://127.0.0.1:9527/ws
def ws(nickname):
user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
if user_socket:
user_socket_dict[nickname]=user_socket
print(len(user_socket_dict))
else:
return render_template("index.html",message="请使用Websocket链接")
while 1:
msg = user_socket.receive()
for user_nick_name,socket in user_socket_dict.items(): # type:WebSocket
if user_socket != socket:
try:
socket.send(json.dumps({"sender":nickname,"msg":msg}))
except:
continue if __name__ == '__main__':
http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
http_serv.serve_forever()
# app.run("0.0.0.0", 9527, debug=True) 单聊:
from flask import Flask, request,render_template
from geventwebsocket.websocket import WebSocket
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
import json app = Flask(__name__) user_socket_dict = {} # {jinwangba:<geventwebsocket.websocket.WebSocket object at 0x000000000B35CE18>} @app.route("/")
def index():
return render_template("index.html") @app.route("/ws/<nickname>") # WS://127.0.0.1:9527/ws
def ws(nickname):
user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
if user_socket:
user_socket_dict[nickname]=user_socket
print(len(user_socket_dict),user_socket_dict)
else:
return render_template("index.html",message="请使用Websocket链接")
while 1:
msg = user_socket.receive()
msg_dict = json.loads(msg)
#{"to_user":jinwangba,msg:"DSB"}
to_user = msg_dict.get("to_user")
print("to_user:",to_user)
to_user_socket = user_socket_dict.get(to_user)
send_str = json.dumps({"sender":nickname,"msg":msg_dict.get("msg")})
to_user_socket.send(send_str) if __name__ == '__main__':
http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
http_serv.serve_forever()
# app.run("0.0.0.0", 9527, debug=True)

  

2、websocket原理
  

import socket, base64, hashlib

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9527))
sock.listen(5)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
print(data) def get_headers(data):
header_dict = {}
header_str = data.decode("utf8")
for i in header_str.split("\r\n"):
if str(i).startswith("Sec-WebSocket-Key"):
header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip() return header_dict ws_key = get_headers(data).get("Sec-WebSocket-Key") # # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
socket_str = ws_key + magic_string
socket_str_sha1 = hashlib.sha1(socket_str.encode("utf8")).digest()
socket_str_base64 = base64.b64encode(socket_str_sha1) response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n" %(socket_str_base64.decode("utf8")) conn.send(response_tpl.encode("utf8"))
while 1:
msg = conn.recv(8096)
print(msg) """
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n
Accept-Encoding: gzip, deflate\r\n
Sec-WebSocket-Version: 13\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Extensions: permessage-deflate\r\n
Sec-WebSocket-Key: x/BjPHeWzOqqLxVuZq/bfw==\r\n
Cookie: session=fe2f4896-0309-4801-b8cd-9a719b26fb8d\r\n
Connection: keep-alive, Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n\r\n'
""" # # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
# magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
#
#
# def get_headers(data):
# header_dict = {}
# header_str = data.decode("utf8")
# for i in header_str.split("\r\n"):
# if str(i).startswith("Sec-WebSocket-Key"):
# header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()
#
# return header_dict
#
#
# def get_header(data):
# """
# 将请求头格式化成字典
# :param data:
# :return:
# """
# header_dict = {}
# data = str(data, encoding='utf-8')
#
# header, body = data.split('\r\n\r\n', 1)
# header_list = header.split('\r\n')
# for i in range(0, len(header_list)):
# if i == 0:
# if len(header_list[i].split(' ')) == 3:
# header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
# else:
# k, v = header_list[i].split(':', 1)
# header_dict[k] = v.strip()
# return header_dict
#
#
# headers = get_headers(data) # 提取请求头信息
# # 对请求头中的sec-websocket-key进行加密
# response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
# "Upgrade:websocket\r\n" \
# "Connection: Upgrade\r\n" \
# "Sec-WebSocket-Accept: %s\r\n" \
# "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n" # value = headers['Sec-WebSocket-Key'] + magic_string
# print(value)
# ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# response_str = response_tpl % (ac.decode('utf-8'))
# # 响应【握手】信息
# conn.send(response_str.encode("utf8"))
#
# while True:
# msg = conn.recv(8096)
# print(msg)

websocket加解密

3、websocket加解密
  

上节回顾:
websocket;握手
浏览器 - 链接 - 服务器
浏览器 - 发送 - 字符串(求情头) - Sec-Websocket-Key - 服务器 服务器 - 获取Sec-Websocket-Key的值+magic_string - sha1 - base64 - 拼接一个响应头 - Sec-WebSocket-Accept值就是
服务器 - 拼接好的响应头 - 浏览器
浏览器 - Sec-WebSocket-Accept 解密得到 Sec-Websocket-Key - 握手成功与否 今日内容:
websocket 加解密
解密:
b_str = b'\x81\xfe\x01G\xa0`~dE\xe5\xf6\x81\x18\xfd\x9b\xec;\x84\xc6\xfeF\xfc\xd4\x81-\xea\x96\xe4,\x84\xc6\xc9I\xe1\xed\x81\x14\xc9\x98\xca"\x8f\xc2\xe8D\xdb\xf4\x81\x04\xc9\x9a\xdc+\x84\xc6\xedE\xe8\xf8\x8b\x1c\xec\x99\xff*\x85\xc9\xfaG\xf6\xcc\x81\x1c\xea\x91\xd8,\x86\xd3\xc0H\xcf\xe4\x81-\xd1\x98\xe4\x05\x85\xd3\xfcD\xda\xdf\x80\x19\xeb\x99\xc3+\x84\xc7\xfbC\xe0\xfc\x83$\xd6\x9a\xda-\x85\xf3\xcfD\xd9\xf5\x8c\'\xc3\x9a\xdc-\x86\xf9\xecD\xda\xf0\x81&\xe5\x91\xd8,\x85\xc1\xc4E\xdf\xe9\x80\x19\xeb\x9b\xc7\x0b\x85\xc1\xfcH\xda\xd5\x80\x1a\xee\x9b\xc06\x88\xfe\xe1O\xdc\xf2\x83;\xf6\x96\xdb\x1d\x85\xfb\xecE\xd8\xe3\x80\x19\xeb\x98\xca*\x89\xff\xe3O\xdc\xf2\x82\x0c\xd2\x98\xee\x05\x84\xc7\xefD\xda\xf0\x8d9\xfb\x9a\xdc+\x84\xc7\xfbC\xe0\xfc\x8c\x0f\xfa\x9b\xca<\x85\xc2\xe4E\xdc\xde\x81<\xc3\x9b\xf4\x0c\x8f\xc2\xe8D\xdb\xdb\x81%\xe9\x9b\xe1(\x85\xc6\xf9I\xe1\xe9\x81\x1e\xd7\x91\xd8,\x86\xff\xc6E\xdc\xe6\x81\x1f\xf7\x9b\xc7\x0b\x84\xc7\xefF\xd0\xea\x8b\x1c\xec\x9a\xdc-\x85\xd0\xf8E\xc6\xfa\x8c\'\xca\x96\xeb\x12\x88\xe8\xe0O\xdc\xf2\x81\x1c\xf5\x9b\xf2\x1b\x85\xda\xd5D\xd9\xf7\x8b\x1c\xec\x9a\xdf\x05\x85\xdf\xfaE\xdf\xde\x8c\x10\xef\x9a\xdd+\x88\xc9\xcbD\xd9\xe1'
c_str = b'\x81\xfe\x02d\xbe@<\x07'
# 123 \x81
#[\x81,\x83,\xc8,\xbd,\xa6,\x9c,\xf9,\x8f,\x95]
#
#
#
#--------
#
# 0111111 127
# 0111110 126 data_lenth = b_str[1] & 127
# print(data_lenth) # data_lenth == 127 之后的多少位为数据长度3-10字节代表数据长度 11-14为mask_key
if data_lenth == 127:
extend_payload_len = b_str[2:10]
mask = b_str[10:14]
data = b_str[14:] # data_lenth == 126 之后的多少位为数据长度3-4字节代表数据长度 65535 5-8为mask_key
if data_lenth == 126:
extend_payload_len = b_str[2:4]
mask = b_str[4:8]
data = b_str[8:] # data_lenth <= 125 当前的 data_lenth 就是数据的长度 125 3-6为mask_key
if data_lenth <= 125:
extend_payload_len = b_str[1]
mask = b_str[2:6]
data = b_str[6:] str_byte = bytearray() for i in range(len(data)):
byte = data[i] ^ mask[i % 4] #
str_byte.append(byte) print(str_byte.decode("utf8"))# 加密:
import struct
msg_bytes = "先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也".encode("utf8")
token = b"\x81"
length = len(msg_bytes) if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length) msg = token + msg_bytes print(msg)

websocket握手和加解密

4、人工智能简易机器人
  

1.百度ai
2.基于百度ai实现了语音合成
3.基于百度ai实现了语音识别

from aip import AipSpeech
import os
from uuid import uuid4
from wav2pcm import wav_to_pcm APP_ID = ''
API_KEY = 'zdU6ldOh7CeQvFFzw9GWhwZr'
SECRET_KEY = 'h5eVzR2G4EPFf78uzceC1zHL6Qj88RCY' client = AipSpeech(APP_ID, API_KEY, SECRET_KEY) # client.setConnectionTimeoutInMillis(5000) # 建立连接的超时时间(单位:毫秒)
# client.setSocketTimeoutInMillis(10000) # 通过打开的连接传输数据的超时时间(单位:毫秒)
# 语音合成
def new_synthesis(text):
result = client.synthesis(text, 'zh', 1, {
'vol': 5, # 音量,取值0-15,默认为5中音量
'spd': 5, # 语速,取值0-9,默认为5中语速
'pit': 5, # 音调,取值0-9,默认为5中语调
'per': 4 # 发音人选择, 0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女
})
audio_file_path = str(uuid4()) + ".mp3"
# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
if not isinstance(result, dict):
with open(audio_file_path, 'wb') as f:
f.write(result)
return audio_file_path # 读取文件
def get_file_content(pcm_file_path):
with open(pcm_file_path, 'rb') as fp:
return fp.read() # 语音识别
def new_asr(filePath):
# 将录音机文件wma转换成pcm
pcm_file_path = wav_to_pcm(filePath) # 识别本地文件
res = client.asr(get_file_content(pcm_file_path), 'pcm', 16000, {
'dev_pid': 1536,
})
try:
text = res.get('result')[0]
except Exception as e:
text = "对不起,没有听清你说的啥,请再说一遍。"
os.remove(pcm_file_path)
return text if __name__ == '__main__':
new_asr('audio.wma')

语音合成和语音识别

FFmpeg 转换PCM音频格式

# wav2pcm.py 文件内容
import os def wav_to_pcm(wav_file):
# 假设 wav_file = "音频文件.wav"
# wav_file.split(".") 得到["音频文件","wav"] 拿出第一个结果"音频文件" 与 ".pcm" 拼接 等到结果 "音频文件.pcm"
pcm_file = "%s.pcm" % (wav_file.split(".")[0]) # 就是此前我们在cmd窗口中输入命令,这里面就是在让Python帮我们在cmd中执行命令
os.system("ffmpeg -y -i %s -acodec pcm_s16le -f s16le -ac 1 -ar 16000 %s" % (wav_file, pcm_file))
os.remove(wav_file)
return pcm_file

wav2pcm.py

4.基于百度ai NLP技术中的Simnet 实现 短文本相似度

# 基于百度ai的Simnet的 实现短文本相似度
from aip import AipNlp APP_ID = ''
API_KEY = 'zdU6ldOh7CeQvFFzw9GWhwZr'
SECRET_KEY = 'h5eVzR2G4EPFf78uzceC1zHL6Qj88RCY' client = AipNlp(APP_ID, API_KEY, SECRET_KEY) def new_simnet(text1, text2):
ret = client.simnet(text1, text2)
return ret.get('score')

基于百度ai的Simnet的 实现短文本相似度

5.简单问答 + Tuling机器人
  

import requests

def tuling_answer(text):
to_tuling_url = "http://openapi.tuling123.com/openapi/api/v2" # 接口地址
json_data = {
"reqType": 0,
"perception": {
"inputText": {
"text": text
},
},
"userInfo": {
"apiKey": "93e5736f3b9e496bbb63a0f29afa4d4d",
"userId": "test123"
}
}
res = requests.post(to_tuling_url, json=json_data).json()
answer = res.get("results")[0]['values']['text']
return answer if __name__ == '__main__': tuling_answer('北京天气')

tuling_answer

6.web录音知道里面是干什么的,Audio标签 src音频地址 autoplay当src加载完成时自动播放 controls 显示或关闭播放器

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title> </head>
<body>
<audio src="" autoplay controls id="player"></audio> <button onclick="start_reco()">录制消息</button>
<br>
<button onclick="stop_reco()">发送语音消息</button> </body>
<script src="/static/Recorder.js"></script>
<script type="application/javascript">
var serv = "http://127.0.0.1:9527";
var ws_serv = "ws://127.0.0.1:9528/ws"; var get_music = serv + "/get_audio/";
var ws = new WebSocket(ws_serv);
ws.onmessage = function (data) {
console.log(data.data,111111111)
document.getElementById("player").src = get_music + data.data
};
var reco = null;
var audio_context = new AudioContext();
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia); navigator.getUserMedia({audio: true}, create_stream, function (err) {
console.log(err)
}); function create_stream(user_media) {
var stream_input = audio_context.createMediaStreamSource(user_media);
reco = new Recorder(stream_input);
} function start_reco() {
reco.record();
} function stop_reco() {
reco.stop();
get_audio();
reco.clear();
} function get_audio() {
reco.exportWAV(function (wav_file) {
ws.send(wav_file);
})
} </script>
</html>

html

7.基于websocket传输语音
8.return send_file(file_name)
  

from flask import Flask, send_file, render_template
from queue import Queue
import os
q = Queue() # type:Queue app = Flask(__name__) # type:Flask @app.route("/index")
def index():
return render_template("index1.html") temp_file_name = [] @app.route('/get_audio/<filename>')
def get_audio(filename):
# 利用队列存储临时生成的文件名,下一次请求来的时候删除文件
if not q.empty():
temp = q.get()
print(temp)
os.remove(temp)
q.put(filename)
return send_file(filename) if __name__ == '__main__':
app.run(host='0.0.0.0', port=9527)

flask_app.py

from flask import Flask, request
from geventwebsocket.websocket import WebSocket
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from uuid import uuid4 from baidu_asr_and_synthesis import new_asr, new_synthesis
from simple_questions_answers import question_answer app = Flask(__name__) # type:Flask @app.route('/ws')
def ws():
user_websocket = request.environ.get('wsgi.websocket') # type:WebSocket
if user_websocket:
while 1:
res = user_websocket.receive()
# 保存接收音频文件
rev_file_name = ("%s" + ".wav") % str(uuid4())
with open(rev_file_name, 'wb') as f:
f.write(res)
print(rev_file_name)
# 将接收的音频文件转换为文本
audio_to_text = new_asr(rev_file_name)
print('audio_to_text',audio_to_text)
# 根据问题文本回答问题
answer_text = question_answer(audio_to_text)
print('answer_text',answer_text)
# 将问题答案转成音频文件
answer_audio_file_path = new_synthesis(answer_text)
print('answer_audio_file_path',answer_audio_file_path)
# 将音频文件路径发给前端
user_websocket.send(answer_audio_file_path) if __name__ == '__main__':
http_serv = WSGIServer(('0.0.0.0', 9528), app, handler_class=WebSocketHandler)
http_serv.serve_forever()

flask_ws.py

python全栈开发day115、116-websocket、websocket原理、websocket加解密、简单问答机器人实现的更多相关文章

  1. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  2. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  3. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  4. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  5. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  6. Python全栈开发【基础二】

    Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...

  7. Python全栈开发【基础一】

    Python全栈开发[第一篇] 本节内容: Python 的种类 Python 的环境 Python 入门(解释器.编码.变量.input输入.if流程控制与缩进.while循环) if流程控制与wh ...

  8. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  9. Python全栈开发

    Python全栈开发 一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了. 一.装饰器 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“ ...

随机推荐

  1. jenkins_jmeter配置

    echo "job begin" date export current_time=`date "+%Y%m%d_%H%M%S"` mkdir -p ${WOR ...

  2. selenium各版本jar包下载地址

    http://selenium-release.storage.googleapis.com/index.html

  3. Springboot集成Quartz

    之前学习过spring的定时任务 :https://www.cnblogs.com/slimshady/p/10112515.html 本文主要学习记录下springboot使用quartz 1.   ...

  4. C#调用DLL文件提示试图加载格式不正确的程序

    最近在调用第三方的DLL遇到了一个问题:试图加载格式不正确的程序,找了好久发现都没有解决问题,一直报错,后来发现是程序编译时的配置错了,要将项目->属性->平台选中release-X86 ...

  5. 使用 LD_PRELOAD 变量拦截调用

    背景&原理 很多 a.out 程序都依赖动态库 libc.so, 比如使用 strcmp() 比较密码, 其实是不安全的 使用 LD_PRELOAD 变量可以使该变量中的可链接文件(编译时使用 ...

  6. DataTable行列转置

    DataTable dtNew = new DataTable(); dtNew.Columns.Add("ColumnName", typeof(string)); ; i &l ...

  7. python3导入sqlite3报错

    今天把本地运行OK的scrapy爬虫程序捣鼓到服务器上运行,结果报了以下错误 2018-10-11 19:00:19 [twisted] CRITICAL: Unhandled error in De ...

  8. ASP.NET Web API 2 OData v4教程

    程序数据库格式标准化的开源数据协议 为了增强各种网页应用程序之间的数据兼容性,微软公司启动了一项旨在推广网页程序数据库格式标准化的开源数据协议(OData)计划,于此同时,他们还发 布了一款适用于OD ...

  9. Java SE之[静态成员/类成员]与[非静态成员/实例成员]【static】

    定义 静态成员:又称类成员,使用static修饰符的方法和变量: 非静态成员:又称实例成员,未使用static修饰符的方法和变量. 结论 注:jdk1.8 测试源码 public class Main ...

  10. Selenium中三种等待的使用方式---规避网络延迟、代码不稳定问题

    在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果你不做任何处理的话,代码会由于没有找到元素,而报错.这时我们就要用到wait(等待),而在Selenium中,我们可以用到一共三种等待, ...