4.flask第三方组件
1.flask-session的使用
在flask中,有一个app.session_interface = SecureCookieSessionInterface(),也就是存session,调用open_session方法,取session调用save_session方法
因此如果我们想要自己定制session的存储位置,那么直接修改app.session_interface即可。这里我们介绍一个第三方的组件,叫做flask-session,直接pip install flask-session即可
from flask import Flask
from flask_session import Session
app = Flask(__name__)
Session(app)
@app.route("/login")
def login():
return "login"
if __name__ == "__main__":
app.run(port=8888, debug=True)
我们看看Session(app)这一步都做了些什么
class Session(object):
def __init__(self, app=None):
# 传入app,这里肯定会部位None
self.app = app
if app is not None:
# 调用init_app
self.init_app(app)
def init_app(self, app):
# 可以看到,帮我们把app.session_interface给换掉了
# 因为默认的是SecureCookieSessionInterface(),然后调用了_get_interface(app)
app.session_interface = self._get_interface(app)
def _get_interface(self, app):
# 这里的配置文件先不用管
config = app.config.copy()
config.setdefault('SESSION_TYPE', 'null')
config.setdefault('SESSION_PERMANENT', True)
config.setdefault('SESSION_USE_SIGNER', False)
config.setdefault('SESSION_KEY_PREFIX', 'session:')
config.setdefault('SESSION_REDIS', None)
config.setdefault('SESSION_MEMCACHED', None)
config.setdefault('SESSION_FILE_DIR',
os.path.join(os.getcwd(), 'flask_session'))
config.setdefault('SESSION_FILE_THRESHOLD', 500)
config.setdefault('SESSION_FILE_MODE', 384)
config.setdefault('SESSION_MONGODB', None)
config.setdefault('SESSION_MONGODB_DB', 'flask_session')
config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
config.setdefault('SESSION_SQLALCHEMY', None)
config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
# 这里通过app.config,指定SESSION_TYPE,创建一个新的session_interface
# 可以使redis,memcached,文件系统,数据库等等
if config['SESSION_TYPE'] == 'redis':
session_interface = RedisSessionInterface(
# 如果是redis,则必须指定'SESSION_REDIS'
# 后面的配置不指定,可以使用默认的
config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'memcached':
session_interface = MemcachedSessionInterface(
config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'filesystem':
session_interface = FileSystemSessionInterface(
# 如果是文件,则需要指定文件路径
config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'mongodb':
session_interface = MongoDBSessionInterface(
config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
config['SESSION_MONGODB_COLLECT'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'sqlalchemy':
session_interface = SqlAlchemySessionInterface(
app, config['SESSION_SQLALCHEMY'],
config['SESSION_SQLALCHEMY_TABLE'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
else:
# 如果在配置中不指定,那么不好意思,open_session返回None
"""
class NullSessionInterface(SessionInterface):
def open_session(self, app, request):
return None
"""
session_interface = NullSessionInterface()
# 最后返回session_interface
return session_interface
使用flask-session
from flask import Flask
from flask_session import Session
app = Flask(__name__)
# 我的阿里云没有安装redis,所以这里就使用文件了
"""
如果是redis的话,也是一样的
import redis
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = redis.Redis()
"""
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_FILE_DIR"] = "session_dir"
Session(app)
# 可以看到,在使用层面上,加上三行代码就可以了
@app.route("/login")
def login():
return "login"
if __name__ == "__main__":
app.run(port=8888, debug=True)
可以看到这里多了一个session_dir目录,里面多了两个文件
当然我这里没有设置session,我们来设置一下
from flask import Flask
from flask_session import Session
from flask import session
app = Flask(__name__)
# 我的阿里云没有安装redis,所以这里就使用文件了
"""
如果是redis的话,也是一样的
import redis
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = redis.Redis()
"""
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_FILE_DIR"] = "session_dir"
Session(app)
# 可以看到,在使用层面上,加上三行代码就可以了
@app.route("/login")
def login():
session["username"] = "satori"
return "login"
@app.route("/index")
def index():
print(session["username"])
return "index"
if __name__ == "__main__":
app.run(port=8888, debug=True)
访问/login,再访问/index
可以看到是成功的。此时的session就不再通过序列化、加密写到用户的浏览器的cookie里面去了,而是写到我们指定的地方。然后返回给用户浏览器的则是一个随机字符串,用户再来请求的时候会拿随机字符串来进行匹配。
那么这是怎么做到的呢?
我们之前在看session源码的时候,知道要通过open_session获取session,通过save_session存储session,既然我们把SecureCookieSessionInterface()给换掉了,那么是不是就以为着,我们用来替代的方法中也必须要有open_session和save_session呢?答案是肯定的。
每个SESSION_TYPE都会对应一个类,里面的open_session和save_session会从对应的位置获取和存储session
2.WTForms表单验证基本使用
WTForms表单的两个主要的功能就是就是验证用户提交数据的合法性以及渲染模板。当然还包括其他的功能:CSRF保护,文件上传等等
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/register" method="post">
<table>
<tbody>
<tr>
<td>姓名:</td>
<td><input name="username" type="text"></td>
</tr>
<tr>
<td>密码:</td>
<td><input name="password" type="password"></td>
</tr>
<tr>
<td>重复密码:</td>
<td><input name="repeat_passwd" type="password"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="立即注册"></td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
from flask import Flask, request, render_template
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo
app = Flask(__name__)
# 定义一个类,继承自Form
class RegisterForm(Form):
# 我们这里的左值,就是html里面的name,这里一定要保持一直,否则不会验证
# 这里传入一个列表,因为验证条件会有多个,所以以列表形式传值,这里表示长度为6到10位
username = StringField(validators=[Length(min=6, max=10)])
password = StringField(validators=[Length(min=6, max=10)])
# 这里的repeat_passwd必须要和password保持一致,所以这里的Length也可以不要,まぁいいや
repeat_passwd = StringField(validators=[Length(min=6, max=10), EqualTo("password")])
@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
# 直接将request.form丢进去就可以了
form = RegisterForm(request.form)
# 如果满足条件,那么form.validate()会返回True
if form.validate():
# 然后调用form.username.data和form.password.data即可获取值了
# 而form.username和form.password则为StringField,加上()调用的话则会生成html标签
print(f"username={form.username}, password={form.password}")
return "登陆成功"
else:
# 没通过的话,那么错误信息会存储在form.errors里面
return f"登录失败:{form.errors}"
if __name__ == "__main__":
app.run(port=8888, debug=True)
但是这种报错信息似乎显得不友好,因此我们也可以自己指定
class RegisterForm(Form):
# 我们这里的左值,就是html里面的name="username",这里一定要保持一直,否则不会验证
# 这里传入一个列表,因为验证条件会有多个,所以以列表形式传值,这里表示长度为6到10位
username = StringField(validators=[Length(min=6, max=10, message="用户名必须在6到10位")])
password = StringField(validators=[Length(min=6, max=10, message="密码必须在6到10位")])
# 这里的repeat_passwd必须要和password保持一致,所以这里的Length也可以不要,まぁいいや
repeat_passwd = StringField(validators=[Length(min=6, max=10, message="密码必须在6到10位"),
EqualTo("password", message="两次输入的密码不一致")])
3.WTForms常用验证器
常用的验证器: 数据发送过来,经过表单验证,因此需要验证器来进行验证,以下是一些常用的验证器
- Email:验证上传的数据是否为邮箱
- EqualTo:验证上传的数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等
- InputRequired:表示该字段为必填项,只要填了就通过,不填就是失败
- Length:长度限制,有min和max两个值进行限制 ,表示输入的字符串的长度为[min, max]
- NumberRange:数字的区间,有mix和max两个值进行限制,而且必须是数字,如果处在这两个数字之间则满足
- Regexp:自定义正则表达式
- URL:必须要是url的形式
- UUID:必须是UUID的形式
class RegisterForm(Form):
# 必须为邮箱
email = StringField(validators=[Email()])
# 必须输入
username = StringField(validators=[InputRequired()])
# 是一个数字,要在18到60之间
age = StringField(validators=[NumberRange(min=18, max=60)])
# 以1开头的11位数字
phone = StringField(validators=[Regexp(r"1\d{10}")])
# url格式
url = StringField(validators=[URL()])
# uuid类型
uuid = StringField(validators=[UUID()])
没有什么技术难度,具体不再演示了
3.自定义表单验证器
from flask import Flask, request, render_template
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange, Regexp, URL, UUID, ValidationError
app = Flask(__name__)
class RegisterForm(Form):
# 如何自定义表单,比如我们在html里面有一个name="username"
# 那么就需要定义一个函数叫做,validate_username,当验证username的时候,会自动执行相应的函数
username = StringField(validators=[InputRequired(), Length(min=8, max=15, message="用户名要在8到15位")])
# 必须得有username =
# 否则即使定义了validate_username也是无效的
def validate_username(self, field):
# field.data,就是我们在html中获取的值
print(type(field)) # <class 'wtforms.fields.core.StringField'>
# 如果全部username全部是数字,那么不允许
if set(field.data) <= set("123456789"):
raise ValidationError("用户名不能全为数字")
@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
# 直接将request.form丢进去就可以了
form = RegisterForm(request.form)
if form.validate():
return "登陆成功"
else:
# 没通过的话,那么错误信息会存储在form.errors里面
return f"登录失败:{form.errors}"
if __name__ == "__main__":
app.run(port=8888, debug=True)
4.使用wtforms渲染模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.text_color {
background-color: yellow;
}
</style>
</head>
<body>
<form action="/index" method="post">
<table>
<tbody>
<tr>
<td>{{ form.username.label }}</td>
<td>{{ form.username(class='text_color') }}</td>
</tr>
<tr>
<td>{{ form.age.label }}</td>
<td>{{ form.age() }}</td>
</tr>
<tr>
<td>{{ form.remember.label }}</td>
<td>{{ form.remember() }}</td>
</tr>
<tr>
<td>{{ form.tags.label }}</td>
<td>{{ form.tags() }}</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="提交">
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
from flask import Flask, request, render_template
from wtforms import Form, StringField, BooleanField, SelectField
from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange, Regexp, URL, UUID, ValidationError
app = Flask(__name__)
class IndexForm(Form):
# 第一个参数指定之后,会自动传到html中的form.username.label,如果不指定,那么自动为左值并且首字母大写
# 而form.username()则是一个input标签,这里的左值username到html中就会变成,name="username"
username = StringField("用户名:", validators=[InputRequired()])
# 同理age也是一样,我这里指定了"年龄:", 那么form.age.label就等于"年龄:"
# form.age()则是一个input标签,等价于<input name="age" type="text"/>
# 当然form.age()里面还可以指定属性,比如class,id等等
age = StringField("年龄:", validators=[InputRequired()])
# 还有BooleanField,在页面中就是一个小框,点击是否确定
remember = BooleanField("记住我:")
# SelectField,则是菜单模式,可以选择
tags = SelectField("选项:", choices=[("1", "古明地觉"), ("2", "椎名真白"), ("3", "四方茉莉")])
@app.route("/index", methods=["GET", "POST"])
def index():
if request.method == "GET":
# 如果我们在html中自己指定表单信息的话,这里是不需要IndexForm的
# 但是现在html中的信息是需要我们生成的,创建将form丢进去
form = IndexForm()
return render_template("index.html", form=form)
else:
# 当用户把信息填完之后,将request.form再丢进IndexForm中,进行验证
form = IndexForm(request.form)
if form.validate():
# 此时form.username获取的是一个StringField
# 如果获取值,需要调用form.username.data
# form.username()的话,则会打印对应标签
print(type(form.username)) # <class 'wtforms.fields.core.StringField'>
print(form.username.name) # username
print(form.username.data) # satori
print(form.username.type) # StringField
print(form.username.label) # <label for="username">用户名:</label>
print(form.username()) # <input id="username" name="username" required type="text" value="satori">
print(type(form.age)) # <class 'wtforms.fields.core.StringField'>
print(form.age.name) # age
print(form.age.data) # 16
print(form.age.type) # StringField
print(form.age.label) # <label for="age">年龄:</label>
print(form.age()) # <input id="age" name="age" required type="text" value="16">
print(type(form.remember)) # <class 'wtforms.fields.core.BooleanField'>
print(form.remember.name) # remember
print(form.remember.data) # True
print(form.remember.type) # BooleanField
print(form.remember.label) # <label for="remember">记住我:</label>
print(form.remember()) # <input checked id="remember" name="remember" type="checkbox" value="y">
print(type(form.tags)) # <class 'wtforms.fields.core.SelectField'>
print(form.tags.name) # tags
print(form.tags.data) # 1
print(form.tags.type) # SelectField
print(form.tags.label) # <label for="tags">选项:</label>
print(form.tags()) # <select id="tags" name="tags"><option selected value="1">古明地觉</option><option value="2">椎名真白</option><option value="3">四方茉莉</option></select>
return f"xxxxx"
else:
return f"error occurred:{form.errors}"
if __name__ == "__main__":
app.run(port=8888, debug=True)
5.上传文件以及访问上传的文件
flask中如何接收用户上传的文件呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<table>
<tr>
<td>头像:</td>
<td><input type="file" name="avatar"></td>
</tr>
<tr>
<td>描述:</td>
<td><input type="text" name="describe"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
from flask import Flask, request, render_template
import os
app = Flask(__name__)
@app.route("/upload", methods=["GET", "POST"])
def upload():
if request.method == 'GET':
return render_template("upload.html")
else:
describe = request.form.get("describe")
avatar = request.files.get("avatar")
# avatar就是我们所获取的文件
# avatar.filename则是文件名,那么如何将文件保存起来呢?可以直接使用save
# 由于直接使用用户获取的文件名比较危险,那么可以from werkzeug.utils import secure_filename,然后进行转化
avatar.save(os.path.join(os.path.dirname(__file__), "upload_file", avatar.filename))
return f"{avatar.filename}上传成功, 描述信息为{describe}"
if __name__ == "__main__":
app.run(port=8888, debug=True)
6.让用户直接访问图片
就拿我们刚才上传的图片为例
from flask import Flask, request, render_template
import os
app = Flask(__name__)
@app.route("/images/<filename>")
def get_image(filename):
from flask import send_from_directory
if os.path.exists(os.path.join(os.path.dirname(__file__), "upload_file", filename)):
# send_from_directory表示将图片直接返回
# 接收两个参数,一个是图片的目录,一个是图片的名字
return send_from_directory(os.path.join(os.path.dirname(__file__), "upload_file"), filename)
else:
return "没有该图片。。。。。。"
if __name__ == "__main__":
app.run(port=8888, debug=True)
7.使用flask-wtf验证上传的文件
之前我们接收用户上传的文件,也没有进行判断,比如说用户上传头像,应该是一张图片,可如果用户上传的是txt,或者py文件怎么办呢?这时候我们应该对用户上传的文件进行一个判断。flask-wtf是一个专门用于对文件进行验证的第三方插件。同样需要pip install flask-wtf
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<table>
<tr>
<td>头像:</td>
<td><input type="file" name="avatar"></td>
</tr>
<tr>
<td>描述:</td>
<td><input type="text" name="describe"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
from flask import Flask, request, render_template
from wtforms import Form, StringField, FileField
from wtforms.validators import InputRequired
# FileRequired表示文件必须上传,FileAllowed可以输入允许的文件格式
from flask_wtf.file import FileRequired, FileAllowed
from werkzeug.datastructures import CombinedMultiDict
app = Flask(__name__)
class UpLoadForm(Form):
avatar = FileField(validators=[FileRequired(), FileAllowed(["jpg", "png", "gif"], message="文件不符合格式呢?")])
describe = StringField(validators=[InputRequired()])
@app.route("/upload", methods=["GET", "POST"])
def get_img():
if request.method == "GET":
return render_template("upload.html")
else:
# 因为我们既有文件类型,还有一般的表单类型。因此需要把两者组合在一块
form = UpLoadForm(CombinedMultiDict([request.form, request.files]))
if form.validate():
avatar = form.avatar.data # 或者avatar = request.files.get("avatar")
describe = form.describe.data # 或者describe= request.form.get("describe")
return f"上传成功,上传的文件为:{avatar.filename}, 描述信息为:{describe}"
else:
return f"error occurred:{form.errors}"
if __name__ == "__main__":
app.run(port=8888, debug=True)
8.flask抵御csrf
什么是csrf
使用flask-wtf开启csrf保护
from flask_wtf.csrf import CsrfProtect
# 开启csrf保护
CsrfProtect(app)
注意,需要为CSRF保护设置一个密钥,但通常情况下,和Flask应用的SECRET_KEY是一样的。如果你设置的模板中存在表单,你只需要在表单中添加如下
<form method="post" action="/">
{{ form.csrf_token() }}
</form>
如果没有模板中没有表单,你仍然需要一个 CSRF 令牌:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
如果网站没有通过CSRF验证,都会返回400响应,我们可以自定义这个错误响应:
from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()
@csrf.error_handler
def csrf_error(reason):
return render_template('csrf_error.html', reason=reason)
这里官方强烈建议对所有视图启用CSRF保护,也提供了给某些视图函数不需要保护的装饰器
@csrf.exempt # 不对index进行保护
@app.route('/foo', methods=('GET', 'POST'))
def index():
return "index"
你也可以在所有的视图中禁用CSRF保护,app.config中通过设置 WTF_CSRF_CHECK_DEFAULT
为 False,仅仅当你需要保护的时候选择调用csrf.protect()手动开启保护
4.flask第三方组件的更多相关文章
- Flask第三方组件之flask_session
flask默认提供了session, 但是存在以下问题: ① session数据存在客户端, 不安全 ② 大小有限制 ③ 增加了客户端的压力 所以才产生了很多第三方的session机制, 我使用的是f ...
- Flask第三方组件 之 Flask-Session
原生session:交由客户端保管机制,安全性相对较差,优势是一点都不占用服务器空间 Flask-Session: 解决原生session的劣势 安装包 from flask import Flask ...
- Flask第三方工具组件介绍
flask-wtf组件flask-login组件flask-session组件flask-sqlalchemy组件flask-script组件flask-cache组件flask-assets组件fl ...
- 1.7 flask 的组件 wtfroms使用
2019-1-7 17:59:37 还有两天左右flask就结束啦!昨晚逛了一下吾爱破解还有慕课,发现有三个意外项目, Django生鲜项目,flask电影网站项目,vue美团网项目,都保存百度云啦, ...
- flask seesion组件
一.简介 flask中session组件可分为内置的session组件还有第三方flask-session组件,内置的session组件功能单一,而第三方的flask-sessoin可支持re ...
- C#通过第三方组件生成二维码(QR Code)和条形码(Bar Code)
用C#如何生成二维码,我们可以通过现有的第三方dll直接来实现,下面列出几种不同的生成方法: 1):通过QrCodeNet(Gma.QrCodeNet.Encoding.dll)来实现 1.1):首先 ...
- .Net开发笔记(二十)创建一个需要授权的第三方组件
在使用需要授权的软件时,注册付费的目标是软件的使用者,我们开发人员平时用到的一些第三方组件也是需要授权付费的,也就是说,付费者是开发人员,并不是系统(使用了该第三方组件)的最终使用者. 以上两者的区别 ...
- .net开发中常用的第三方组件
.net开发中常用的第三方组件 2013-05-09 09:33:32| 分类: dotnet |举报 |字号 订阅 下载LOFTER 我的照片书 | RSS.NET.dll RSS. ...
- iOS 项目中用到的一些开源库和第三方组件
iOS 项目中用到的一些 iOS 开源库和第三方组件 分享一下我目前所在公司 iOS 项目中用到的一些 iOS 开源库和第三方组件, 感谢开源, 减少了我们的劳动力, 节约了我们大量的时间, 让我们有 ...
随机推荐
- mongodb游标的使用
1.插入数据 ;i<;i++){ db.shop.insert({_id:i+,name:+i}) } 2.查看数据数 db.shop.find().count() 3.获取游标.判断是否还存在 ...
- Asp.Net Core 反向工程
反向工程1.反向工程是实体类型类和一个基于数据库架构的 DbContext 类的基架的过程2.Scaffold-DbContext(数据库上下文脚手架) 使用Scaffold-DbContext ...
- Qt qss 动态属性-不同条件不同显示
一. 1.为了用户界面外观的动态变化,属性选择器可以与动态属性组合使用. 2.当一个属性值变化时,所引用的样式不会自动更新.相反地,必须手动触发更新才会生效.unpolish()用于清理之前的样式,而 ...
- Golang中基础的命令行模块urfave/cli
前言相信只要部署过线上服务,都知道启动参数一定是必不可少的,当你在不同的网络.硬件.软件环境下去启动一个服务的时候,总会有一些启动参数是不确定的,这时候就需要通过命令行模块去解析这些参数,urfave ...
- 技术简历写这么写,才能得到BAT面试官们的青睐
公众号[程序员江湖] 作者陆小凤,985 软件硕士,阿里 Java 研发工程师,在技术校园招聘.自学编程.计算机考研等方面有丰富经验和独到见解,目前致力于分享程序员干货和学习经验,同时热衷于分享作为程 ...
- 2019年icpc区域赛银川站总结
目录 一.前言 二.10月19日热身赛 三.10月20日正式赛 四.结果 一.前言 比赛前我们队有ccpc厦门和icpc银川的名额,然而这两个地区的时间正好撞了,考虑到银川更容易拿奖,加上我们ACM协 ...
- TensorFlow实战第五课(MNIST手写数据集识别)
Tensorflow实现softmax regression识别手写数字 MNIST手写数字识别可以形象的描述为机器学习领域中的hello world. MNIST是一个非常简单的机器视觉数据集.它由 ...
- python selenium API 常用方法
配置使用环境 下载相应的浏览器驱动, Firefox 是默认的 本文以 chrome 为主 ,放在scripts目录下ChromeDriver 官方下载地址 : 所有版本的 ChromeDriver ...
- ciscn-华北赛区-Day1-Web2题解
漏洞点 薅羊毛逻辑漏洞 Cookie伪造 -> JWT python反序列化 -> 反弹shell 薅羊毛逻辑漏洞 打开题目是这样一个页面 其实最初的题目这个募集资金的进度条是没有刷满的, ...
- oracle在group by时某列有多个值的拼接
最近编码过程中出现了group by后,某些列会有多个值,而我需要把这些多个值的列进行拼接的情况,和大家分享一下. 有如下表student: 我们希望以class分组,每组的信息平铺,效果如下 分组首 ...