0%

flask

flask介绍安装快速入门

  • 差异
1
2
3
4
5
6
7
8
9
# django    大

# flask 小

# tornado 异步(2.x用的多,3.5以后用得少)

# Sanic

# FastAPi
  • 安装
1
2
3
4
5
# 安装:pip3 install flask

# https://www.cnblogs.com/liuqingzheng/p/11012099.html

# Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架
  • 快速使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 快速使用

from flask import Flask

app=Flask(__name__)

@app.route('/',methods=['GET',])
def index():
return 'hello world lqz'

if __name__ == '__main__':
app.run(port=8080)
# 一旦有请求过来,执行app(),对象()---->触发类的__call__()
# 请求一来,执行 app()--->Flask类的__call__方法执行

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from flask import Flask,request,render_template,redirect,session,url_for
app = Flask(__name__)


#配置信息
## 方式一:直接通过app对象设置,只能设置这两个,其他不支持
# app.debug = False # debug模式,开启了,就会热更新
# app.secret_key = 'sdfsdfsdfsdf' # 秘钥,django配置文件中的秘钥

## 方式二:直接通过app对象的config(字典)属性设置
# app.config['DEBUG']=True
# print(app.config)


## 方式三:直接使用py文件
# app.config.from_pyfile("settings.py")





### 重点方式:后期用这种方式,使用类方式
# app.config.from_object("python类或类的路径")

app.config.from_object('settings.ProductionConfig')
print(app.config['DATABASE_URI'])
### 其他方式:(了解)
#通过环境变量配置
# app.config.from_envvar("环境变量名称")
# app.config.from_json("json文件名称")
# app.config.from_mapping({'DEBUG': True})





# print(app.config)
if __name__ == '__main__':
app.run()


'''
有很多内置配置参数,不需要掌握
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}


'''

路由系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from flask import Flask,request,render_template,redirect,session,url_for
app = Flask(__name__)

app.debug = True # debug模式,开启了,就会热更新
app.secret_key = 'sdfsdfsdfsdf' # 秘钥,django配置文件中的秘钥


# @app.route('/index/<name>',methods=['GET'],endpoint='index',defaults={'name':'lqz'},strict_slashes=True,redirect_to='http://www.baidu.com')
@app.route('/index/<string:name>/<int:pk>',methods=['GET'],endpoint='index')
def index(name,pk):
print(name)
return 'hello'



# app.add_url_rule('/index',endpoint='index',view_func=index,defaults={'name':'lqz','age':19})


if __name__ == '__main__':
app.run()


## 路由本质app.add_url_rule
'''
路由系统的本质,就是 app.add_url_rule(路径, 别名, 函数内存地址, **options)
endpoint:如果不填,就是函数名(加装饰器时要注意)
与django路由类似
django与flask路由:flask路由基于装饰器,本质是基于:add_url_rule
add_url_rule 源码中,endpoint如果为空,endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)

'''


#### add_url_rule的参数
'''
##### 记住
rule, URL规则
view_func, 视图函数名称
defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'} 为函数提供参数
endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
methods = None, 允许的请求方式,如:["GET", "POST"]



###了解
#对URL最后的 / 符号是否严格要求
strict_slashes = None
@app.route('/index', strict_slashes=False)
#访问http://www.xx.com/index/ 或http://www.xx.com/index均可
@app.route('/index', strict_slashes=True)
#仅访问http://www.xx.com/index
#重定向到指定地址
redirect_to = None,
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')

'''



### 路由转换器
'''
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,

'''

## 自定义转换器(不用)

CBV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from flask import Flask,request,render_template,redirect,session,url_for
from flask.views import View,MethodView
app = Flask(__name__)

app.debug = True # debug模式,开启了,就会热更新
app.secret_key = 'sdfsdfsdfsdf' # 秘钥,django配置文件中的秘钥



class IndexView(MethodView): # 继承MethodView
def get(self):
url=url_for('aaa')
print(url)
return '我是get'

def post(self):
return '我是post'



app.add_url_rule('/index',view_func=IndexView.as_view(name='aaa'))
'''
endpoint:如果传了,优先使用endpoint,如果不传使用as_view(name='aaa'),但是name='aaa'必须传

cbv要继承MethodView,只需要写get,post...

cbv要继承View,必须重写dispatch
'''
if __name__ == '__main__':
app.run(port=8888)

模板(不重要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from flask import Flask,request,render_template,redirect,session,url_for,Markup
from flask.views import View,MethodView
app = Flask(__name__)

app.debug = True # debug模式,开启了,就会热更新
app.secret_key = 'sdfsdfsdfsdf' # 秘钥,django配置文件中的秘钥



def test(a,b):
return a+b

class IndexView(MethodView): # 继承MethodView
def get(self):
url=url_for('aaa')
print(url)
# a=Markup('<a href="http://www.baidu.com">点我看美女</a>')
a='<a href="http://www.baidu.com">点我看美女</a>'
return render_template('test.html',name='lqz',test=test,a=a)

def post(self):
return '我是post'



app.add_url_rule('/index',view_func=IndexView.as_view(name='aaa'))

if __name__ == '__main__':
app.run(port=8888)



'''
0 跟dtl完全一样,但是它可以执行函数
1.Markup等价django的mark_safe ,

2.extends,include一模一样

'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<h1>{{name}}</h1>
<hr>
{{test(4,5)}}
<hr>
{{a|safe}}
{{a}}
</body>
</html>

请求响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from flask import Flask,jsonify
from flask import views

from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True


@app.route('/login.html', methods=['GET', "POST"])
def login():
# 请求相关信息
# request.method 提交的方法
# print(request.args.get('name')) #get请求提交的数据---GET
# print(request.form) #post请求提交数据----POST
# print(request.values) # get和post的汇总
# print(request.query_string) #b'name=lqz&age=19'
# print(request.cookies)
# print(request.path)
# print(request.full_path)
# request.args get请求提及的数据
# request.form post请求提交的数据
# request.values post和get提交的数据总和
# request.cookies 客户端所带的cookie
# request.headers 请求头
# request.path 不带域名,请求路径
# request.full_path 不带域名,带参数的请求路径
# request.url 带域名带参数的请求路径
# request.base_url 带域名请求路径
# request.url_root 域名
# request.host_url 域名
# request.host 127.0.0.1:500
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))



# 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
# return jsonify({'k1':'v1'})


# response = make_response(render_template('index.html'))
response = make_response('hello')
# response是flask.wrappers.Response类型
response.delete_cookie('session')
response.set_cookie('name', 'lqz')
response.headers['X-Something'] = 'A value'
return response
# return "内容"

if __name__ == '__main__':
app.run(port=8888)

session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 使用
-增:session['name']=lqz
-查:session.get('name')
-删:session.pop('name')

## 源码分析SecureCookieSessionInterface
## open_session
'''
val = request.cookies.get(app.session_cookie_name)
data = s.loads(val, max_age=max_age)
return self.session_class(data)
'''

## save_session
'''
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
app.session_cookie_name,
val,
expires=expires,
)
''''name')


# set_cookie其他参数

key, 键
value='', 值
max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。
domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取
secure=False, 浏览器将通过HTTPS来回传cookie
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from flask import Flask,jsonify
from flask import views

from flask import Flask,session
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True
app.secret_key='sdfsdfsadfasdf'

app.session_interface

@app.route('/login.html', methods=['GET', "POST"])
def login():
session['name']='lqz'
'''
#在django中发什么三件事,1,生成一个随机的字符串 2 往数据库存 3 写入cookie返回浏览器
#在flask中没有数据库,但session是怎样实现的?
# 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session

'''
response=make_response('hello')
# response.set_cookie('name', 'lqz')
return response


@app.route('/index', methods=['GET', "POST"])
def index():
print(session.get('name'))
return '我是首页'
if __name__ == '__main__':
app.run(port=8080)


## 源码分析SecureCookieSessionInterface
## open_session
'''
val = request.cookies.get(app.session_cookie_name)
data = s.loads(val, max_age=max_age)
return self.session_class(data)
'''

## save_session
'''
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
app.session_cookie_name,
val,
expires=expires,
)
'''

闪现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from flask import Flask,jsonify,flash,get_flashed_messages

from flask import Flask
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=True
app.secret_key='sdfsdfsadfasdf'


@app.route('/user', methods=['GET', "POST"])
def login():
try:
a=[1,2,3]
print(a[9])
except Exception as e:
print(e)
## 放到某个位置
# flash(str(e))
# 高级使用
flash('超时错误', category="x1")
flash('xx错误', category="x3")


# return redirect('/error?errors=%s'%str(e))
return redirect('/error')
response=make_response('hello')
return response


@app.route('/error', methods=['GET', "POST"])
def error():
#从那个位置取出来
# errors=get_flashed_messages()

errors=get_flashed_messages(category_filter=['x1'])
return render_template('error.html',errors=errors)
if __name__ == '__main__':
app.run(port=8080)

请求扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from flask import Flask,jsonify,flash,get_flashed_messages

from flask import Flask,request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
app.debug=False
app.secret_key='sdfsdfsadfasdf'


# @app.before_request #类比django中间件中的process_request,写多个执行顺序是从上往下
# def before():
# print('我来了')
#
# request.xx='xxxxxx'
# # return '回去吧' #四件套之一
# return None # 继续进入下一个before_request

# @app.before_request #类比django中间件中的process_request
# def before2():
# print('我来了22')
#
# return None

# @app.after_request # 从下往上
# def after(response):
# print('我走了')
# print(response)
# response.headers['xxxx']='xxx'
# return response #要return response
# @app.after_request
# def after2(response):
# print('我走了222')
# return response #要return response


# @app.before_first_request # 只会执行一次,程序启动以后,第一个访问的会触发,以后再也不会了
# def first():
# # 程序初始化的一些操作
# print('我的第一次')


# @app.teardown_request
# def ter(e):
# # 日志记录,不管当次请求是否出异常,都会执行,出了异常,e就是异常对象,debug=False模式下
# print(e)
# print('我执行了')

# @app.errorhandler(404) #只要是404错误,都会走它
# def error_404(arg):
# return "404错误了"
# # return render_template('404.html')

# @app.errorhandler(500) #只要是500错误,都会走它,debug模式要关掉
# def error_500(arg):
# return "出问题了"


@app.template_global()
def sb(a1, a2):
return a1 + a2
#{{sb(1,2)}}


@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
#{{ 1|db(2,3)}}

@app.route('/user', methods=['GET', "POST"])
def login():
# print(request.xx)
# response=make_response('hello')
# return response
return render_template('login.html')


if __name__ == '__main__':
app.run(port=8080)

蓝图

1
2
3
4
5
6
7
# 对程序进行目录结构划分


# 使用步骤
-实例化得到一个蓝图对象(可以指定直接的静态文件和模板路径)
-在app中注册蓝图(可以指定前缀)
-以后再写路由装饰器,使用蓝图对象的.route

flask 项目演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1 创建一个库movie
# 2 手动把表同步进去
-modes.py,解开注释,右键执行

# 3 安装项目依赖
-flask-sqlalchemy
-flask_script
-flask_redis
-flask_wtf
# 4 命令行中运行
python3 manage.py runserver
# 5 后台管理中rbac控制



# https://gitee.com/openspug/spug/tree/1.x/

g对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from flask import Flask,g,request,session

# g对象在当次请求中一直有效
# g和session有什么区别
#session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,
# 但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次


app = Flask(__name__)

@app.before_request
def first():
session['name']='dlrb'
request.form='egon'
g.name='lqz'

@app.after_request
def after(response):
print('11111',g.name)
return response

@app.route('/')
def hello_world():
print('00000',g.name)

# test()
# test1()
return 'Hello World!'

if __name__ == '__main__':
app.run()

flask-session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# pip install flask-session

from flask import Flask,g,request,session

from flask_session import RedisSessionInterface
app = Flask(__name__)
app.debug=True

app.secret_key='asdfasdfasdf'
# 方式一
# from redis import Redis
# conn=Redis(host='127.0.0.1',port=6379)
# app.session_interface=RedisSessionInterface(redis=conn,key_prefix='flask_session')



## 方式二(flask使用第三方插件的通用方案)
from flask_session import Session
from redis import Redis
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX']='flask_session'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
Session(app)

@app.route('/')
def hello_world():
session['name']='lqz'
return 'Hello World!'

if __name__ == '__main__':
app.run()



# 如何设置session的过期时间?
配置文件一个参数


#设置cookie时,如何设定关闭浏览器则cookie失效
app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)

数据库连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78


# from flask import Flask,g,request,session
# import time
# import pymysql
# app = Flask(__name__)
# app.debug=True
#
# app.secret_key='asdfasdfasdf'
#
#
#
#
#
# @app.route('/')
# def hello_world():
# conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1', database='luffy')
# cursor = conn.cursor()
# cursor.execute('select * from luffy_order')
# time.sleep(1)
# print(cursor.fetchall())
# return 'Hello World!'
#
#
# @app.route('/hello')
# def hello_world1():
# conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1', database='luffy')
# cursor = conn.cursor()
# cursor.execute('select * from luffy_banner')
# time.sleep(1)
#
# print(cursor.fetchall())
# return 'Hello World!'
# if __name__ == '__main__':
# app.run()



## 问题:如果使用全局连接对象,会导致数据错乱
## 问题二:如果在视图函数中创建数据库连接对象,会导致连接数过多
## 解决:使用数据库连接池 DBUtils
from dbutils.pooled_db import PooledDB
import pymysql
POOL=PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3,
# 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='1',
database='luffy',
charset='utf8')


# 去池中获取连接
from threading import Thread

# mysql可以看到当前有多少个连接数

def task():
conn = POOL.connection()
cursor = conn.cursor()
cursor.execute('select * from luffy_order')

print(cursor.fetchall())
for i in range(100):
t=Thread(target=task)
t.start()

wtforms(了解)

1
# 类似forms组件

信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# Flask框架中的信号基于blinker,其主要就是让开发者可是在flask执行过程中定制一些用户行为



# pip3 install blinker


## flask中有内置信号
# 内置信号什么时候触发的
'''
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行

before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行

got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行

request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)

appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发


'''

## 自定义信号

'''
1 写一个信号
2 写一个函数
3 信号绑定函数
4 触发信号
'''




#### 内置信号的使用
'''
内置信号使用步骤:
-写一个函数
-跟内置信号绑定
-以后只要触发内置信号,函数就会执行
'''
from flask import Flask,signals,render_template
from flask.signals import _signals
app = Flask(__name__)

# 往信号中注册函数
def func(*args,**kwargs):
print('触发型号',args,kwargs)
# 信号一般用来记录日志
# signals.request_started.connect(func)

# 给模板渲染前编写信号
def template_before(*args,**kwargs):
print(args)
print(kwargs)
print('模板开始渲染了')
# signals.before_render_template.connect(template_before)




'''
1 写一个信号
2 写一个函数
3 信号绑定函数
4 触发信号
'''
# 自定义信号
before_view = _signals.signal('before_view')

# 写函数
def test(*args,**kwargs):
print('我执行了')
print(args)
print(kwargs)

# 绑定给信号
before_view.connect(test)


# 触发信号

@app.route('/',methods=['GET',"POST"])
def index():
print('视图')
return 'hello world'


@app.route('/index',methods=['GET',"POST"])
def index1():
# 触发信号
before_view.send(name='lqz',age=19)
print('视图')
return render_template('index.html',a='lqz')

if __name__ == '__main__':
app.run(port=8080)
app.__call__

flask-script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask_script import Manager
from flask import Flask


app = Flask(__name__)

manager=Manager(app)

@app.route('/')
def index():
return '你好'

if __name__ == '__main__':
manager.run()

#以后在执行,直接:python3 manage.py runserver
#python3 manage.py runserver --help

SQLAlchemy(orm框架)

1
2
3
# orm框架SQLAlchemy,第三方,独立使用,集成到web框架中
# django的orm框架
# pip install SQLAlchemy

sqlalchemy 一对多关系

1
2
3
4
5
6
7
# 操作mysql
-pymysql---》写原生sql
-django的orm
-sqlalchemy:orm框架,独立于其他框架,可以单独使用,也可以集成到框架中使用
-(了解)同步框架:sanic,fastapi,只用来对表进行管理,不用来查询插入,aiomysql
-Gino:https://python-gino.org/docs/zh/master/tutorials/tutorial.html
-国人写的异步的orm框架

sqlalchemy 多对多关系,其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
Base = declarative_base()

#单表
class Users(Base):
__tablename__ = 'users' # 数据库表名称
id = Column(Integer, primary_key=True) # id 主键
name = Column(String(32), index=True, nullable=False) # name列,索引,不可为空
# email = Column(String(32), unique=True)
#datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
# ctime = Column(DateTime, default=datetime.datetime.now)
# extra = Column(Text, nullable=True)

__table_args__ = (
# UniqueConstraint('id', 'name', name='uix_id_name'), #联合唯一
# Index('ix_id_name', 'name', 'email'), #索引
)

def __repr__(self):
return self.name+'==='+str(self.id)


### 一对多
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='篮球')
def __str__(self):
return self.caption

class Person(Base):
__tablename__ = 'person'
nid = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=True)
# hobby指的是tablename而不是类名,uselist=False
hobby_id = Column(Integer, ForeignKey("hobby.id"))

# 跟数据库无关,不会新增字段,只用于快速链表操作
# 类名,backref用于反向查询
hobby = relationship('Hobby', backref='pers')

def __str__(self):
return self.name

def __repr__(self): # 解释器环境下打印对象显示的样子
return self.name


###多对多
class Boy2Girl(Base):
__tablename__ = 'boy2girl'
id = Column(Integer, primary_key=True, autoincrement=True)
girl_id = Column(Integer, ForeignKey('girl.id'))
boy_id = Column(Integer, ForeignKey('boy.id'))



class Girl(Base):
__tablename__ = 'girl'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)


class Boy(Base):
__tablename__ = 'boy'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True, nullable=False)

# 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以
girls = relationship('Girl', secondary='boy2girl', backref='boys')
def __str__(self):
return self.name

def init_db():
"""
根据类创建数据库表
:return:
"""
engine = create_engine(
"mysql+pymysql://root:1@127.0.0.1:3306/aaa?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Base.metadata.create_all(engine)

def drop_db():
"""
根据类删除数据库表
:return:
"""
engine = create_engine(
"mysql+pymysql://root:1@127.0.0.1:3306/aaa?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Base.metadata.drop_all(engine)

if __name__ == '__main__':
# drop_db()
init_db()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196


from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
import models
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from models import Users
from sqlalchemy.sql import text
#"mysql+pymysql://root@127.0.0.1:3306/aaa"
engine = create_engine("mysql+pymysql://root:1@127.0.0.1:3306/aaa", max_overflow=0, pool_size=5)
Connection = sessionmaker(bind=engine)

session=Connection()


## 一对多操作
# hobby=models.Hobby(caption='橄榄球')
# session.add(hobby)
# hobby=models.Hobby(caption='足球')
# person=models.Person(name='张三',hobby_id=1)
# session.add_all([hobby,person])

# 使用relationship
# hobby=models.Hobby(caption='水球')
# person=models.Person(name='张三',hobby=hobby)
# session.add_all([hobby,person])

## 基于对象的跨表查询
# 正向
# person=session.query(models.Person).filter_by(nid=1).first()
# print(person)
# print(person.hobby)

# 反向
# hobby=session.query(models.Hobby).filter_by(id=1).first()
# print(hobby)
# print(hobby.pers)


# 多对多

# girl=models.Girl(name='刘亦菲')
# boy=models.Boy(name='吴亦凡')
# session.add_all([girl,boy])

# b2g=models.Boy2Girl(boy_id=1,girl_id=1)
# session.add(b2g)



## 基于对象的跨表查询
# 正向
# boy=session.query(models.Boy).filter_by(name='吴亦凡').first()
# print(boy)
# print(boy.girls)

# 反向
# girl=session.query(models.Girl).filter_by(name='刘亦菲').first()
# print(girl)
# print(girl.boys)

# b2g=session.query(models.Boy2Girl).filter_by(id=1).first()
# print(b2g.boy)



## filter 和 filter_by区别
# filter内写条件,filter_by内传参数

# ################ 修改 ################

#传字典
# res=session.query(models.Boy).filter_by(id = 1).all()
# res=session.query(models.Boy).filter(models.Boy.id >= 1).all()
# print(res)


# session.query(models.Boy).filter(models.Boy.id > 1).update({"name" : "lqz"})
#类似于django的F查询
# session.query(models.Boy).filter(models.Boy.id > 1).update({models.Boy.name: models.Boy.name + "099"}, synchronize_session=False)
# session.query(models.Boy).filter(models.Boy.id == 3).update({"id": models.Boy.id + 1}, synchronize_session="evaluate")


# session.commit()

# ################ 查询 ################

# r1 = session.query(models.Boy).all()
#只取age列,把name重命名为xx

# select name as xx,hobby_id from person
# r1 = session.query(models.Person.name.label('xx'), models.Person.hobby_id).all()
# #filter传的是表达式,filter_by传的是参数
# r1 = session.query(Users).filter(Users.name == "lqz").all()
# r4 = session.query(Users).filter_by(name='lqz').all()
# r1 = session.query(Users).filter_by(name='lqz').first()
# #:value 和:name 相当于占位符,用params传参数
# r1 = session.query(Users).filter(text("id<:value and name=:name")).params(value=3, name='lqz').order_by(Users.id).all()
# #自定义查询sql
# r1 = session.query(Users).from_statement(text("SELECT * FROM users where name=:name")).params(name='lqz').all()



###更多操作
# 条件
# ret = session.query(Users).filter_by(name='lqz').all()
#表达式,and条件连接
# ret = session.query(Users).filter(Users.id > 1, Users.name == 'lqz').all()
# ret = session.query(Users).filter(Users.id.between(1, 2), Users.name == 'lqz1').all()
# #注意下划线
# ret = session.query(Users).filter(Users.id.in_([1,3,4])).all()
# #~非,除。。外
# ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all()
# #二次筛选
# ret = session.query(Users).filter(Users.id.in_(session.query(models.Person.nid).filter_by(name='张三'))).all()
from sqlalchemy import and_, or_
# #or_包裹的都是or条件,and_包裹的都是and条件
# ret = session.query(Users).filter(and_(Users.id > 1, Users.name == 'lqz')).all()
# ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'lqz')).all()
# ret = session.query(Users).filter(
# or_(
# Users.id < 2,
# and_(Users.name == '李易峰', Users.id > 2),
# )).all()
#
#
# # 通配符,以e开头,不以e开头
# ret = session.query(Users).filter(Users.name.like('l%')).all()
# ret = session.query(Users).filter(~Users.name.like('l%')).all()
#
# # 限制,用于分页,区间
# ret = session.query(Users)[0:2] #前闭后开
#
# # 排序,根据name降序排列(从大到小)
# ret = session.query(Users).order_by(Users.name.desc()).all()
# #第一个条件重复后,再按第二个条件升序排
# ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()
#
# # 分组
from sqlalchemy.sql import func
#
# ret = session.query(Users).group_by(Users.name).all()
# #分组之后取最大id,id之和,最小id


# select min(id) as mid_id ,max(id) as max_id,sum(id) as sum_id from user where id >10 groupby name having sum(id)>10

# valuse 在annotate前表示groupby
# filter 在annotate前表示where
# filter 在annotate后表示having
# valuse 在annotate后表示取字段
# User.objects.all().filter(id__gt=10).values('name').annotate(max_id=max(id),sum_id=sum(id),min_id=min(id)).filter(sum_id>10)


# 分组以后,只能拿分组字段和聚合函数字段
# ret = session.query(
# func.max(Users.id),
# func.sum(Users.id),
# func.min(Users.id),Users.name).group_by(Users.name).all()
# #haviing筛选
# ret = session.query(
# func.max(Users.id),
# func.sum(Users.id),
# func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all()
#
# # 连表(默认用forinkey关联)
# select * from person,hobby where person.hobby_id=hobby.id;

# ret = session.query(models.Person,models.Hobby).filter(models.Person.hobby_id == models.Hobby.id).all()
# #join表,默认是inner join
# select * from person inner join hobby on person.hobby_id=hobby.id

#select * from person inner join hobby on person.hobby_id=hobby.id where hobby.caption=篮球

# Person.objects.all().filter(hobby__caption='篮球')
# ret = session.query(models.Person).join(models.Hobby).filter(models.Hobby.caption=='篮球').all()
# #isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可

#select * from person left join hobby on person.hobby_id=hobby.id
# ret = session.query(models.Person).join(models.Hobby, isouter=True).all()

# ret = session.query(models.Hobby).join(models.Person, isouter=True).all()
# #打印原生sql
# aa=session.query(models.Person).join(models.Hobby)
# print(aa)
# # 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上

# ret = session.query(models.Person).join(models.Hobby,models.Person.hobby_id==models.Hobby.id, isouter=True)
# print(ret)


# print(ret)


session.commit() # 提交
session.close()

flask-sqlalchemy 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# flask-sqlalchemy:让flask更好的集成sqlalchemy
# flask_migrate:类似于django的makemigrations和migrate,因为sqlalchemy不支持表修改(删除,增加字段)
flask-migrate
python3 manage.py db init 初始化:只执行一次

python3 manage.py db migrate 等同于 makemigartions
python3 manage.py db upgrade 等同于migrate


## flask-sqlalchemy
-先导入,实例化得到一个对象
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
-所有表模型都继承 db.Model
-在视图函数中查询那个session对象
db.session


## 导出项目依赖
# 安装:pip3 install pipreqs
#导出:pipreqs . --encoding=utf8

爬虫

介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 爬虫流程
模拟发送http请求 ---> 解析数据(清洗数据) ---> 入库

# 百度、谷歌...(大爬虫)
百度搜索:输入关键字 ---> 搜的是百度的数据库 ---> 页面展示 ---> 点击具体内容 ---> 网页跳转

seo优化:主动让百度爬到你

sem:花钱做广告买关键词

# 爬虫协议
哪部分允许爬取,哪部分不允许爬取(https://www.csdn.net/robots.txt)

# python中爬虫相关内容
模拟发送http请求(requests,slenium) ---> 解析数据(清洗数据)(json、bs4...) --->入库
(文件、mysql、redis、Excel、MongoDB)
反爬:
封ip --- 代理池
封账号 --- cookie池
请求头中带特殊校验 --- 相应破解出那些字段
数据加密 --- js解析出加密方式,自行组装数据
html --- css反爬,字体反爬

requests库介绍

1
2
3
# requests模块,基于urllib3封装,方便的发出http请求

# pip install requests

requests发送get请求

  • 普通请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
res=requests.get('https://www.cnblogs.com/xiaoyuanqujing/articles/11805698.html')

print(res.text) # 返回的数据

search = input('请输入要搜索的内容:')
res = requests.get('https://www.baidu.com/s?wd=' + search,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36',
'Host': 'www.baidu.com',
})

print(res.text)
with open('search.html','w',encoding='utf-8') as f:
f.write(res.text)
  • 携带参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
response=requests.get('https://www.sogou.com/web',
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
},params={'query':'美女'})
print(response.text)
with open('search.html','w',encoding='utf-8') as f:
f.write(response.text)

# url编码和解码
from urllib.parse import quote,unquote
# res=quote('美女')
# print(res) #%E7%BE%8E%E5%A5%B3

res=unquote('%E7%BE%8E%E5%A5%B3')
print(res)


from urllib.parse import urlencode
res=urlencode({'wd':'美女','age':19},encoding='utf-8')
print(res)
  • 携带请求头
1
2
3
4
5
6
7
8
9
10
# 如果被做了反爬,但是用浏览器可以,一定是模拟的不像


header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36',
'Cookie':'BIDUPSID=185720F0FEA0DC697147E75D48AFB1D8; PSTM=1593942899; BAIDUID=185720F0FEA0DC69D0675C2EEDB05721:SL=0:NR=10:FG=1; sug=3; ORIGIN=0; bdime=0; sugstore=1; BD_UPN=12314753; __yjs_duid=1_61812ebe639caffca8271e1786971c8b1617936053918; BDUSS=lhDOTR6OWU0UWdmcFBLTDdxRUlqQXJtdFFjajlxfjFhVUpRLTNDNEd0VW51Uk5oSVFBQUFBJCQAAAAAAAAAAAEAAACwPo3XwM~E0Lqiyc-6o9Cjx~gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcs7GAnLOxgW; BDUSS_BFESS=lhDOTR6OWU0UWdmcFBLTDdxRUlqQXJtdFFjajlxfjFhVUpRLTNDNEd0VW51Uk5oSVFBQUFBJCQAAAAAAAAAAAEAAACwPo3XwM~E0Lqiyc-6o9Cjx~gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcs7GAnLOxgW; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDSFRCVID_BFESS=uQLOJeC62GhrmIcHWL4ru7XUvDmJR3TTH6aoUKcPVTiblG6zzmh3EG0Pbf8g0K4bdMXhogKK0eOTHkuF_2uxOjjg8UtVJeC6EG0Ptf8g0f5; H_BDCLCKID_SF_BFESS=tRk8oK-aJKvbfP0kKno_MtCsqxby26n9-Rb9aJ5y-J7nhMTz5Mn1DT_OQl_fXpQq5m3ion3vQpbZ8h5D34vW-fLRDmct-p5MQ26xKl0MLPbcsU5nBU4VhnkD2fnMBMPj5mOnaIQc3fAKftnOM46JehL3346-35543bRTLnLy5KJYMDFCjTA-D6QyeUbQa4JWHD6QB4TaajrjDnCrBPjUXUI82h5y05OkbmteaU3PJMnhMUna54ovynKZDnORXx745j5b-bA-Bh3tfKJKbPQ63ML1Db3JqP7M0aQtsCouan3oepvoD-oc3MvByPjdJJQOBKQB0KnGbUQkeq8CQft20b0EeMtjW6LEJJkO_D_atKvDqTrP-trf5DCShUFsWPKJB2Q-XPoO3KJZfqRhyhJIjpk0jn7P-tQiW5cpoMbgylRM8P3y0bb2DUA1y4vpK-ogQgTxoUJ2fnRJEUcGqj5Ah--ebPRiJPQ9Qg-qahQ7tt5W8ncFbT7l5hKpbt-q0x-jLTnhVn0MBCK0hD0wjT0-DjcM-Uv05-PXKCJ0X458HJOoDDvFqfbcy4LdjG5NeRvbLnc7-hRu2PKboM5Cbxbmj4Pu3-Aq54RIL5505tnqtMcNb-0xeJrhQfbQ0bjuqP-jW5Ta-qI-HR7JOpkxbfnxy-P0QRPH-Rv92DQMVU52QqcqEIQHQT3m5-5bbN3ht6IHJJIq_I82JIvbfP0k5R35hnjH-UIs-lorB2Q-5KL-3bnKDqTnyhJdjbD0jn7P-f3LWHue-UbdJJjoSqvn0hjxMtDjQNjEhtr3t2TxoUJt5DnJhhkm-4OYW-kebPRiJPQ9QgbWLlQ7tt5W8ncFbT7l5hKpbt-q0x-jLTnhVn0M5DK0HPonHjAKDjvP; delPer=0; BD_CK_SAM=1; PSINO=3; BAIDUID_BFESS=185720F0FEA0DC69D0675C2EEDB05721:SL=0:NR=10:FG=1; H_PS_PSSID=34300_34335_34273_31254_34377_33848_34092_34107_34111_26350_34360; COOKIE_SESSION=3698_4_8_9_18_23_0_1_7_3_0_11_2964_0_0_0_1628046671_1627963945_1628050426%7C9%23101_72_1627963943%7C9; H_PS_645EC=4707Tvcdepk6pvKnFnabHvwqrLGAFZiyVAOXDTeK8IdAgRrAQD714rlnFSA; BA_HECTOR=0ha5al208l240k2hf91ggk5e30q'}
res = requests.get('https://www.baidu.com/s?wd=帅哥',headers=header)
print(res.text)

with open('search.html','w',encoding='utf-8') as f:
f.write(res.text)
  • 携带cookie
1
2
3
4
res = requests.get('http://www.aa7a.cn/', headers={
# 'cookie': 'ECS_ID=b435f5897f41c2f2c322fa3065165c9fbc56ddd5; ECS[visit_times]=1; _jzqa=1.4423902263705487400.1628049030.1628049030.1628049030.1; _jzqc=1; _jzqy=1.1628049030.1628049030.1.jzqsr=baidu.-; _jzqckmp=1; UM_distinctid=17b0f48bcda509-0e53827f49b667-5e422810-1fa400-17b0f48bcdb54c; CNZZDATA4603183=cnzz_eid%3D1414483188-1628045968-null%26ntime%3D1628045968; Hm_lvt_c29657ca36c6c88e02fed9a397826038=1628049030; CNZZDATA1260462072=271803043-1628045968-null%7C1628045968; Qs_lvt_201322=1628049030; mediav=%7B%22eid%22%3A%22179539%22%2C%22ep%22%3A%22%22%2C%22vid%22%3A%22%22%2C%22ctn%22%3A%22%22%2C%22vvid%22%3A%22%22%2C%22_mvnf%22%3A1%2C%22_mvctn%22%3A0%2C%22_mvck%22%3A1%2C%22_refnf%22%3A0%7D; _qzjc=1; __xsptplusUT_422=1; __xsptplus422=422.1.1628049032.1628049032.1%234%7C%7C%7C%7C%7C%23%23HXwWiieCoDk4evQa5H5dKIEBtnxLTY12%23; ECS[username]=616564099%40qq.com; ECS[user_id]=61399; ECS[password]=4a5e6ce9d1aba9de9b31abdf303bbdc2; _qzja=1.1591066928.1628049030327.1628049030327.1628049030327.1628049032756.1628049055229.616564099%2540qq_com.1.0.3.1; _qzjb=1.1628049030327.3.0.0.0; _qzjto=3.1.0; _jzqb=1.8.10.1628049030.1; Qs_pv_201322=2246468261428716000%2C1231523507243942000; Hm_lpvt_c29657ca36c6c88e02fed9a397826038=1628049055; cto_bundle=Gd60IF9TJTJGRHFuTzdidXZYZGEyVW9ydFFJV25YY0RqSlBRODRlTDdjSG9RT01NUlg4NmYyVjhPMzNmenolMkJDMlRiQjJWTHA2UlBoUUdNOGtBTnoyTkZqdmJMOEI5Vk14aVU4JTJGbHdyTXFqaCUyRlY1dWt3JTNE'
})
print('616564099@qq.com ' in res.text)

requests发送post请求

  • 自动登录某网站
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

res = requests.post('http://www.aa7a.cn/user.php', data={
'username': '616564099@qq.com',
'password': 'lqz123',
'captcha': 'zxv7',
'remember': 1,
'ref': 'http://www.aa7a.cn/',
'act': 'act_login'
})

# print(res.text)
## 取出cookie,登录成功的cookie
cookie=res.cookies # CookieJar对象
print(cookie)


res2=requests.get('http://www.aa7a.cn/',cookies=cookie)
# res2=requests.get('http://www.aa7a.cn/')
print('616564099@qq.com' in res2.text)
  • body体中携带数据
1
2
3
4
5
6
### 6 body体携带数据
# res = requests.post('',data={}) # urlencoded方式
# res = requests.post('',json='json格式字符串') # aplication/json方式
# res = requests.post('',json='',headers={
# 'content-type': 'application/json;charset=utf-8'
# })
  • response属性、编码问题,获取二进制,解析json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
## 7  response属性,


# respone=requests.get('http://www.aa7a.cn/')
#
#
# print(respone.text) # 响应体的字符串
# print('----------------------------------')
# print(respone.content) # 响应体的二进制(图片,视频,页面)
# print('----------------------------------')
# print(respone.status_code) # 响应的状态码
# print(respone.headers) # 响应头
# print(respone.cookies) # 返回的cookie
# print(respone.cookies.get_dict()) # cookieJar对象转成字典
# print(respone.cookies.items()) # 相当于字典的items
#
# print(respone.url) # 当次请求地址
# print(respone.history) # 重定向过才有值
#
# print(respone.encoding) # 响应的编码格式

#关闭:response.close()
# from contextlib import closing
# with closing(requests.get('xxx',stream=True)) as response:
# for line in response.iter_content():
# pass


# 8 编码问题,
# 可能会遇到打印respone.text出现乱码,在浏览器页面中看不会出现乱码
# respone=requests.get('http://www.aa7a.cn/')
# # respone.encoding='gbk' # 修改编码方式
# respone.encoding=respone.apparent_encoding # 页面使用的编码方式
# print(respone.text) # 响应体的字符串



# 9 获取二进制,
res=requests.get('http://www.aa7a.cn/data/afficheimg/20201102gophex.png')
# print(res.content)
# with open('致命诱惑.png','wb') as f:
# f.write(res.content)


# with open('致命诱惑.png','wb') as f:
# for line in res.iter_content(1024):
# f.write(line)



# 10 解析json
# import json
# res=requests.get('https://api.luffycity.com/api/v1/course/category/actual/?courseType=actual')
# # print(json.loads(res.text))
#
# print(res.json())

爬取视频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#  爬取视频
#https://www.pearvideo.com/category_loading.jsp?reqType=5&categoryId=5&start=0

# import re
# res=requests.get('https://www.pearvideo.com/category_loading.jsp?reqType=5&categoryId=5&start=0')
#
# # print(res.text)
# # 如果使用bs4,非常简单
#
# video_list=re.findall('<a href="(.*?)" class="vervideo-lilink actplay">',res.text)
# # print(video_list)
# for video in video_list:
# video_url='https://www.pearvideo.com/'+video
# # print(video_url)
# video_id=video.split('_')[-1]
#
# header={
# 'Referer':video_url
# }
#
# res2=requests.get('https://www.pearvideo.com/videoStatus.jsp?contId=%s&mrd=0.5165499193941832'%video_id,headers=header)
#
# video_f_url=res2.json()['videoInfo']['videos']['srcUrl']
# video_real_url=video_f_url.replace(video_f_url.rsplit('/')[-1].split('-')[0], 'cont-%s' % video_id)
# print(video_real_url)
#
# res3=requests.get(video_real_url)
# with open('%s.mp4'%video_id,'wb') as f:
# for line in res3.iter_content(1024):
# f.write(line)









# 分析过程稿
# referer:上一次访问的地址,可以做图片防盗链
# header={
# 'Referer': 'https://www.pearvideo.com/video_1737590'
# }
#
# res=requests.get('https://www.pearvideo.com/videoStatus.jsp?contId=1737590&mrd=0.5165499193941832',headers=header)
# print(res.text)



## 可以播放的视频
# 'https://video.pearvideo.com/mp4/short/20210729/cont-1736870-15732687-hd.mp4'
# ## 不可以播放的视频
# 'https://video.pearvideo.com/mp4/short/20210729/1628062847275-15732687-hd.mp4'
#
#
# 'https://video.pearvideo.com/mp4/short/20210729/ cont-1736870 -15732687-hd.mp4'
# 'https://video.pearvideo.com/mp4/short/20210729/ 1628062847275 -15732687-hd.mp4'
#
# s='https://video.pearvideo.com/mp4/short/20210729/ 1628062847275 -15732687-hd.mp4'
# s.replace(s.rsplit('/')[-1].split('-')[0],'cont-%s'%video_id)

补充

1
2
# 长链转短链服务
核心:重定向

requests高级用法

  • 补充
1
2
3
4
5
1 正向代理和反向代理
正向代理:代理客户端
反向代理:代理服务端(nginx就是反向代理服务器)

2 requests使用的代理:正向代理
  • SSL Cert Verification(了解)
1
2
3
4
5
6
7
8
9
10
11
12
## 不验证证书
# import requests
# respone=requests.get('https://www.12306.cn',verify=False) #不验证证书,报警告,返回200
# print(respone.status_code)
#
#
# ## 携带证书
# import requests
# respone=requests.get('https://www.12306.cn',
# cert=('/path/server.crt',
# '/path/key'))
# print(respone.status_code)
  • 使用代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# import requests
# proxies = {
# 'http':'http://117.69.230.132:3256',
# }
# respone=requests.get('https://www.12306.cn',
# proxies=proxies)
#
# print(respone.status_code)

# import requests
# proxies = {
# 'http':'http://117.69.230.132:3256',
# }
# # respone=requests.get('http://127.0.0.1:8000',proxies=proxies)
# respone=requests.get('http://127.0.0.1:8000')
#
# print(respone.text)


# import requests
# proxies = {
# 'http':'http://103.228.245.98:3128',
# }
# respone=requests.get('http://101.133.225.166:8888/',proxies=proxies)
# # respone=requests.get('http://101.133.225.166:8888/')
#
# print(respone.text)

## 如果你有很多代理,每次发请求,随机取一个代理ip,发送,这样我们的ip就不会被封
### 花钱买
### 白嫖
# import requests
# res=requests.get('http://demo.spiderpy.cn/get/').json()['proxy']
# print(res)
#
# proxies = {
# 'https':'https://%s'%res,
# }
# print()
#
# respone=requests.get('http://www.baidu.com',proxies=proxies)
# # respone=requests.get('http://www.baidu.com')

# print(respone.text)



### 借助于第三方,自己搭建(读一读人家源码)
#https://github.com/jhao104/proxy_pool
  • 超时时间
1
# respone=requests.get('https://www.baidu.com',timeout=0.0001)
  • 认证(像老款路由器的登录)
1
2
3
4
# import requests
# from requests.auth import HTTPBasicAuth
# r=requests.get('xxx',auth=HTTPBasicAuth('user','password'))
# print(r.status_code)
  • 异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# import requests
# from requests.exceptions import * #可以查看requests.exceptions获取异常类型
#
# try:
# r=requests.get('http://www.baidu.com',timeout=0.00001)
# # except ReadTimeout:
# # print('===:')
# # except ConnectionError: #网络不通
# # print('-----')
# # except Timeout:
# # print('aaaaa')
#
# except Exception:
# print('Error')
  • 文件上传
1
2
3
4
# import requests
# files={'myfile':open('1 自动处理cookie.py','rb')}
# respone=requests.post('http://127.0.0.1:8000/upload_file/',files=files)
# print(respone.text)

抽屉自动点赞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

import requests

header={
'Cookie':'',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'
}
# res=requests.post('https://dig.chouti.com/link/vote',data={'linkId':'31857081'},headers=header)
# print(res.text)


# 所有的都点赞,----》id解析---》bs4模块(解析xml)

res=requests.get('https://dig.chouti.com/top/24hr?_=1628136305346',headers=header).json()
for item in res['data']:
id=item['id']
res=requests.post('https://dig.chouti.com/link/vote',data={'linkId':'%s'%id},headers=header)
print(res.text)

爬取汽车之家新闻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# pip3 install beautifulsoup4
import requests
from bs4 import BeautifulSoup

for i in range(1,100):
res=requests.get('https://www.autohome.com.cn/news/%s/#liststart'%i)
# print(res.text)
# 第一个参数,要解析的内容,第二参数:使用的解析器 html.parser bs4内置的解析器 lxml
soup=BeautifulSoup(res.text,'html.parser')

# pip3 install lxml
# soup=BeautifulSoup(res.text,'lxml')


# find_all找所有
ul_list=soup.find_all(name='ul',class_='article')
# ul_list=soup.find_all(name='ul')
# print(len(ul_list))

for ul in ul_list:
li_list=ul.find_all(name='li')
for li in li_list:
h3=li.find(name='h3')
if h3:
title=h3.text # 获取标签的文本内容,标签对象.text
# print(title)
desc=li.find(name='p').text
# print(desc)
img_url=li.find(name='img')['src']
if not img_url.startswith('http'):
img_url='https:'+img_url
# print(img_url)
url='https:'+li.find(name='a')['href']
print(url)

print('''
新闻标题:%s
新闻摘要:%s
新闻图片:%s
新闻地址:%s
'''%(title,desc,img_url,url))

bs4遍历文档树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98



from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story <span>lqz</span></b><span>egon</span></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
soup=BeautifulSoup(html_doc,'html.parser')


# res=soup.prettify() # 美化
# print(res)

#1、用法
# html=soup.html
# title=soup.html.head.title
# title=soup.title
# print(title)



#2、获取标签的名称 ---> 标签对象.name
# a=soup.body.a
# a=soup.a.name
# print(a)
# print(soup.body.name)


#3、获取标签的属性 ---->标签对象['标签名']
# href=soup.body.a['href']
# attrs=soup.body.a.attrs # 所有属性,---》字典
# href=soup.body.a.attrs['href']
# print(attrs['class'])

# c=soup.p.attrs['class']
# print(c)

#4、获取标签的内容

# res=soup.b.text # 拿到当前标签子子孙所有的text
# res=soup.p.text

# res=soup.p.string # 当前标签有且只有一个文本内容才能拿出来
# res=soup.b.string # 当前标签有且只有一个文本内容才能拿出来

# res=soup.p.strings # 把子子孙放到生成器中
#
# print(list(res))



#5、嵌套选择
# res=soup.html.body.p
# print(type(res)) # bs4.element.Tag
from bs4.element import Tag


####了解
#6、子节点、子孙节点
# print(soup.p.contents) #p下所有子节点,放到列表中

# print(soup.p.children) #得到一个迭代器,包含p下所有子节点

# for i,child in enumerate(soup.p.children):
# print(i,child)

# print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来
# for i,child in enumerate(soup.p.descendants):
# print(i,child)


#7、父节点、祖先节点

# print(soup.a.parent) #获取a标签的父节点

# print(soup.body.parent)

# print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
# print(list(soup.a.parents))
# print(len(list(soup.a.parents)))


#8、兄弟节点
# print(soup.a.next_sibling) #下一个兄弟
# print(soup.a.previous_sibling) #上一个兄弟
#
# print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
# print(list(soup.a.previous_siblings)) #上面的兄弟们=>生成器对象

bs4搜索文档树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body id='body'>
<p class="title"><b>The Dormouse's story <span>lqz</span></b><span>egon</span></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
soup=BeautifulSoup(html_doc,'html.parser')


# 搜索文档树 find find_all

# 五种过滤器: 字符串、正则表达式、列表、True、方法


##### 字符串
# res=soup.find(name='body')
# res=soup.find(name='p',class_='story')

# 查找id为link2的标签
# res=soup.find(id='link2',name='a',class_='sister',href='http://example.com/lacie')
# res=soup.find(href='http://example.com/lacie')
# print(res)

# res=soup.find(attrs={'class':['sister']})
# print(res)


#### 正则表达式
import re
# res=soup.find_all(name=re.compile('^b')) #找出b开头的标签,结果有body和b标签
# res=soup.find(name=re.compile('^b'))


# res=soup.find_all(class_=re.compile('^s'))
# res=soup.find_all(href=re.compile('^http'))
# res=soup.find_all(id=re.compile('^l'))
# print(res)


####列表、

# res=soup.find_all(name=['body','b'])
# res=soup.find_all(id=['link1','link2'])

# res=soup.find_all(attrs={'id':['link1','link2']})
#
# print(res)

# True、

# links=soup.find_all(href=True)
# print(links)

# res=soup.find_all(name=True)
# res=soup.find_all(id=True)
# print(res)



#方法
# def has_class_but_no_id(tag):
# return tag.has_attr('class') and not tag.has_attr('id')
#
# print(len(soup.find_all(name=has_class_but_no_id)))


# 拿出当前页面所有图片
soup.find_all(name='img',href=True)



## 建议 遍历文档树和搜索文档树混用
# soup.body.div.find




### 其他参数 find,find_all

#limit
# soup.find()
# res=soup.find_all(name='a',href=True,limit=2) # 限制获取的条数
# print(res)


# recursive 是否递归查找
# res=soup.find_all(name='a',recursive=False)
# res=soup.find_all(name='html',recursive=False)
# print(res)

css选择器(与xpath是通用的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title">
<b>The Dormouse's story <p>asdfasdf</p></b>
Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">
<span>Elsie</span>
</a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
<div class='panel-1'>
<ul class='list' id='list-1'>
<li class='element'>Foo</li>
<li class='element'>Bar</li>
<li class='element'>Jay</li>
</ul>
<ul class='list list-small' id='list-2'>
<li class='element'><h1 class='yyyy'>Foo</h1></li>
<li class='element xxx'>Bar</li>
<li class='element'>Jay</li>
</ul>
</div>
and they lived at the bottom of a well.
</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'html.parser')


'''
#id
.类名
标签
标签>标签
标签 标签
'''

# res=soup.p.select('.sister') # 使用css选择器
# res=soup.p.select('#link1') # 使用css选择器
# res=soup.select('body>p') # 使用css选择器 body的子标签p
res=soup.select('body p') # 使用css选择器 body的子子孙孙标签p
print(len(res))


### css选择器是通用的:bs4,lxml解析也可以是css选择器

##css选择器不会写怎么办?
'#maincontent > div:nth-child(3) > table > tbody > tr:nth-child(13) > td:nth-child(3)'

## xpath选择
'//*[@id="maincontent"]/div[2]/table/tbody/tr[18]/td[2]'

selenium使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36


# 如果使用requests模块,发送请求获取的数据不全,它不能执行js

# selenium:可以使用代码控制模拟人操作浏览器


## 操作某个浏览器,就需要有浏览器驱动
# http://npm.taobao.org/mirrors/chromedriver/ 谷歌驱动的淘宝镜像站
# 谷歌浏览器版本要跟驱动版本对应

## 92.0.4515.131 下载相应版本驱动,放到项目代码中

# pip3 install selenium

# from selenium import webdriver
# import time
# # 打开一个谷歌浏览器
# bro=webdriver.Chrome(executable_path='chromedriver.exe')
#
# #地址栏中输入百度
# bro.get('https://www.cnblogs.com/')
#
# time.sleep(2)
#
# print(bro.page_source) #当前页面的html内容
#
# bro.close() # 关闭浏览器


# import requests
#
# res=requests.get('https://dig.chouti.com/',headers={
# 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
# })
# print(res.text)

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from selenium import webdriver
import time

# 浏览器对象
bro = webdriver.Chrome(executable_path='chromedriver.exe')
bro.implicitly_wait(10) # 隐式等待,去找控件,如果没有会等10s

bro.get('https://www.baidu.com/')

# sub_button=bro.find_element_by_css_selector('#s-top-loginbtn')
sub_button = bro.find_element_by_id('s-top-loginbtn') # 如果有id,优先用它
# 点击
sub_button.click()

# 找到用户名密码登录
user_btn = bro.find_element_by_xpath('//*[@id="TANGRAM__PSP_11__footerULoginBtn"]')
# user_btn=bro.find_element_by_id('TANGRAM__PSP_11__footerULoginBtn')
user_btn.click()

username = bro.find_element_by_id('TANGRAM__PSP_11__userName')
password = bro.find_element_by_id('TANGRAM__PSP_11__password')

# 往输入框中写东西
username.send_keys('6666666@qq.com')
password.send_keys('lqz12345')

sumbit_btn = bro.find_element_by_id('TANGRAM__PSP_11__submit')
time.sleep(3)
sumbit_btn.click()

time.sleep(3)
bro.close()

无头浏览器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18



from selenium import webdriver

from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('window-size=1920x3000') #指定浏览器分辨率
chrome_options.add_argument('--disable-gpu') #谷歌文档提到需要加上这个属性来规避bug
chrome_options.add_argument('--hide-scrollbars') #隐藏滚动条, 应对一些特殊页面
chrome_options.add_argument('blink-settings=imagesEnabled=false') #不加载图片, 提升速度
chrome_options.add_argument('--headless') #浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败


driver=webdriver.Chrome(executable_path='chromedriver.exe',chrome_options=chrome_options)
driver.get('https://www.baidu.com')
print(driver.page_source)
driver.close()

获取元素位置,属性,大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from selenium import webdriver
import time
driver=webdriver.Chrome(executable_path='chromedriver.exe')
driver.get('https://kyfw.12306.cn/otn/resources/login.html')
driver.implicitly_wait(10)

user_login=driver.find_element_by_css_selector('.login-hd-account>a')

user_login.click()
time.sleep(2)
img=driver.find_element_by_id('J-loginImg')
print(img)

print(img.id) #selenium提供的id,忽略
print(img.tag_name) # 标签名



print('-----')
print(img.location) # img标签的位置
print(img.size) # img标签大小

# 获取属性
# print(img.get_attribute('src'))
print(img.get_attribute('class'))

driver.close()

等待元素被加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from selenium import webdriver

# 两种等待方式
# 显示等待
# 隐式等待:只需要写一句话,等待所有要获取的标签

driver=webdriver.Chrome(executable_path='chromedriver.exe')
driver.get('https://www.baidu.com')
'''
# 两种等待方式
# 显示等待(忽略掉)
wait=WebDriverWait(driver,10)
wait.until(EC.presence_of_element_located((By.ID,'content_left')))
contents=browser.find_element(By.CSS_SELECTOR,'#content_left')
# 隐式等待:
-driver.implicitly_wait(10)
-driver.find_element_by_css_selector()
-只需要写一句话,等待所有要获取的标签

'''

driver.implicitly_wait(10)


print(driver.page_source)
# 再找控件,只要没加载成功,就会等待,最多等10s
driver.close()

元素操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from selenium import webdriver
import time

driver=webdriver.Chrome(executable_path='chromedriver.exe')
driver.get('https://www.baidu.com')
driver.implicitly_wait(10)

## 点击,清空,输入操作

input_search=driver.find_element_by_id('kw')
input_search.send_keys('美女') # 输入
time.sleep(3)
input_search.clear() # 清空

time.sleep(2)
input_search.send_keys('性感美女')
time.sleep(2)
btn=driver.find_element_by_id('su')
btn.click() # 点击
time.sleep(10)

driver.close()

执行js

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
import time

driver=webdriver.Chrome(executable_path='chromedriver.exe')
driver.get('http://127.0.0.1:8000/')
driver.implicitly_wait(10)

driver.execute_script("name='egon';") # 这里面写js代码
driver.execute_script("alert(name)") # 这里面写js代码


time.sleep(5)
# driver.close()

切换选项卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
from selenium import webdriver

browser=webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')

print(browser.window_handles) #获取所有的选项卡
# browser.switch_to_window(browser.window_handles[1])
# browser.switch_to_window(browser.window_handles[1])
browser.switch_to.window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(5)
# browser.switch_to_window(browser.window_handles[0])
browser.switch_to.window(browser.window_handles[0])
browser.get('https://www.sina.com.cn')
browser.close()

模拟前进后退

1
2
3
4
5
6
7
8
9
10
11
12
13

import time
from selenium import webdriver

browser=webdriver.Chrome(executable_path='chromedriver.exe')
browser.get('https://www.baidu.com')
browser.get('https://www.taobao.com')
browser.get('http://www.sina.com.cn/')

browser.back()
time.sleep(3)
browser.forward()
browser.close()

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13

from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException

browser = webdriver.Chrome()
try:
browser.get('http://www.baidu.com')

except Exception as e:
print(e)
finally:
browser.close()

selenium登录cnblogs获取cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#selenium登录cnblogs获取cookie
from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException
import time
import json
browser = webdriver.Chrome(executable_path='chromedriver.exe')
browser.implicitly_wait(10)

#### 登录过程
# try:
# browser.get('http://www.cnblogs.com')
# submit_btn=browser.find_element_by_link_text('登录') # a标签的内容
# submit_btn.click()
#
# username=browser.find_element_by_id('mat-input-0')
# password=browser.find_element_by_id('mat-input-1')
# username.send_keys('616564099@qq.com')
# password.send_keys('1111')
# input('等会')
# sub_btn=browser.find_element_by_css_selector('body > app-root > mat-sidenav-container > mat-sidenav-content > div > div > app-sign-in > app-content-container > div > div > div > form > div > button > span.mat-button-wrapper')
# sub_btn.click()
#
# # 人工参与,滑动
# input('等会')
#
# # 获取到登录后的cookie
# print(browser.get_cookies())
#
# with open('cookie.json','w') as f:
# json.dump(browser.get_cookies(),f)
#
#
# except Exception as e:
# print(e)
# finally:
# browser.close()


### 不登录了,把cookie写入浏览器
# browser.get('http://www.cnblogs.com')
# with open('cookie.json','r') as f:
# cookie=json.load(f)
# time.sleep(5)
# for item in cookie: # 设置cookie必须用字典,cookie的json文件是列表,所以用循环往里放
# browser.add_cookie(item)
#
#
#
# browser.refresh() # 刷新页面
#
# time.sleep(5)
#
# browser.close()

抽屉半自动点赞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


from selenium import webdriver
import json
import time

#### 登录过程
# bro=webdriver.Chrome(executable_path='chromedriver.exe')
# bro.implicitly_wait(10)
# bro.get('https://dig.chouti.com/')
# try:
# sub_btn=bro.find_element_by_id('login_btn')
# print(sub_btn)
#
# # sub_btn.click() # 报错
# bro.execute_script('arguments[0].click();',sub_btn)
#
# # username=bro.find_element_by_css_selector('body > div.login-dialog.dialog.animated2.scaleIn > div > div.login-body > div.form-item.login-item.clearfix.phone-item.mt24 > div.input-item.input-item-short.left.clearfix > input')
# username=bro.find_element_by_css_selector('div.input-item>input.login-phone')
# username.send_keys('18953675221')
# # password=bro.find_element_by_css_selector('body > div.login-dialog.dialog.animated2.scaleIn > div > div.login-footer > div.form-item.login-item.clearfix.mt24 > div')
# password = bro.find_element_by_css_selector('div.input-item>input.pwd-password-input')
# password.send_keys('lqz123')
#
# time.sleep(3)
# btn=bro.find_element_by_css_selector('body > div.login-dialog.dialog.animated2.scaleIn > div > div.login-footer > div:nth-child(4) > button')
#
# btn.click()
#
# input('等')
#
# with open('chouti.json','w') as f:
# json.dump(bro.get_cookies(),f)
#
#
#
#
# finally:
# bro.close()
import requests

bro=webdriver.Chrome(executable_path='chromedriver.exe')
bro.implicitly_wait(10)
bro.get('https://dig.chouti.com/')



# 把屏幕滑倒最底下
bro.execute_script('window.scrollTo(0, document.body.scrollHeight);')
# bro.find_elements_by_css_selector('.link-item')
cookie={}
##从文件中读出cookie
with open('chouti.json','r') as f:
res=json.load(f)
for item in res:
cookie[item['name']]=item['value']

print(cookie) # requests能够使用的cookie


div= bro.find_element_by_class_name('link-con')
time.sleep(2)
header={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
div_list=div.find_elements_by_class_name('link-item')
for div in div_list:
article_id=div.get_attribute('data-id')
print(article_id)
# 使用requests发送请求
res=requests.post('https://dig.chouti.com/link/vote',data={'linkId': article_id},cookies=cookie,headers=header)
print(res.text)
bro.close()

打码平台使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 人工破解
# 图像识别模块---》数字,字母组合
# 验证码破解平台---》云打码,超级鹰
-给它一张图片---》结果返回 (收费的)



#!/usr/bin/env python
# coding:utf-8

import requests
from hashlib import md5


class Chaojiying_Client(object):

def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}

def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
headers=self.headers)
return r.json()

def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()


if __name__ == '__main__':
chaojiying = Chaojiying_Client('306334678', 'lqz12345', '903641') # 用户中心>>软件ID 生成一个替换 96001
im = open('a.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 1902)) # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()

xpath使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
1 一门在html中查找数据的语言
2 记住的语法:
/ 取当前路径下的xx
// 取所有路径下的xx
. 当前路径
.. 上一层
@ 取属性

4 lxml解析模块提供的xpath
doc='''
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html' name='sss'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
<a href='image2.html' name='lqz'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html' class='li'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html' class='li li-item' name='items'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
<a href='image6.html' name='items'><span><h5>test</h5></span>Name: My image 6 <br /><img src='image6_thumb.jpg' /></a>
</div>
</body>
</html>
'''
from lxml import etree

# 传入要解析的内容
html=etree.HTML(doc)

# res=html.xpath('//body')
# print(res)

# 1 所有节点
# a=html.xpath('//*')




# 2 指定节点(结果为列表)
# a=html.xpath('//head')
# 3 子节点,子孙节点
# a=html.xpath('//div/a')
# a=html.xpath('//body//a') #无数据
# a=html.xpath('//body//a')
# 4 父节点
# a=html.xpath('//body//a[@href="image1.html"]/..')
# a=html.xpath('//body//a')
# a=html.xpath('//body//a[@href="image1.html"]')
# a=html.xpath('//body//a[1]/..')
# 也可以这样
# a=html.xpath('//body//a[1]/parent::*')
# a=html.xpath('//body//a[1]/parent::p')
# 5 属性匹配
# a=html.xpath('//a[@href="image1.html"]')
# a=html.xpath('//a[@name="sss"]')

# 6 文本获取 text()
# a=html.xpath('//a[@href="image1.html"]/text()')
# a=html.xpath('//a/text()')

# 7 属性获取
# a=html.xpath('//a/@href')
# a=html.xpath('//a[1]/@name')
# # 注意从1 开始取(不是从0)
# a=html.xpath('//body//a[2]/@href')
# 8 属性多值匹配
# a 标签有多个class类,直接匹配就不可以了,需要用contains
# a=html.xpath('//a[@class="li"]')
# a=html.xpath('//a[contains(@class,"li")]')
# a=html.xpath('//body//a[contains(@class,"li")]/text()')
# 9 多属性匹配
# a=html.xpath('//body//a[contains(@class,"li") or @name="items"]')
# a=html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')
# a=html.xpath('//body//a[contains(@class,"li")]/text()')
# 10 按序选择
# a=html.xpath('//a[2]/text()')
# a=html.xpath('//a[2]/@href')
# a=html.xpath('//a[2]/@name')
# 取最后一个
# a=html.xpath('//a[last()]/@href')
# 位置小于3的
# a=html.xpath('//a[position()<3]/@href')
# 倒数第二个
# a=html.xpath('//a[last()-2]/@href')
# 11 节点轴选择
# ancestor:祖先节点
# 使用了* 获取所有祖先节点
# a=html.xpath('//a/ancestor::*')
# # 获取祖先节点中的div
# a=html.xpath('//a/ancestor::div')
# attribute:属性值
# a=html.xpath('//a[1]/attribute::*')
# child:直接子节点
# a=html.xpath('//a[1]/child::*')
# a=html.xpath('//a[1]/child::img/@src')
# descendant:所有子孙节点
# a=html.xpath('//a[6]/descendant::*')
# following:当前节点之后所有节点
# a=html.xpath('//a[1]/following::*')
# a=html.xpath('//a[1]/following::*[1]/@href')
# following-sibling:当前节点之后同级节点
# a=html.xpath('//a[1]/following-sibling::*')
# a=html.xpath('//a[1]/following-sibling::a')
# a=html.xpath('//a[1]/following-sibling::*[2]/text()')
# a=html.xpath('//a[1]/following-sibling::*[2]/@href')

print(a)

自动登录12306

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

from selenium import webdriver
import base64
from PIL import Image
import time
from chaojiying import Chaojiying_Client
from selenium.webdriver import ActionChains


# 不让程序检测出是用驱动控制
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")
bro=webdriver.Chrome(executable_path='./chromedriver.exe',chrome_options=options)

bro.get('https://kyfw.12306.cn/otn/resources/login.html')

bro.implicitly_wait(10)
# 把窗口设置全屏
bro.maximize_window()

try:
username_login_btn=bro.find_element_by_css_selector('.login-hd-account>a')
username_login_btn.click()

username=bro.find_element_by_id('J-userName')
password=bro.find_element_by_id('J-password')
login_btn=bro.find_element_by_id('J-login')
username.send_keys('liuqingzheng')
password.send_keys('lqz12345')


img_code=bro.find_element_by_id('J-loginImg')
print(img_code.size)
print(img_code.location)
# 获取验证码图片的两种方案
# 方案一:整体截图,根据位置抠出验证码图片
# bro.save_screenshot('main.png') # 对整个页面进行截图,main.png
#
# location=img_code.location
# size=img_code.size
# print(location)
# print(size)
# #验证码的坐标
# img_tu = (int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
# #使用pillow打开截图
# img=Image.open('./main.png')
# #从截图中按照位置扣除验证码
# code_img=img.crop(img_tu)
# # 把扣出来的图,保存到本地
# code_img.save('./code2.png')

# 方案二:把图片的base64编码转成图片保存到本地
img_base64=img_code.get_attribute('src')
img_base64_real=img_base64.split(',')[-1]
img_1=base64.b64decode(img_base64_real)
with open('code.jpg','wb') as f:
f.write(img_1)


# 调用超级鹰,完成验证码破解
# 调用超级鹰识别
chaojiying = Chaojiying_Client('306334678', 'lqz12345', '903641') # 用户中心>>软件ID 生成一个替换 96001
im = open('code.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
res=chaojiying.PostPic(im, 9004) # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
# 123,155|42,135|11,77---->[[123,155],[42,135],[11,77]]
print(res)
result=res['pic_str']
all_list = []
if '|' in result:
list_1 = result.split('|')
count_1 = len(list_1)
for i in range(count_1):
xy_list = []
x = int(list_1[i].split(',')[0])
y = int(list_1[i].split(',')[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
x = int(result.split(',')[0])
y = int(result.split(',')[1])
xy_list = []
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
print(all_list)


### 在页面中点击破解的图案
#点击 [[123,155],[42,135],[11,77]]
for item in all_list:
ActionChains(bro).move_to_element_with_offset(img_code,item[0],item[1]).click().perform()
time.sleep(1)

time.sleep(5)
login_btn.click()
time.sleep(1)

# 滑动滑块
span=bro.find_element_by_id('nc_1_n1z')
ActionChains(bro).drag_and_drop_by_offset(span, 300, 0).perform()

time.sleep(30)

print(bro.get_cookies())
except Exception as e:
print(e)
finally:
bro.close()
bro.quit() # 关闭整个浏览器

项目上线(裸机)

项目部署架构

image-20210804191237777

补充

  • kvm
  • vmare
  • OpenStack
  • docker
  • k8s

云服务器安装mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
yum update -y  #更新软件包
yum -y groupinstall "Development tools" # 装一个开发工具包(开发相关的一些软件,git)
yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel # 依赖,
git #验证git装完


1)前往用户根目录
>: cd ~

2)下载mysql57
>: wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm

也可以本地上传,这条命令要在本地终端上执行
>: scp .\mysql57-community-release-el7-10.noarch.rpm root@106.14.137.210:/root

3)安装mysql57
>: yum -y install mysql57-community-release-el7-10.noarch.rpm
>: yum -y install mysql-community-server

4)启动mysql57并查看启动状态
>: systemctl start mysqld.service
>: systemctl status mysqld.service

5)查看默认密码并登录
>: grep "password" /var/log/mysqld.log
xdjj(/qgj0A4

>: mysql -uroot -p

6)修改密码
>: ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';
>: ALTER USER 'root'@'localhost' IDENTIFIED BY 'Jerry123?';

云服务器安装redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1)前往用户根目录
>: cd ~

2)下载redis-5.0.5
>: wget http://download.redis.io/releases/redis-5.0.5.tar.gz #源码包,c语言源码

3)解压安装包
>: tar -xf redis-5.0.5.tar.gz

4)进入目标文件
>: cd redis-5.0.5

5)编译环境
>: make

6)复制环境到指定路径完成安装
>: cp -r ~/redis-5.0.5 /usr/local/redis

7)配置redis可以后台启动:修改下方内容
>: vim /usr/local/redis/redis.conf

daemonize yes

8)完成配置修改
>: esc
>: :wq

9)建立软连接
>: ln -s /usr/local/redis/src/redis-server /usr/bin/redis-server
>: ln -s /usr/local/redis/src/redis-cli /usr/bin/redis-cli

10)后台运行redis
>: cd /usr/local/redis
>: redis-server ./redis.conf &

ctrl + c

11)测试redis环境
>: redis-cli
ctrl + c

12)关闭redis服务
>: pkill -f redis -9

云服务器安装nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1)前往用户根目录
>: cd ~

2)下载nginx1.13.7
>: wget http://nginx.org/download/nginx-1.13.7.tar.gz

3)解压安装包
>: tar -xf nginx-1.13.7.tar.gz

4)进入目标文件
>: cd nginx-1.13.7

5)配置安装路径:/usr/local/nginx
>: ./configure --prefix=/usr/local/nginx

6)编译并安装
>: make && sudo make install

7)建立软连接:终端命令 nginx
>: ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

8)删除安装包与文件:
>: cd ~
>: rm -rf nginx-1.13.7
>: rm -rf nginx-1.13.7.tar.xz

9)测试Nginx环境,服务器运行nginx,本地访问服务器ip
>: nginx
>: 服务器绑定的域名 或 ip:80



# 命令
1)启动
>: nginx
2)关闭nginx
>: nginx -s stop
3)重启nginx
>: nginx -s reload
4)查看端口,强行关闭
>: ps -aux|grep nginx
>: kill <pid:进程编号>

安装python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1)前往用户根目录
>: cd ~

2)下载 或 上传 Python3.7.7
# 服务器终端
>: wget https://www.python.org/ftp/python/3.7.7/Python-3.7.7.tar.xz

3)解压安装包
>: tar -xf Python-3.7.7.tar.xz

4)进入目标文件
>: cd Python-3.7.7

5)配置安装路径:/usr/local/python3
>: ./configure --prefix=/usr/local/python3.7

6)编译并安装
yum -y install zlib*
yum install libffi-devel
>: make && sudo make install # 把报错复制到百度里


7)建立软连接:终端命令 python3,pip3
>: ln -s /usr/local/python3.7/bin/python3.7 /usr/bin/python3.7
>: ln -s /usr/local/python3.7/bin/pip3.7 /usr/bin/pip3.7

8)删除安装包与文件:
>: rm -rf Python-3.7.7
>: rm -rf Python-3.7.7.tar.xz

安装uwsgi

1
2
3
4
5
6
# cgi,uwsgi...名词解释:http://www.liuqingzheng.top/article/1/05-CGI,FastCGI,WSGI,uWSGI,uwsgi%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82/

# 安装uwsgi(不使用咱们装的解释器环境,使用阿里云内置的3.6.8)
pip3.7 install uwsgi
在任意路径下执行uwsgi会发现命令存在,这就可以了
ln -s /usr/local/python3.7/bin/uwsgi /usr/bin/uwsgi

配置虚拟环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1)安装依赖
>: pip3.7 install virtualenv
>: pip3.7 install virtualenvwrapper


# 如果有问题
pip3.7 install --upgrade setuptools
python3.7 -m pip install --upgrade pip

2)建立虚拟环境软连接(使用内置的python3.6 不需要加)
>: ln -s /usr/local/python3.7/bin/virtualenv /usr/bin/virtualenv

3)配置虚拟环境:填入下方内容
>: vim ~/.bash_profile

VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3.7
source /usr/bin/virtualenvwrapper.sh
4)退出编辑状态
>: esc

5)保存修改并退出
>: :wq

6)更新配置文件内容
>: source ~/.bash_profile

7)虚拟环境默认根目录:~/.virtualenvs

8)创建虚拟环境
mkvirtualenv luffy
workon luffy

前端项目部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 修改服务端地址
base_url: 'http://106.14.137.210:8000', // 真实环境:django项目就是跑在8000端口上的
# 编译
cnpm run build
压缩成zip,拖到服务器上去
# 在服务端安装
yum install unzip
# 解压
unzip dist.zip
mkdir /home/html
mv ~/dist /home/html



# 使用nginx代理静态文件
cd /usr/local/nginx/conf
# nginx.conf 配置文件是nginx的配置文件
#去向Nginx配置目录,备份配置,完全更新配置:填入下方内容
>: cd /usr/local/nginx/conf
>: mv nginx.conf nginx.conf.bak
>: vim nginx.conf
>: i

events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 80;
server_name 127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
charset utf-8;
location / {
root /home/html; # html访问路径
index index.html; # html文件名称
try_files $uri $uri/ /index.html; # 解决单页面应用刷新404问题
}
}
}

# 退出
>: esc
>: :wq

# 重启nginx
>: nginx -s reload

后端项目部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#1  把dev.py复制到pro.py中,相应的做修改(数据库。。。)
#2 新建一个manage_pro.py 指定配置文件为pro.py,以后迁移数据库,要迁移到pro.py配置的数据库中
#3 确认好wsgi.py,配置文件是不是pro.py
#4 导出项目依赖(在项目根路径下就会产生req.txt这是项目所有的依赖)
pip3 freeze > req.txt

# 5 把所有更改提交到git远端

# 6 在服务器上把代码拉下来
mkdir /home/project
cd /home/project
git clone https://gitee.com/liuqingzheng/luffy_api.git

# 7 使用uwsgi启动django
# 在项目根路径,新建uwsgi的配置文件luffy.xml
<uwsgi>
<socket>127.0.0.1:8888</socket>
<chdir>/home/project/luffy_api/</chdir>
<module>luffy_api.wsgi</module>
<processes>4</processes>
<daemonize>uwsgi.log</daemonize>
</uwsgi>


# 8 配置nginx 转发动态请求(加一个server)
>:

events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 80;
server_name 127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
charset utf-8;
location / {
root /home/html; # html访问路径
index index.html; # html文件名称
try_files $uri $uri/ /index.html; # 解决单页面应用刷新404问题
}
}
# 新增的server
server {
listen 8000;
server_name 127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
charset utf-8;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8888; # 端口要和uwsgi里配置的一样
uwsgi_param UWSGI_SCRIPT luffy_api.wsgi; #wsgi.py所在的目录名+.wsgi
uwsgi_param UWSGI_CHDIR /home/project/luffy_api/; # 项目路径
}
}
}


# 9 创建luffy库,新建lqz用户

1)管理员连接数据库
>: mysql -uroot -Jerry123?
2)创建数据库
>: create database imooc default charset=utf8mb4;

3)设置权限账号密码:账号密码要与项目中配置的一致
>: grant all privileges on imooc.* to 'cjt'@'%' identified by 'Jerry123?';
>: grant all privileges on imooc.* to 'cjt'@'localhost' identified by 'Jerry123?';
>: flush privileges;

4)退出mysql
>: quit;


#### 注意:动态设置linux环境变量
export DB_PASSWORD=Lqz12345?


# 10 进入虚拟环境,安装依赖
# mysqlclient
workon luffy
pip install -r req.txt # 如果报错,相应解决报错
pip install uwsgi # 正式环境中,虚拟环境也要装
# 11 迁移数据库
# 必须在luffy虚拟环境下
python manage_pro.py migrate
# 创建超级用户
python manage_pro.py createsuperuser


# 12 启动uwsgi
uwsgi -x ./imooc.xml
ps aux |grep uwsgi

# 13 启动nginx
nginx -s reload



# 14 收集静态文件
mkdir /home/project/luffy/luffy_api/static
# 修改配置文件
STATIC_URL = '/static/'
STATIC_ROOT = '/home/project/luffy/luffy_api/static'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
python ./manage_pro.py collectstatic # 前后端混合项目,用于收集静态资源

# 15 使用nginx代理静态文件
# 新增的配置静态文件
location /static {
alias /home/project/luffy/luffy_api/static;
}

# 16 把测试数据导入



# 17 申请域名,备案
把域名指向我们服务器的地址即可

docker-compose一键部署

安装/更新依赖包

1
2
3
4
yum update -y  #更新软件包
yum -y groupinstall "Development tools" # 装一个开发工具包(开发相关的一些软件,git)
yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel zlib* libffi-devel # 依赖,
git #验证git装完

安装docker/docker-compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

一 安装Docker
# 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2

# 设置yum源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装docker
yum install -y docker-ce

# 设置开机启动
systemctl enable docker

# 启动 Docker
systemctl start docker

# 查看版本
docker version



二 安装Docker-compose
# 下载
curl -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m) > /usr/bin/docker-compose

# 赋予执行权限
chmod +x /usr/bin/docker-compose

# 查看版本
docker-compose --version



# 从git上拉去最新项目

# 在项目路径下执行
docker-compose up -d

# 导入测试数据
# 在浏览器访问服务器地址,就可以看到项目

整合项目文件夹

1
2
3
# 写ym...配置文件

https://gitee.com/chi-jintao/imooc.git

阿里云-ECS服务器-CENTOS7系统部署MINIO图床

1. 下载MINIO的二进制文件

==注: 阿里云ECS网速过慢, 但可以接受==

1
wget https://dl.minio.io/server/minio/release/linux-amd64/minio
1
2
// 为minio文件赋予750权限
chmod 750 minio

2. 创建MINIO运行用户

1
2
3
4
5
6
7
8
9
// 创建用户组
groupadd -g 2021 minio
useradd -r -u 2021 -g 2021 -c "Minio User" -s /sbin/nologin minio

// 查看相关
id minio
>>> uid=2021(minio) gid=2021(minio) groups=2021(minio)
cat /etc/passwd
>>>inio:x:2021:2021:Minio User:/home/minio:/sbin/nologin

3. 创建MINIO相关目录

1
2
3
4
mkdir /usr/local/minio
mkdir /usr/local/minio/bin
mkdir /usr/local/minio/etc
mkdir /usr/local/minio/data
1
2
// 将下载的minio传入规定位置
cp minio /usr/local/minio/bin

4. 创建MINIO配置文件

默认文件配置

==注: listen_ip位置填写0.0.0.0, 不能填写公网/私网ip==

1
vim /usr/local/minio/etc/minio.conf
1
2
3
4
5
# minio.conf文件内填写
MINIO_VOLUMES="/usr/local/minio/data"
MINIO_OPTS="-C /usr/local/minio/etc --address listen_ip:9000"
MINIO_ACCESS_KEY="MYMINIO"
MINIO_SECRET_KEY="12345678"

启动文件配置

1
vim /etc/systemd/system/minio.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# minio.service文件内填写
[Unit]
Description=MinIO
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/minio/bin/minio
[Service]
# User and group
User=minio
Group=minio
EnvironmentFile=/usr/local/minio/etc/minio.conf
ExecStart=/usr/local/minio/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
# Let systemd restart this service always
Restart=always
# Specifies the maximum file descriptor number that can be opened by this process
LimitNOFILE=65536
# Disable timeout logic and wait until process is stopped
TimeoutStopSec=infinity
SendSIGKILL=no
[Install]
WantedBy=multi-user.target

5. 更改文件、目录属主属组

1
chown -R minio:minio /usr/local/minio

6. 启动/停止/查询服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 重载配置文件
systemctl daemon-reload

// 启动/停止/查询服务
systemctl enable minio.service # 停止服务
systemctl start minio.service # 启动服务
systemctl status minio.service # 查询服务运行情况
systemctl restart minio.service # 重启服务

// 过滤查询
ps aux | grep minio

// 端口查询
ss -tan | grep 9000

7. 配置阿里云安全组规则

8. 日志查看

==注: 服务可能启动失败==

1
2
// 日志中查看错误
tail -500 /var/log/messages

常见错误: 

  • ```shell
    May 14 20:57:22 Change-myself minio: ERROR Unable to validate passed arguments: host in server address should be this server
    May 14 20:57:22 Change-myself minio: > Please check –address parameter
    May 14 20:57:22 Change-myself minio: HINT:
    May 14 20:57:22 Change-myself minio: –address binds to a specific ADDRESS:PORT, ADDRESS can be an IPv4/IPv6 address or hostname (default port is ‘:9000’)

    配置文件监听地址错误, 配置为0.0.0.0

    MINIO_OPTS=”-C /usr/local/minio/etc –address 0.0.0.0:9000”
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    * ```shell
    May 14 21:01:13 Change-myself minio: ERROR Unable to validate credentials inherited from the shell environment: Invalid credentials
    May 14 21:01:13 Change-myself minio: > Please provide correct credentials
    May 14 21:01:13 Change-myself minio: HINT:
    May 14 21:01:13 Change-myself minio: Access key length should be at least 3, and secret key length at least 8 characters

    # 用户名、密码配置错误, 长度问题
    MINIO_ACCESS_KEY="MYMINIO"
    MINIO_SECRET_KEY="12345678"

9. 访问成功

==注: 无域名就使用公网ip:9000==

Redis

简介

Redis是一个key - value存储系统,非关系型数据库(nosql数据库),缓存数据库,数据存在于内存中

Redis支持五大数据类型

  • 字符串
  • 列表
  • hash(字典)
  • 集合
  • 有序集合

使用Redis的好处

  • 速度快:缓存数据库
  • 支持丰富数据类型(五大数据类型)
  • 支持事务(通过管道支持)(单实例上有效)
  • 丰富的特性:可用于缓存、消息队列、按key设置过期时间,过期后自动删除

Redis对比memcached

  • 都是内存数据库
  • Redis类型丰富(五大数据类型),memcached只支持字符串类型
  • Redis支持持久化,memcached不支持持久化
  • Redis速度比memcached块

Redis特点

  • 可以持久化
  • 单线程,单进程
    • Redis最新版本是6.x
    • 6.x版本之前是单线程,单进程

Redis单线程为什么这么快(官方:10w左右并发,实际:6w左右并发)

  • 纯内存操作
  • 使用了io多路复用模型(epoll、select)
  • 因为是单线程,不存在线程间切换,节省资源

服务:客户端,服务端(跟MySQL一样,是存数据的地方)

安装

启动Redis服务

  • 在任意路径下敲(前提:已经添加环境变量):redis-server
  • Windows平台已经做成服务,直接开启服务

客户端连接

  • 任意路径下敲:redis-cli -h 127.0.0.1 -p 6379
  • 使用客户端连接
  • 使用python操作(pip install redis)

普通连接与连接池

普通连接

1
2
3
4
5
6
7
# redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py

import redis

conn = redis.Redis(host='127.0.0.1', port=6379)
conn.set('foo', 'Bar')
print(r.get('foo'))

连接池

1
2
3
4
5
6
7
8
9
10
11
12
# redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销

# 默认:每个Redis实例都会维护一个自己的连接池

# 可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池

import redis

pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
conn = redis.Redis(connection_pool=pool)
conn.set('foo', 'Bar')
print(r.get('foo'))

把POOL以单例形式使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# pool.py

import redis

# 拿到一个连接数为3的连接池
# 把POOL做成单例:把它以模块导入方式
POOL=redis.ConnectionPool(max_connections=3,host='127.0.0.1', port=6380)

========================================================================================

import redis

from pool import POOL # 以模块形式导入,是天然单例
conn=redis.Redis(connection_pool=POOL)
# conn2=redis.Redis(connection_pool=POOL)
# conn3=redis.Redis(connection_pool=POOL)
# conn4=redis.Redis(connection_pool=POOL)
# conn5=redis.Redis(connection_pool=POOL)
# conn6=redis.Redis(connection_pool=POOL)

conn.set('age',19)
conn.close() # 不是关闭连接,把该连接,放回到连接池

Redis五大数据类型之字符串

字符串操作,Redis中的字符串再内存中按照一个name对应一个value来存储

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
'''
set(name, value, ex=None, px=None, nx=False, xx=False) 重点
setnx(name, value)
setex(name, value, time)
psetex(name, time_ms, value)
mset(*args, **kwargs) 重点
get(name) 重点
mget(keys, *args) 重点
getset(name, value)
getrange(key, start, end)
setrange(name, offset, value)
setbit(name, offset, value)
getbit(name, offset)
bitcount(key, start=None, end=None)

strlen(name) 重点
incr(self, name, amount=1) 重点
incrbyfloat(self, name, amount=1.0)
decr(self, name, amount=1)
append(key, value)
'''


import redis

conn=redis.Redis()

# set:放字符串值
'''
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
'''
# conn.set('name','lqz')
# res=conn.set('name','lqz',ex=3)
# res=conn.set('name','lqz',px=3000)
# res=conn.set('name','lqz')
# res=conn.set('name','egon')
# res=conn.set('name','lqz',nx=True)
# res=conn.set('name','19',xx=True)


# get 获取值
# res=conn.get('name')

# mset 批量设置值

# conn.mset({'name':'lqz','age':19,'sex':'男'})

# mget 批量获取值
# res=conn.mget(['name','age'])
# res=conn.mget('name','age')

# conn.setex(name, value, time)
# conn.psetex(name, time_ms, value)
# conn.set(name, valeu, px=time)

# res=conn.getset('name', '刘亦菲')


# res=conn.getrange('name',3,5) #前闭后闭区间 拿到 亦 这个字 拿字节
#
# print(str(res,encoding='utf-8'))


# res=conn.setrange('name',0,'lqzzzzzzz')
# res=conn.setrange('name',0,'qq')


##获取和设置比特位
# setbit(name, offset, value)
# getbit(name, offset)


# print(conn.getbit('name',3))
# conn.setbit('name',0,1)
# print(conn.getbit('name',0))

# print(conn.strlen('name')) # 指的是字节 获取字节长度

# conn.incr('age') # 让该键自增1
# conn.incrby('age',3) # 让该键自增3
# conn.incrbyfloat('age', amount=1.1)


conn.append('name', 'nb')

conn.close()

Redis五大数据类型之hash

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
'''
hset(name, key, value) 重点
hmset(name, mapping) 重点
hget(name,key) 重点
hmget(name, keys, *args) 重点
hgetall(name)
hlen(name) 重点
hkeys(name)
hvals(name)
hexists(name, key)
hdel(name,*keys)
hincrby(name, key, amount=1) 重点
hincrbyfloat(name, key, amount=1.0)
hscan(name, cursor=0, match=None, count=None)
hscan_iter(name, match=None, count=None)
'''

import redis

conn=redis.Redis()

# conn.hset('users','name','egon')
# conn.hset('users','age','19')

# res=conn.hget('users','age')
# res=conn.hget('users','name')



# conn.hmset('h1',{'name':'lqz','age':19})


# res=conn.hmget('h1',['name','age'])
# res=conn.hmget('h1','name','age')


# res=conn.hlen('users') # 统计键值对的个数



# res=conn.hkeys('users')
# res=conn.hvals('users')
# hvals(name)
# print(str(res[2],encoding='utf-8'))


# res=conn.hexists('users','age1')
# res=conn.hdel('users','age')



# conn.hincrby('h1', 'age',7)
# conn.hincrbyfloat('h1', 'age', amount=-1.01)



# for i in range(1000):
# conn.hset('htest',i,'第%s个鸡蛋'%i)

###hash(字典)类型是无序的

# res=conn.hgetall('htest')
# print(res)



# res=conn.hscan('htest', cursor=0,count=500) # 用的少
# print(res)
# res=conn.hscan('htest', cursor=254,count=499) # 用的少
# print(res)
# res=conn.hscan('htest', cursor=511,count=2) # 用的少
# print(res)
# print(res)
# print(len(res[1]))


res=conn.hscan_iter('htest', count=20) # 把hash类型的所有数据都打印出来,比hgetall节省内存
for item in res:
print(str(item[1],encoding='utf-8'))

# print(res)



conn.close()

Redis五大数据类型之列表

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
'''

lpush(name,values) #从左侧插入值
lpushx(name,value)
llen(name) # 重点
linsert(name, where, refvalue, value)) # 重点
lset(name, index, value)
lrem(name, value, num) # 删除值
lpop(name) #从左侧弹出
lindex(name, index)
lrange(name, start, end)
ltrim(name, start, end)
rpoplpush(src, dst)
blpop(keys, timeout) # 阻塞式弹出
brpoplpush(src, dst, timeout=0)
'''

import redis

conn=redis.Redis()

# conn.lpush('list1','1') #左侧插入,上部
# conn.lpush('list1','lqz')
# conn.rpush('list1','egon') #右侧插入,下部

# conn.rpushx('list1','刘亦菲') # 只有name存在才能插入
# conn.lpushx('list2','刘亦菲')


# print(conn.llen('list1'))


# conn.linsert('list1','before','egon','迪丽热巴')

# conn.linsert('list1','AFTER','egon','古力娜扎')
# conn.linsert('list1','AFTER','1','99999')


# conn.lset('list1',3,66666) # 修改某个位置的值



# 如果count=0,表示删除所有符合条件的,删除所有66666
# conn.lrem('list1',0,'66666')

# 如果count=1,表示从左侧删除1个符合条件的,删除1个99999
# conn.lrem('list1',1,'99999')
# 如果count=-2,表示从底部,从右侧,删除符合条件的2个
# conn.lrem('list1',-2,'99999')



# res=conn.lpop('list1')
# res=conn.rpop('list1')


# res=conn.lindex('list1',1) #返回某个索引的值 列表操作 l[0]


# res=conn.lrange('list1', 0, 4) # 前闭后闭


# res=conn.ltrim('list1', 1, 3) #修剪,不符合范围的都删除,前闭后闭
# conn.lpush('list2','李易峰','刘亦菲','迪丽热巴')

# conn.rpoplpush('list1', 'list2')

res=conn.blpop('list2',timeout=2) # 阻塞式弹出,通过它做一些分布式任务,可以用来做消息队列
print(res)


# brpoplpush(src, dst, timeout=0)




conn.close()

其他通用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
'''

delete(*names)
# 根据删除redis中的任意数据类型

exists(name)
# 检测redis的name是否存在

keys(pattern='*')
# 根据模型获取redis的name

expire(name ,time)
# 为某个redis的某个name设置超时时间

rename(src, dst)
# 对redis的name重命名为

move(name, db))
# 将redis的某个值移动到指定的db下

randomkey()
# 随机获取一个redis的name(不删除)

type(name)
#查看类型
'''



##通用操作

import redis

conn=redis.Redis()
# conn.delete('h1')


# print(conn.exists('name'))


# res=conn.keys('list*')
# print(res)

# conn.expire('list1',3)

# conn.rename('sex','sex111')

# conn.move('sex111',2)


# res=conn.randomkey()

# res=conn.type('users')
res=conn.type('name')

print(res)
conn.close()

管道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# redis支持事务,借助于管道


# import redis
#
#
# conn=redis.Redis()
#
# conn.decr('zhangsan',100)
#
# raise Exception('程序废了,出异常了')
#
# conn.incrby('wo',100)
#
# conn.close()


# 借助于管道实现

import redis
conn=redis.Redis()
pip_line=conn.pipeline(transaction=True) #开启一个管道
pip_line.decr('zhangsan',100) #往管道中放一个命令
raise Exception('程序崩了')
pip_line.incrby('wo',100) #往管道中放一个命令

pip_line.execute() # 把管道中的命令一次性执行

conn.close()

Django使用Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 第一种:通用方式(任何web框架都可以)
新建一个文件,其他文件导入使用

# 第二种:django提供的
-在配置文件中配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100} # 连接池大小为100
# "PASSWORD": "123",
}
}
}
-安装django_redis模块
-使用
from django_redis import get_redis_connection
def index(request):
conn=get_redis_connection()
res=conn.get('name')
print(res)
return HttpResponse('okkkkk')

-一旦这样配置,django中的缓存,也用redis
cache.set('key',python的任意对象)

git

介绍与安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0 git,github,gitee,gitlab
-git:版本管理软件
-github:网站,全球开源代码,放在上面,我们可以看到,并下载--》本质是一个git的远程仓库
-gitee:中国版的github,公有,私有(中小型公司的代码放在私有仓库中)
-gitlab:公司内部的(公司内部的github)

1 git:软件,实现 版本控制:协同开发(多人合作开发),合并代码

2 下载:https://git-scm.com/download/win
一路下一步

3 装完后,任意位置,鼠标右键,多出
git gui here:图形化界面
git bash here:命令行

4 git和svn
-git的版本管理不需要服务端(远程仓库)
-svn要进行版本管理,必须有服务端

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 分为三个地方
1 工作区
2 暂存区
3 版本库


# 命令
git init # 初始化仓库,在当前路径下生成一个.git文件夹,隐藏的
git add . # 把工作区的更该(.表示当前路径下所有更改,都提交),提交到暂存区
git status # 查看文件状态(绿色表示,在暂存区,不在版本库,红色表示工作区更改了,没有提交到暂存区)
git reset . # 把提交到暂存区的,拉回到工作区(以后基本不用)

git commit -m '注释' # 把暂存区的所有内容,提交到版本库

#在提交到版本库之前,要先配置用户信息 (--global:表示全局,在用户家路径.gitconfig)
git config --global user.email "306334678@qq.com"
git config --global user.name "liuqingzheng"

# 局部的用户信息,是在仓库的.git文件夹下的config中
git config user.email "33@qq.com"
git config user.name "egon"


git log # 查看版本提交的日志(谁提交的,提交时间,提交的版本号)
git reflog # 查看版本提交的日志

git reset --hard 74c071f922 #工作区的内容回退到 74c071f922 版本


# 空文件夹,不会被git管理,python中包会:包有 __init__.py

git过滤文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
在仓库目录下新建.gitignore  文件,在文件中写入要忽略的文件,文件夹,模糊匹配
## 注意:
'''
文件或文件夹名:代表所有目录下的同名文件或文件夹都被过滤
/文件或文件夹名:代表仓库根目录下的文件或文件夹被过滤

eg:
a.txt:项目中所有a.txt文件和文件夹都会被过滤
/a.txt:项目中只有根目录下a.txt文件和文件夹会被过滤
/b/a.txt:项目中只有根目录下的b文件夹下的a.txt文件和文件夹会被过滤
*x*:名字中有一个x的都会被过滤(*代表0~n个任意字符)
空文件夹不会被提交,空包会被提交,包可以被提交(包中有一个init空文件)

eg:
.idea
scripts
*.pyc
__pycache__
'''


pycharm软件
-绿色:在暂存区,没有提交到版本库
-红色:在工作区新增,没有提交到暂存区
-蓝色:在工作区修改的,没有提交到暂存区
-黄色:忽略的
-白色:已经被git管理的

git分支操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#创建分支
git branch 分支名
git checkout -b 分支名 #创建并切换到新分支
#切换分支
git checkout dev
#查看分支
git branch

#删除分支
git branch -d 分支名

#合并分支
git merge 分支名 #把dev分支合并到master分支:切换到master分支,执行合并dev分支的命令

# 分支合并可能会出冲突

git远程仓库

  • GitHub
  • gitee
  • gitlab
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1 在远程仓库中新建空仓库

2 git init # 初始化,让项目被git管理
git add . # 将所有项目提交到暂存区
git commit -m '注释' # 将暂存区的项目提交到本地版本库
git remote add 名字 远程仓库地址 # 增加一个远程仓库(指定目标远程仓库)
git push 远程仓库名字 分支名字 # 把本地版本库内容,提交到远程仓库(需要输入账户密码/用ssh连接)

3 远程仓库操作
增加
git remote add 名字 远程仓库地址
查看
git remote
删除
git remote remove origin
提交到远程仓库
git push origin master

采用ssh协议连接远程仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 https方式:用户名+密码认证方式

2 ssh方式:公钥私钥方式
本地生成公钥私钥
ssh-keygen -t rsa -C "1965617308@qq.com"
生成的公钥私钥在用户家路径的.ssh文件夹下
id_rsa # 私钥
id_rsa.pub # 公钥
'公钥'配置到远程仓库上
以后再提交代码就不用输入用户名密码

ps:重新配置远程仓库
git remote add origin git@gitee.com:chi-jintao/luffy_api.git
git push origin master # 选yes

项目创始者与项目开发者

  • 开发者
1
2
3
4
5
6
需要把代码clone到本地

1 git clone https://gitee.com/liuqingzheng/luffy_api.git
2 修改代码
3 提交到远程
4 git push origin master
  • 创始者
1
需要远程新建仓库,本地代码push上去

协同开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1 管理员、开发者(10开发者以上需要充值~)
能提交代码(增删改查)

2 有一个开发者提交了代码

3 另一个开发者,在提交之前,必须先拉取代码(让本地保持最新) (有事没事pull一下)

4 冲突解决(多人在同一个分支开发,导致冲突)

"""
<<<<<<< HEAD # 冲突的开始
# 自己的代码
======= # 分割线
# 别人的代码
>>>>>>> b63c408abd05b87fc492d40523240561999dba50 # 冲突的结束(版本)
"""

# 解决
1 该同样的功能出现的冲突,保留好代码,删掉其中一个即可

2 改不同功能出现冲突,两者都留

# 有事没事pull一下代码

线上分支合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1 本地和远端分支
本地建立分支,同步到远端
git branch dev
git push origin dev

远端建立分支,同步到本地
远端建立分支
git pull origin dev
git branch # 看不到
git checkout dev # 切到dev分支,本地就能看到了

2 提一个pr:pull request(分支合并的请求)

3 审核通过、测试通过后,合并分支/扁平化合并
git 变基(git rebase) ---> 扁平化合并(不保留分支的各个提交的小版本)(结果好看,但有冲突比较麻烦)
合并分支 合并分支(保留分支的各个提交的小版本)(结果乱,有冲突比较好解决)

4 远端:dev分支代码和master分支代码完全一致

本地分支合并推送到远程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1 由于dev分支和master分支合并时,有冲突,就不能自动合并了,我们需要本地合并完,解决冲突,再提交

2 把dev合并到master
git checkout master
git merge dev
有冲突,解决冲突
再提交

3 本地的分支提交到远端分支(如果名字不一样)
git push origin dev111:dev222 # 不要作死

ps:
1 git操作出没出冲突,如何解决
-第一:多人在同一分支开发
-第二:分支合并出冲突

2 git 变基
3 git fetch 和 git pull的区别
4 git add . 与git add xxx

HTML介绍

Web服务本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket


sk = socket.socket()

sk.bind(("127.0.0.1", 8080))
sk.listen(5)


while True:
conn, addr = sk.accept()
data = conn.recv(8096)
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
conn.send(b"<h1>Hello world!</h1>")
conn.close()

浏览器发请求 –> HTTP协议 –> 服务端接收请求 –> 服务端返回响应 –> 服务端把HTML文件内容发给浏览器 –> 浏览器渲染页面

HTML是什么?

  • 超文本标记语言(Hypertext Markup Language, HTML)是一种用于创建网页的标记语言。
  • 本质上是浏览器可识别的规则,我们按照规则写网页,浏览器根据规则渲染我们的网页。对于不同的浏览器,对同一个标签可能会有不同的解释。(兼容性问题)
  • 网页文件的扩展名:.html或.htm

HTML不是什么?

HTML是一种标记语言(markup language),它不是一种编程语言。

HTML使用标签来描述网页。

img

HTML文档结构

最基本的HTML文档:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>css样式优先级</title>
</head>
<body>

</body>
</html>
  1. 声明为HTML5文档。
  2. 、是文档的开始标记和结束的标记。是HTML页面的根元素,在它们之间是文档的头部(head)和主体(body)。
  3. 、定义了HTML文档的开头部分。它们之间的内容不会在浏览器的文档窗口显示。包含了文档的元(meta)数据。
  4. 定义了网页标题,在浏览器标题栏显示。
  5. 、之间的文本是可见的网页主体内容。

注意:对于中文网页需要使用 声明编码,否则会出现乱码。有些浏览器会设置 GBK 为默认编码,则你需要设置为

HTML标签格式

  • HTML标签是由尖括号包围的关键字,如,
  • HTML标签通常是成对出现的,比如:
    ,第一个标签是开始,第二个标签是结束。结束标签会有斜线。
  • 也有一部分标签是单独呈现的,比如:

    等。
  • 标签里面可以有若干属性,也可以不带属性。

标签的语法:

  • <标签名 属性1=“属性值1” 属性2=“属性值2”……>内容部分</标签名>
  • <标签名 属性1=“属性值1” 属性2=“属性值2”…… />

几个很重要的属性:

  • id:定义标签的唯一ID,HTML文档树中唯一
  • class:为html元素定义一个或多个类名(classname)(CSS样式类名)
  • style:规定元素的行内样式(CSS样式)

HTML注释

1
<!--注释内容-->

注释是代码之母。

<!DOCTYPE> 标签

声明必须是 HTML 文档的第一行,位于 标签之前。

声明不是 HTML 标签;它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。

HTML常用标签

head内常用标签

标签 意义
定义网页标题
定义内部样式表
定义JS代码或引入外部JS文件
引入外部样式表文件或网站图标
定义网页原信息

Meta标签

Meta标签介绍:

  • 元素可提供有关页面的元信息(mata-information),针对搜索引擎和更新频度的描述和关键词。
  • 标签位于文档的头部,不包含任何内容。
  • 提供的信息是用户不可见的。

meta标签的组成:meta标签共有两个属性,它们分别是http-equiv属性和name 属性,不同的属性又有不同的参数值,这些不同的参数值就实现了不同的网页功能。

1.http-equiv属性:相当于http的文件头作用,它可以向浏览器传回一些有用的信息,以帮助正确地显示网页内容,与之对应的属性值为content,content中的内容其实就是各个参数的变量值。

1
2
3
4
5
6
<!--指定文档的编码类型(需要知道)-->
<meta http-equiv="content-Type" charset=UTF8">
<!--2秒后跳转到对应的网址,注意引号(了解)-->
<meta http-equiv="refresh" content="2;URL=https://www.oldboyedu.com">
<!--告诉IE以最高级模式渲染文档(了解)-->
<meta http-equiv="x-ua-compatible" content="IE=edge">

2.name属性: 主要用于描述网页,与之对应的属性值为content,content中的内容主要是便于搜索引擎机器人查找信息和分类信息用的。

1
2
<meta name="keywords" content="meta总结,html meta,meta属性,meta跳转">
<meta name="description" content="老男孩教育Python学院">

body内常用标签

基本标签(块级标签和内联标签)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<b>加粗</b>
<i>斜体</i>
<u>下划线</u>
<s>删除</s>

<p>段落标签</p>

<h1>标题1</h1>
<h2>标题2</h2>
<h3>标题3</h3>
<h4>标题4</h4>
<h5>标题5</h5>
<h6>标题6</h6>

<!--换行-->
<br>

<!--水平线--><hr>

特殊字符

内容 对应代码
空格  
> >
< <
& &
¥ ¥
版权 ©
注册 ®

div标签和span标签

div标签用来定义一个块级元素,并无实际的意义。主要通过CSS样式为其赋予不同的表现。
span标签用来定义内联(行内)元素,并无实际的意义。主要通过CSS样式为其赋予不同的表现。

块级元素与行内元素的区别:
所谓块元素,是以另起一行开始渲染的元素,行内元素则不需另起一行。如果单独在网页中插入这两个元素,不会对页面产生任何的影响。
这两个元素是专门为定义CSS样式而生的。

注意:

关于标签嵌套:通常块级元素可以包含内联元素或某些块级元素,但内联元素不能包含块级元素,它只能包含其它内联元素。

p标签不能包含块级标签,p标签也不能包含p标签。

img标签

1
<img src="图片的路径" alt="图片未加载成功时的提示" title="鼠标悬浮时提示信息" width="宽" height="高(宽高两个属性只用一个会自动等比缩放)">

a标签

超链接标签

所谓的超链接是指从一个网页指向一个目标的连接关系,这个目标可以是另一个网页,也可以是相同网页上的不同位置,还可以是一个图片,一个电子邮件地址,一个文件,甚至是一个应用程序。

1
2
3
4
5
6
7
8
9
10
11
12
什么是URL?
URL是统一资源定位器(Uniform Resource Locator)的缩写,也被称为网页地址,是因特网上标准的资源的地址。
URL举例
http://www.sohu.com/stu/intro.html
http://222.172.123.33/stu/intro.html

URL地址由4部分组成
第1部分:为协议:http://、ftp://等
第2部分:为站点地址:可以是域名或IP地址
第3部分:为页面在站点中的目录:stu
第4部分:为页面名称,例如 index.html
各部分之间用“/”符号隔开。
1
<a href="http://www.oldboyedu.com" target="_blank" >点我</a>

href属性指定目标网页地址。该地址可以有几种类型:

  • 绝对URL - 指向另一个站点(比如 href=”http://www.jd.com)
  • 相对URL - 指当前站点中确切的路径(href=”index.htm”)
  • 锚URL - 指向页面中的锚(href=”#top”)

target:

  • _blank表示在新标签页中打开目标网页
  • _self表示在当前标签页中打开目标网页

列表

1.无序列表

1
2
3
4
<ul type="disc">
<li>第一项</li>
<li>第二项</li>
</ul>

type属性:

  • disc(实心圆点,默认值)
  • circle(空心圆圈)
  • square(实心方块)
  • none(无样式)

2.有序列表

1
2
3
4
<ol type="1" start="2">
<li>第一项</li>
<li>第二项</li>
</ol>

type属性:

  • 1 数字列表,默认值
  • A 大写字母
  • a 小写字母
  • Ⅰ大写罗马
  • ⅰ小写罗马

3.标题列表

1
2
3
4
5
6
7
<dl>
<dt>标题1</dt>
<dd>内容1</dd>
<dt>标题2</dt>
<dd>内容1</dd>
<dd>内容2</dd>
</dl>

表格

表格是一个二维数据空间,一个表格由若干行组成,一个行又有若干单元格组成,单元格里可以包含文字、列表、图案、表单、数字符号、预置文本和其它的表格等内容。
表格最重要的目的是显示表格类数据。表格类数据是指最适合组织为表格格式(即按行和列组织)的数据。
表格的基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<table>
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>爱好</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Egon</td>
<td>杠娘</td>
</tr>
<tr>
<td>2</td>
<td>Yuan</td>
<td>日天</td>
</tr>
</tbody>
</table>

属性:

  • border: 表格边框.
  • cellpadding: 内边距
  • cellspacing: 外边距.
  • width: 像素 百分比.(最好通过css来设置长宽)
  • rowspan: 单元格竖跨多少行
  • colspan: 单元格横跨多少列(即合并单元格)

form

功能:

表单用于向服务器传输数据,从而实现用户与Web服务器的交互

表单能够包含input系列标签,比如文本字段、复选框、单选框、提交按钮等等。

表单还可以包含textarea、select、fieldset和 label标签。

表单属性

属性 描述
accept-charset 规定在被提交表单中使用的字符集(默认:页面字符集)。
action 规定向何处提交表单的地址(URL)(提交页面)。
autocomplete 规定浏览器应该自动完成表单(默认:开启)。
enctype 规定被提交数据的编码(默认:url-encoded)。
method 规定在提交表单时所用的 HTTP 方法(默认:GET)。
name 规定识别表单的名称(对于 DOM 使用:document.forms.name)。
novalidate 规定浏览器不验证表单。
target 规定 action 属性中地址的目标(默认:_self)。

表单元素

基本概念:
HTML表单是HTML元素中较为复杂的部分,表单往往和脚本、动态页面、数据处理等功能相结合,因此它是制作动态网站很重要的内容。
表单一般用来收集用户的输入信息
表单工作原理:
访问者在浏览有表单的网页时,可填写必需的信息,然后按某个按钮提交。这些信息通过Internet传送到服务器上。
服务器上专门的程序对这些数据进行处理,如果有错误会返回错误信息,并要求纠正错误。当数据完整无误后,服务器反馈一个输入完成的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.conf.urls import url
from django.shortcuts import HttpResponse


def upload(request):
print("request.GET:", request.GET)
print("request.POST:", request.POST)

if request.FILES:
filename = request.FILES["file"].name
with open(filename, 'wb') as f:
for chunk in request.FILES['file'].chunks():
f.write(chunk)
return HttpResponse('上传成功')
return HttpResponse("收到了!")

urlpatterns = [
url(r'^upload/', upload),
]

input

元素会根据不同的 type 属性,变化为多种形态。

type属性值 表现形式 对应代码
text 单行输入文本 <input type=text” />
password 密码输入框
date 日期输入框
checkbox 复选框
radio 单选框
submit 提交按钮
reset 重置按钮
button 普通按钮
hidden 隐藏输入框
file 文本选择框

属性说明:

  • name:表单提交时的“键”,注意和id的区别
  • value:表单提交时对应项的值
    • type=”button”, “reset”, “submit”时,为按钮上显示的文本年内容
    • type=”text”,”password”,”hidden”时,为输入框的初始值
    • type=”checkbox”, “radio”, “file”,为输入相关联的值
  • checked:radio和checkbox默认被选中的项
  • readonly:text和password设置只读
  • disabled:所有input均适用

select标签

1
2
3
4
5
6
7
8
<form action="" method="post">
<select name="city" id="city">
<option value="1">北京</option>
<option selected="selected" value="2">上海</option>
<option value="3">广州</option>
<option value="4">深圳</option>
</select>
</form>

属性说明:

  • multiple:布尔属性,设置后为多选,否则默认单选
  • disabled:禁用
  • selected:默认选中该项
  • value:定义提交时的选项值

label标签

定义:

  1. label 元素不会向用户呈现任何特殊效果。
1
2
3
4
<form action="">
<label for="username">用户名</label>
<input type="text" id="username" name="username">
</form>

textarea多行文本

1
2
3
<textarea name="memo" id="memo" cols="30" rows="10">
默认内容
</textarea>

属性说明:

  • name:名称
  • rows:行数
  • cols:列数
  • disabled:禁用

简介

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。

HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

image-20200924172049937

协议特性

基于TCP/IP协议之上的应用层协议

基于请求-响应模式

HTTP协议规定,请求从客户端发出,最后服务器端应该接收请求并返回响应,换句话说,肯定是先从客户端开始建立通信的,服务端在没有接收到请求之前不会发送响应(于是出现了web_socket)

image-20200924172118199

无状态保存

HTTP是一种不保存状态,即无状态协议. HTTP协议自身不对请求和响应之间的通信状态进行保存. 也就是说HTTP协议对于发送过的请求或响应都不做持久化处理

image-20200924172149501

使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产生. 协议本身并不保留之前的一切请求或响应报文的信息. 这是为了更快地处理大量事务,确保协议的可伸缩性而特意吧HTTP协议设计成如此简单的

可是随着Web的不断发展,因无状态而导致业务处理变的棘手的情况增多了

HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能,于是引入了Cookie技术

无连接

无连接的含义是限制每次只连接处理一个请求,服务器处理完客户的请求,并收到客户的响应后,立刻断开链接,采用这种方式可以节省传输时间

HTTP版本区别

HTTP/0.9

http协议的第一个版本仅适用于数据信息的简介交换,即仅文本传输,只接受GET这一种请求方式,且不支持请求头,由于不支持POST方式,大多数敏感信息无法交换,至此客户端无法向服务器传递过多信息。
缺点: 信息种类过于单一,无法满足需求,串行处理

HTTP/1.0

第一个在通讯中指定版本号的HTTP协议版本,常用于代理服务器,成为了面向事务的应用层协议,;支持cache, MIME, method该协议需要每请求一次响应建立并拆除一次连接,1.0引入了POST和HEAD命令,相对于一版本在一定程度上保障的数据的传输安全,是一个典型的串行连接事务。
缺点: 串行处理,效率低下

HTTP/1.1

引入持久连接机制并被默认采用,且更好的配合代理服务器工作,还支持管道方式同一连接下同时发送多个请求,以降低线路负载,提高传输速度,新增方法: PUT、PATCH、OPTIONS、DELETE
缺点: 同一TCP连接里,所有通信按次序进行,服务器只能顺序处理回应,如果前面处理过慢,会有许多请求排队,造成队头阻塞(Head-of-line blocking)

HTTP/2.0

头信息和数据体都是二进制,称为头信息帧和数据帧
复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,且不用按顺序一一对应,避免了“队头堵塞“,此双向的实时通信称为多工(Multiplexing)
引入头信息压缩机制(header compression),头信息使用gzip或compress压缩后再发送;客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,不发送同样字段,只发送索引号,提高速度
HTTP/2 允许服务器未经请求,主动向客户端发送资源,即服务器推送(server push)

HTTP请求协议与响应协议

HTTP协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议

用于HTTP协议交互的信息被称为HTTP报文

请求端(客户端)的HTTP报文,叫做请求报文,响应端(服务器端)的HTTP报文叫做响应报文

image-20200924172222493

请求协议

请求格式

image-20200924172250257

image-20200924172316497

请求方式:get与post请求

  • GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
  • GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
  • GET与POST请求在服务端获取请求数据方式不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
'''
GET请求
# 请求首行
GET / HTTP/1.1\r\n
# get请求后面的参数
GET /?name=lqz&age=18 HTTP/1.1\r\n
# 请求头
Host: 127.0.0.1:8008\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: csrftoken=7xx6BxQDJ6KB0PM7qS8uTA892ACtooNbnnF4LDwlYk1Y7S7nTS81FBqwruizHsxF\r\n\r\n'
# 请求体(get请求,请求体为空)
'''
'''
POST请求
# 请求首行
POST /?name=lqz&age=18 HTTP/1.1\r\n
# 请求头
Host: 127.0.0.1:8008\r\nConnection: keep-alive\r\nContent-Length: 21\r\nCache-Control: max-age=0\r\nOrigin: http://127.0.0.1:8008\r\nUpgrade-Insecure-Requests: 1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nReferer: http://127.0.0.1:8008/?name=lqz&age=18\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=7xx6BxQDJ6KB0PM7qS8uTA892ACtooNbnnF4LDwlYk1Y7S7nTS81FBqwruizHsxF\r\n\r\n
# 请求体
name=lqz&password=123'

'''

响应协议

响应格式

image-20200924172428894

image-20200924172443165

响应状态码

image-20200924172516228

内容概要

  • 分组与嵌套
  • 伪类选择器
  • 伪元素选择器
  • 选择器优先级
  • css属性相关(操作标签样式)
  • 浮动
  • 定位
  • 模态框
  • 透明度

内容详细

分组与嵌套

1
2
3
4
5
6
div,p,span {  /*逗号表示并列关系*/
color: yellow;
}
#d1,.c1,span {
color: orange;
}

伪类选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
background-color: black;
}
a:link { /*访问之前的状态*/
color: red;
}
a:hover { /*需要记住*/
color: aqua; /*鼠标悬浮态*/
}
a:active {
color: black; /*鼠标点击不松开的状态 激活态*/
}
a:visited {
color: darkgray; /*访问之后的状态*/
}
p {
color: darkgray;
font-size: 48px;
}
p:hover {
color: white;
}

input:focus { /*input框获取焦点(鼠标点了input框)*/
background-color: red;
}
</style>
</head>
<body>
<a href="https://www.jd.com/">小轩在不在?</a>
<p>点我有你好看哦</p>
<input type="text">
</body>
</html>

伪元素选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
p:first-letter {
font-size: 48px;
color: orange;
}
p:before { /*在文本开头 同css添加内容*/
content: '你说的对';
color: blue;
}
p:after {
content: '雨露均沾';
color: orange;
}
ps:before和after通常都是用来清除浮动带来的影响:父标签塌陷的问题(后面马上讲)

选择器优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""
id选择器
类选择器
标签选择器
行内式

"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
/*
1.选择器相同 书写顺序不同
就近原则:谁离标签更近就听谁的
2.选择器不同 ...
行内 > id选择器 > 类选择器 > 标签选择器
精确度越高越有效
*/
#d1 {
color: aqua;
}
/*.c1 {*/
/* color: orange;*/
/*}*/
/*p {*/
/* color: red;*/
/*}*/
</style>
<!-- <link rel="stylesheet" href="mycss1.css">-->
</head>
<body>
<p id="d1" class="c1" style="color: blue">贤妻果然很识趣,有前途~</p>
</body>
</html>

css属性相关

1
2
3
4
5
6
7
8
9
10
11
12
13
<style>
p {
background-color: red;
height: 200px;
width: 400px;
}
span {
background-color: green;
height: 200px;
width: 400px;
/*行内标签无法设置长宽 就算你写了 也不会生效*/
}
</style>

字体属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
p {
/*font-family: "Arial Black","微软雅黑","..."; !*第一个不生效就用后面的 写多个备用*!*/

/*font-size: 24px; !*字体大小*!*/

/*font-weight: inherit; !*bolder lighter 100~900 inherit继承父元素的粗细值*!*/

/*color: red; !*直接写颜色英文*!*/
/*color: #ee762e; !*颜色编号*!*/
/*color: rgb(128,23,45); !*三基色 数字 范围0-255*!*/
/*color: rgba(23, 128, 91, 0.9); !*第四个参数是颜色的透明度 范围是0-1*!*/

/*当你想要一些颜色的时候 可以利用现成的工具
1 pycharm提供的取色器
2 qq或者微信截图功能
也可以多软件结合使用
*/
}

文字属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
p {
/*text-align: center; !*居中*!*/
/*text-align: right;*/
/*text-align: left;*/
/*text-align: justify; !*两端对齐*!*/

/*text-decoration: underline;*/
/*text-decoration: overline;*/
/*text-decoration: line-through;*/
/*text-decoration: none;*/
/*在html中 有很多标签渲染出来的样式效果是一样的*/
font-size: 16px;
text-indent: 32px; /*缩进32px*/
}
a {
text-decoration: none; /*主要用于给a标签去掉自带的下划线 需要掌握*/
}

背景图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
#d1 {
height: 500px;
background-color: red;
}
#d2 {
height: 500px;
background-color: green;
}
#d3 {
height: 500px;
background-image: url("222.png");
background-attachment: fixed;
}
#d4 {
height: 500px;
background-color: aqua;
}
</style>
</head>
<body>
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
</body>
</html>

边框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>

p {
background-color: red;

border-width: 5px;
border-style: solid;
border-color: green;

}
div {
/*border-left-width: 5px;*/
/*border-left-color: red;*/
/*border-left-style: dotted;*/

/*border-right-width: 10px;*/
/*border-right-color: greenyellow;*/
/*border-right-style: solid;*/

/*border-top-width: 15px;*/
/*border-top-color: deeppink;*/
/*border-top-style: dashed;*/

/*border-bottom-width: 10px;*/
/*border-bottom-color: tomato;*/
/*border-bottom-style: solid;*/
border: 3px solid red; /*三者位置可以随意写*/

}
#d1 {
background-color: greenyellow;
height: 400px;
width: 400px;
border-radius: 50%; /*直接写50%即可 长宽一样就是圆 不一样就是椭圆*/
}
</style>
</head>
<body>
<p>穷人 被diss到了 哭泣.png</p>
<div>妈拉个巴子,妈拉个巴子,妈拉个巴子,妈拉个巴子</div>
<div id="d1"></div>
</body>
</html>

display属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/*#d1 {*/
/* !*display: none; !*隐藏标签不展示到前端页面并且原来的位置也不再占有了 但是还存在于文档上*!*!*/
/* display: inline; !*将标签设置为行内标签的特点*!*/
/*}*/
/*#d2 {*/
/* display: inline;*/
/*}*/
/*#d1 {*/
/* display: block; !*将标签设置成块儿级标签的特点*!*/
/*}*/
/*#d2 {*/
/* display: block;*/
/*}*/
/*#d1 {*/
/* display: inline-block;*/
/*}*/
/*#d2 {*/
/* display: inline-block; !*标签即可以在一行显示又可以设置长宽*!*/
/*}*/
</style>
</head>
<body>
<div style="display: none">div1</div>
<div>div2</div>
<div style="visibility: hidden">单纯的隐藏 位置还在</div>
<div>div4</div>
<!--<div id="d1" style="height: 100px;width: 100px;background-color: red">01</div>-->
<!--<div id="d2" style="height: 100px;width: 100px;background-color: greenyellow">02</div>-->
<!--<span id="d1" style="height: 100px;width: 100px;background-color: red">span</span>-->
<!--<span id="d2" style="height: 100px;width: 100px;background-color: greenyellow">span</span>-->

<!--<div id="d1" style="height: 100px;width: 100px;background-color: red">01</div>-->
<!--<div id="d2" style="height: 100px;width: 100px;background-color: greenyellow">02</div>-->
</body>
</html>

盒子模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""
盒子模型
就以快递盒为例
快递盒与快递盒之间的距离(标签与标签之间的距离 margin外边距)
盒子的厚度(标签的边框 border)
盒子里面的物体到盒子的距离(内容到边框的距离 padding内边距)
物体的大小(内容 content)


如果你想要调整标签与标签之间的距离 你就可以调整margin

浏览器会自带8px的margin,一般情况下我们在写页面的时候,上来就会先将body的margin去除

"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0; /*上下左右全是0
/*margin: 10px 20px; !* 第一个上下 第二个左右*!*/
/*margin: 10px 20px 30px; !*第一个上 第二个左右 第三个下*!*/
/*margin: 10px 20px 30px 40px; !*上 右 下 左*!*/
}
/*p {*/
/* margin-left: 0;*/
/* margin-top: 0;*/
/* margin-right: 0;*/
/* margin-bottom: 0;*/
/*}*/

#d1 {
margin-bottom: 50px;
}


#d2 {
margin-top: 20px; /*不叠加 只取大的*/
}

#dd {
margin: 0 auto; /*只能做到标签的水平居中*/
}
p {
border: 3px solid red;
/*padding-left: 10px;*/
/*padding-top: 20px;*/
/*padding-right: 20px;*/
/*padding-bottom: 50px;*/

/*padding: 10px;*/
/*padding: 10px 20px;*/
/*padding: 10px 20px 30px;*/
/*padding: 10px 20px 30px 40px;*/ /*规律和margin一模一样*/
}
</style>
</head>
<body>
<!-- <p style="border: 1px solid red;" id="d1">ppp</p>-->
<!-- <p style="border: 1px solid orange;" id="d2">ppp</p>-->
<!--<div style="border: 3px solid red;height: 400px;width: 400px">-->
<!-- <div id='dd' style="border: 1px solid orange;height: 50px;width: 50px;"></div>-->
<!--</div>-->

<p>ppp</p>

</body>
</html>

浮动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"""浮动的元素 没有块儿级一说 本身多大浮起来之后就只能占多大"""
只要是设计到页面的布局一般都是用浮动来提前规划好
<style>
body {
margin: 0;
}
#d1 {
height: 200px;
width: 200px;
background-color: red;
float: left; /*浮动 浮到空中往左飘*/
}
#d2 {
height: 200px;
width: 200px;
background-color: greenyellow;
float: right; /*浮动 浮到空中往右飘*/
}
</style>

解决浮动带来的影响

1
2
<!--浮动带来的影响-->
会造成父标签塌陷的问题

溢出属性

1
2
3
4
5
6
7
8
9
p {
height: 100px;
width: 50px;
border: 3px solid red;
/*overflow: visible; !*默认就是可见 溢出还是展示*!*/
/*overflow: hidden; !*溢出部分直接隐藏*!*/
/*overflow: scroll; !*设置成上下滚动条的形式*!*/
/*overflow: auto;*/
}

定位

  • 静态

    所有的标签默认都是静态的static,无法改变位置

  • 相对定位(了解)

    相对于标签原来的位置做移动relative

  • 绝对定位(常用)

    相对于已经定位过的父标签做移动(如果没有父标签那么就以body为参照)

    eg:小米网站购物车

    当你不知道页面其他标签的位置和参数,只给了你一个父标签的参数,让你基于该标签左定位

  • 固定定位(常用)

    相对于浏览器窗口固定在某个位置

    eg:右侧小广告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
}
#d1 {
height: 100px;
width: 100px;
background-color: red;
left: 50px; /*从左往右 如果是负数 方向则相反*/
top: 50px; /*从上往下 如果是负数 方向则相反*/
/*position: static; !*默认是static无法修改位置*!*/
position: relative;
/*相对定位
标签由static变为relative它的性质就从原来没有定位的标签变成了已经定位过的标签
虽然你哪怕没有动 但是你的性质也已经改变了
*/
}

#d2 {
height: 100px;
width: 200px;
background-color: red;
position: relative; /*已经定位过了*/
}
#d3 {
height: 200px;
width: 400px;
background-color: yellowgreen;
position: absolute;
left: 200px;
top: 100px;
}

#d4 {
position: fixed; /*写了fixed之后 定位就是依据浏览器窗口*/
bottom: 10px;
right: 20px;

height: 50px;
width: 100px;
background-color: white;
border: 3px solid black;
}
</style>
</head>
<body>
<!-- <div id="d1"></div>-->

<!--<div id="d2">-->
<!-- <div id="d3"></div>-->
<!--</div>-->

<div style="height: 500px; color: rgb(152, 26, 26);">></div>
<div style="height: 500px; color: rgb(152, 26, 26);">></div>
<div style="height: 500px; color: rgb(152, 26, 26);">></div>
<div id="d4">回到顶部</div>

</body>
</html>

ps:浏览器是优先展示文本内容的

验证浮动和定位是否脱离文档流(原来的位置是否还保留)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"""
浮动
相对定位
绝对定位
固定定位
"""
# 不脱离文档流
1.相对定位
# 脱离文档流
1.浮动
2.绝对定位
3.固定定位

<!--<div style="height: 100px;width: 200px;position: relative;left: 500px"></div>-->
<!--<div style="height: 100px;width: 200px; color: rgb(152, 26, 26);">></div>-->

<!--<div style="height: 100px;width: 200px;"></div>-->
<!--<div style="height: 100px;width: 200px;position: absolute;left: 500px"></div>-->
<!--当没有父标签做到位 就参照与body-->
<!--<div style="height: 100px;width: 200px;"></div>-->

<div style="height: 100px;width: 200px;"></div>
<div style="height: 100px;width: 200px;position: fixed;bottom: 10px;right: 20px"></div>
<div style="height: 100px;width: 200px;"></div>

z-index模态框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
eg:百度登陆页面 其实是三层结构
1.最底部是正常内容(z=0) 最远的
2.黑色的透明区(z=99) 中间层
3.白色的注册区域(z=100) 离用户最近

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
}
.cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
z-index: 99;
}
.modal {
background-color: white;
height: 200px;
width: 400px;
position: fixed;
left: 50%;
top: 50%;
z-index: 100;
margin-left: -200px;
margin-top: -100px;

}
</style>
</head>
<body>
<div>这是最底层的页面内容</div>
<div class="cover"></div>
<div class="modal">
<h1>登陆页面</h1>
<p>username:<input type="text"></p>
<p>password:<input type="text"></p>
<button>点我点我~</button>
</div>
</body>
</html>

透明度opacity

1
2
3
4
5
# 它不单单可以修改颜色的透明度还同时修改字体的透明度
rgba只能影响颜色
而opacity可以修改颜色和字体

opacity: 0.5;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""
jQuery内部封装了原生的js代码(还额外添加了很多的功能)
能够让你通过书写更少的代码 完成js操作
类似于python里面的模块 在前端模块不叫模块 叫 “类库”

兼容多个浏览器的 你在使用jQuery的时候就不需要考虑浏览器兼容问题

jQuery的宗旨
write less do more
让你用更少的代码完成更多的事情

复习
python导入模块发生了哪些事?
导入模块其实需要消耗资源
jQuery在使用的时候也需要导入
但是它的文件非常的小(几十KB) 基本不影响网络速度

选择器
筛选器
样式操作
文本操作
属性操作
文档处理
事件
动画效果
插件
each、data、Ajax(重点 django部分学)

版本介绍

jQuery文件下载
压缩 容量更小
未压缩
"""
# jQuery在使用之前 一定要确保已经导入了

针对导入问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1 文件下载到了本地 如何解决多个文件反复书写引入语句的代码
可以借助于pycharm自动初始化代码功能完成自动添加
配置
编辑
file and code template
"""我不想下载jQuery文件 能不能使用呢?"""

# 2 直接引入jQuery提供的CDN服务(基于网络直接请求加载)
CDN:内容分发网络
CDN有免费的也有收费的
前端免费的cdn网站:
bootcdn
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
""" """


# jQuery基本语法
jQuery(选择器).action()
秉持着jQuery的宗旨 jQuery简写 $
jQuery() === $()

# jQuery与js代码对比
eg:将p标签内部的文本颜色改为红色
// 原生js代码操作标签
let pEle = document.getElementById('d1')
pEle.style.color = 'red'

// jQuery操作标签
$('p').css('color','blue')

基本选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// id选择器
$('#d1')
w.fn.init [div#d1]0: div#d1length: 1__proto__: Object(0)
// class选择器
$('.c1')
w.fn.init [p.c1, prevObject: w.fn.init(1)]0: p.c1length: 1prevObject: w.fn.init [document]__proto__: Object(0)
// 标签选择器
$('span')
w.fn.init(3) [span, span, span, prevObject: w.fn.init(1)]

"""一定要区分开(重点)"""
// jQuery对象如何变成标签对象
undefined
$('#d1')[0]
<div id="d1">…</div>
document.getElementById('d1')
<div id="d1">…</div>
// 标签对象如何转jQuery对象
undefined
$(document.getElementById('d1'))
w.fn.init [div#d1]

组合选择器/分组与嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$('div')
w.fn.init(2) [div#d1, div.c1, prevObject: w.fn.init(1)]
$('div.c1')
w.fn.init [div.c1, prevObject: w.fn.init(1)]0: div.c1length: 1prevObject: w.fn.init [document]__proto__: Object(0)
$('div#d1')
w.fn.init [div#d1, prevObject: w.fn.init(1)]
$('*')
w.fn.init(19) [html, head, meta, title, meta, link, script, script, body, span, span, div#d1, span, p#d2, span, span, div.c1, span, span, prevObject: w.fn.init(1)]

$('#d1,.c1,p') # 并列+混用
w.fn.init(3) [div#d1, p#d2, div.c1, prevObject: w.fn.init(1)]

$('div span') # 后代
w.fn.init(3) [span, span, span, prevObject: w.fn.init(1)]
$('div>span') # 儿子
w.fn.init(2) [span, span, prevObject: w.fn.init(1)]
$('div+span') # 毗邻
w.fn.init [span, prevObject: w.fn.init(1)]
$('div~span') # 弟弟
w.fn.init(2) [span, span, prevObject: w.fn.init(1)]

基本筛选器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$('ul li')
w.fn.init(10) [li, li, li, li, li, li, li.c1, li, li#d1, li, prevObject: w.fn.init(1)]

$('ul li:first') # 大儿子
w.fn.init [li, prevObject: w.fn.init(1)]0: lilength: 1prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:last') # 小儿子
w.fn.init [li, prevObject: w.fn.init(1)]0: lilength: 1prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:eq(2)') # 放索引
w.fn.init [li, prevObject: w.fn.init(1)]0: lilength: 1prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:even') # 偶数索引 0包含在内
w.fn.init(5) [li, li, li, li.c1, li#d1, prevObject: w.fn.init(1)]0: li1: li2: li3: li.c14: li#d1length: 5prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:odd') # 奇数索引
w.fn.init(5) [li, li, li, li, li, prevObject: w.fn.init(1)]0: li1: li2: li3: li4: lilength: 5prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:gt(2)') # 大于索引
w.fn.init(7) [li, li, li, li.c1, li, li#d1, li, prevObject: w.fn.init(1)]0: li1: li2: li3: li.c14: li5: li#d16: lilength: 7prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:lt(2)') # 小于索引
w.fn.init(2) [li, li, prevObject: w.fn.init(1)]0: li1: lilength: 2prevObject: w.fn.init [document]__proto__: Object(0)

$('ul li:not("#d1")') # 移除满足条件的标签
w.fn.init(9) [li, li, li, li, li, li, li.c1, li, li, prevObject: w.fn.init(1)]

$('div')
w.fn.init(2) [div, div, prevObject: w.fn.init(1)]
$('div:has("p")') # 选取出包含一个或多个标签在内的标签
w.fn.init [div, prevObject: w.fn.init(1)]

属性选择器

1
2
3
4
5
6
7
8
9
10
11
12
$('[username]')
w.fn.init(3) [input, input, p, prevObject: w.fn.init(1)]
$('[username="jason"]')
w.fn.init [input, prevObject: w.fn.init(1)]
$('p[username="egon"]')
w.fn.init [p, prevObject: w.fn.init(1)]

# 内置的属性
$('[type]')
w.fn.init(2) [input, input, prevObject: w.fn.init(1)]
$('[type="text"]')
w.fn.init(2) [input, input, prevObject: w.fn.init(1)]

表单筛选器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$('input[type="text"]')
w.fn.init [input, prevObject: w.fn.init(1)]0: inputlength: 1prevObject: w.fn.init [document]__proto__: Object(0)
$('input[type="password"]')
w.fn.init [input, prevObject: w.fn.init(1)]

$(':text') # 等价于上面第一个
w.fn.init [input, prevObject: w.fn.init(1)]0: inputlength: 1prevObject: w.fn.init [document]__proto__: Object(0)
$(':password') # 等价于上面第二个
w.fn.init [input, prevObject: w.fn.init(1)]


:text
:password
:file
:radio
:checkbox
:submit
:reset
:button
...

表单对象属性
:enabled
:disabled
:checked
:selected
"""特殊情况"""
$(':checked') # 它会将checked和selected都拿到
w.fn.init(2) [input, option, prevObject: w.fn.init(1)]0: input1: optionlength: 2prevObject: w.fn.init [document]__proto__: Object(0)
$(':selected') # 它不会 只拿selected
w.fn.init [option, prevObject: w.fn.init(1)]
$('input:checked') # 自己加一个限制条件
w.fn.init [input, prevObject: w.fn.init(1)]

筛选器方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
$('#d1').next()  # 同级别下一个
w.fn.init [span, prevObject: w.fn.init(1)]0: spanlength: 1prevObject: w.fn.init [span#d1]__proto__: Object(0)
$('#d1').nextAll() # 同级别下的所有
w.fn.init(5) [span, div#d2, span, span, span.c1, prevObject: w.fn.init(1)]0: span1: div#d22: span3: span4: span.c1length: 5prevObject: w.fn.init [span#d1]__proto__: Object(0)
$('#d1').nextUntil('.c1') # 不包括最后一个
w.fn.init(4) [span, div#d2, span, span, prevObject: w.fn.init(1)]0: span1: div#d22: span3: spanlength: 4prevObject: w.fn.init [span#d1]__proto__: Object(0)


$('.c1').prev() # 上一个
w.fn.init [span, prevObject: w.fn.init(1)]0: spanlength: 1prevObject: w.fn.init [span.c1, prevObject: w.fn.init(1)]__proto__: Object(0)
$('.c1').prevAll()
w.fn.init(5) [span, span, div#d2, span, span#d1, prevObject: w.fn.init(1)]
$('.c1').prevUntil('#d2')
w.fn.init(2) [span, span, prevObject: w.fn.init(1)]

$('#d3').parent() # 第一级父标签
w.fn.init [p, prevObject: w.fn.init(1)]0: plength: 1prevObject: w.fn.init [span#d3]__proto__: Object(0)
$('#d3').parent().parent()
w.fn.init [div#d2, prevObject: w.fn.init(1)]
$('#d3').parent().parent().parent()
w.fn.init [body, prevObject: w.fn.init(1)]
$('#d3').parent().parent().parent().parent()
w.fn.init [html, prevObject: w.fn.init(1)]
$('#d3').parent().parent().parent().parent().parent()
w.fn.init [document, prevObject: w.fn.init(1)]
$('#d3').parent().parent().parent().parent().parent().parent()
w.fn.init [prevObject: w.fn.init(1)]
$('#d3').parents()
w.fn.init(4) [p, div#d2, body, html, prevObject: w.fn.init(1)]
$('#d3').parentsUntil('body')
w.fn.init(2) [p, div#d2, prevObject: w.fn.init(1)]


$('#d2').children() # 儿子

$('#d2').siblings() # 同级别上下所有



$('div p')
# 等价
$('div').find('p') # find从某个区域内筛选出想要的标签


"""下述两两等价"""
$('div span:first')
w.fn.init [span, prevObject: w.fn.init(1)]
$('div span').first()
w.fn.init [span, prevObject: w.fn.init(3)]0: spanlength: 1prevObject: w.fn.init(3) [span, span#d3, span, prevObject: w.fn.init(1)]__proto__: Object(0)

$('div span:last')
w.fn.init [span, prevObject: w.fn.init(1)]
$('div span').last()

w.fn.init [span, prevObject: w.fn.init(3)]
$('div span:not("#d3")')
w.fn.init(2) [span, span, prevObject: w.fn.init(1)]
$('div span').not('#d3')
w.fn.init(2) [span, span, prevObject: w.fn.init(3)]

jQuery练习题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$('#i1')
r.fn.init [div#i1.container]

$('h2')
r.fn.init [h2, prevObject: r.fn.init(1)]

$('input')
r.fn.init(9) [input#exampleInputEmail1.form-control, input#exampleInputPassword1.form-control, input#exampleInputFile, input, input, input, input, input#optionsRadios1, input#optionsRadios2, prevObject: r.fn.init(1)]

$('.c1')
r.fn.init(2) [h1.c1, h1.c1, prevObject: r.fn.init(1)]

$('.btn-default')
r.fn.init [button#btnSubmit.btn.btn-default, prevObject: r.fn.init(1)]

$('.c1,h2')
r.fn.init(3) [h1.c1, h1.c1, h2, prevObject: r.fn.init(1)]

$('.c1,#p3')
r.fn.init(3) [h1.c1, h1.c1, p#p3.divider, prevObject: r.fn.init(1)]

$('.c1,.btn')
r.fn.init(11) [h1.c1, h1.c1, a.btn.btn-primary.btn-lg, button.btn.btn-warning, button.btn.btn-danger, button.btn.btn-warning, button.btn.btn-danger, button.btn.btn-warning, button.btn.btn-danger, button#btnSubmit.btn.btn-default, a.btn.btn-success, prevObject: r.fn.init(1)]

$('form').find('input')
r.fn.init(3) [input#exampleInputEmail1.form-control, input#exampleInputPassword1.form-control, input#exampleInputFile, prevObject: r.fn.init(1)]

$('label input')
r.fn.init(6) [input, input, input, input, input#optionsRadios1, input#optionsRadios2, prevObject: r.fn.init(1)]

$('label+input')
r.fn.init(3) [input#exampleInputEmail1.form-control, input#exampleInputPassword1.form-control, input#exampleInputFile, prevObject: r.fn.init(1)]

$('#p2~li')
r.fn.init(8) [li, li, li, li, li, li, li, li, prevObject: r.fn.init(1)]

$('#f1 input:first')
r.fn.init [input#exampleInputEmail1.form-control, prevObject: r.fn.init(1)]

$('#my-checkbox input:last')
r.fn.init [input, prevObject: r.fn.init(1)]

$('#my-checkbox input[checked!="checked"]')
r.fn.init(3) [input, input, input, prevObject: r.fn.init(1)]0: input1: input2: inputlength: 3prevObject: r.fn.init [document]__proto__: Object(0)

$('label:has("input")')
r.fn.init(6) [label, label, label, label, label, label, prevObject: r.fn.init(1)]

操作标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# 操作类
"""
js版本 jQuery版本
classList.add() addClass()
classList.remove() removeClass()
classList.contains() hasClass()
classList.toggle() toggleClass()
"""

# css操作
<p>111</p>
<p>222</p>
"""一行代码将第一个p标签变成红色第二个p标签变成绿色"""
$('p').first().css('color','red').next().css('color','green')
# jQuery的链式操作 使用jQuery可以做到一行代码操作很多标签
# jQuery对象调用jQuery方法之后返回的还是当前jQuery对象 也就可以继续调用其他方法
class MyClass(object):
def func1(self):
print('func1')
return self

def func2(self):
print('func2')
return self
obj = MyClass()
obj.func1().func2()

# 位置操作
offset() # 相对于浏览器窗口
position() # 相对于父标签

scrollTop() # 需要了解
$(window).scrollTop()
0
$(window).scrollTop()
969
$(window).scrollTop() # 括号内不加参数就是获取
1733
$(window).scrollTop(0) # 加了参数就是设置
n.fn.init [Window]
$(window).scrollTop(500)
n.fn.init [Window]
scrollLeft()

# 尺寸
$('p').height() # 文本
20
$('p').width()
1670
$('p').innerHeight() # 文本+padding
26
$('p').innerWidth()
1674
$('p').outerHeight() # 文本+padding+border
26
$('p').outerWidth()
1674


# 文本操作
"""
操作标签内部文本
js jQuery
innerText text() 括号内不加参数就是获取加了就是设置
innerHTML html()

$('div').text()
"

有些话听听就过去了,不要在意,都是成年人!

"
$('div').html()
"
<p>
有些话听听就过去了,不要在意,都是成年人!
</p>
"
$('div').text('你们都是我的大宝贝')
w.fn.init [div, prevObject: w.fn.init(1)]
$('div').html('你个臭妹妹')
w.fn.init [div, prevObject: w.fn.init(1)]
$('div').text('<h1>你们都是我的大宝贝</h1>')
w.fn.init [div, prevObject: w.fn.init(1)]
$('div').html('<h1>你个臭妹妹</h1>')
w.fn.init [div, prevObject: w.fn.init(1)]
"""
# 获取值操作
"""
js jQuery
.value .val()
"""
$('#d1').val()
"sasdasdsadsadad"
$('#d1').val('520快乐') # 括号内不加参数就是获取加了就是设置

w.fn.init [input#d1]
$('#d2').val()
"C:\fakepath\01_测试路由.png"
$('#d2')[0].files[0] # 牢记两个对象之间的转换
File {name: "01_测试路由.png", lastModified: 1557043083000, lastModifiedDate: Sun May 05 2019 15:58:03 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 28733, …}


# 属性操作
"""
js中 jQuery
setAttribute() attr(name,value)
getAttribute() attr(name)
removeAttribute() removeAttr(name)

在用变量存储对象的时候 js中推荐使用
XXXEle 标签对象
jQuery中推荐使用
$XXXEle jQuery对象
"""
let $pEle = $('p')
undefined
$pEle.attr('id')
"d1"
$pEle.attr('class')
undefined
$pEle.attr('class','c1')
w.fn.init [p#d1.c1, prevObject: w.fn.init(1)]
$pEle.attr('id','id666')
w.fn.init [p#id666.c1, prevObject: w.fn.init(1)]
$pEle.attr('password','jason123')
w.fn.init [p#id666.c1, prevObject: w.fn.init(1)]
$pEle.removeAttr('password')
w.fn.init [p#id666.c1, prevObject: w.fn.init(1)]


"""
对于标签上有的能够看到的属性和自定义属性用attr
对于返回布尔值比如checkbox radio option是否被选中用prop
"""

$('#d3').attr('checked')
"checked"
$('#d2').attr('checked')
undefined
$('#d2').attr('checked')
undefined
$('#d4').attr('checked')
undefined
$('#d3').attr('checked')
"checked"
$('#d3').attr('checked','checked') # 无效
w.fn.init [input#d3]


$('#d2').prop('checked')
false
$('#d2').prop('checked')
true
$('#d3').prop('checked',true)
w.fn.init [input#d3]
$('#d3').prop('checked',false)
w.fn.init [input#d3]


# 文档处理
"""
js jQuery
createElement('p') $('<p>')
appendChild() append()

"""
let $pEle = $('<p>')
$pEle.text('你好啊 草莓要不要来几个?')
$pEle.attr('id','d1')
$('#d1').append($pEle) # 内部尾部追加
$('#d1').append($pEle[0]) # 内部尾部追加
$pEle.appendTo($('#d1'))

$('#d1').prepend($pEle) # 内部头部追加
w.fn.init [div#d1]
$pEle.prependTo($('#d1'))
w.fn.init [p#d1, prevObject: w.fn.init(1)]

$('#d2').after($pEle) # 放在某个标签后面
w.fn.init [p#d2]
$pEle.insertAfter($('#d1'))

$('#d1').before($pEle)
w.fn.init [div#d1]
$pEle.insertBefore($('#d2'))

$('#d1').remove() # 将标签从DOM树中删除
w.fn.init [div#d1]

$('#d1').empty() # 清空标签内部所有的内容
w.fn.init [div#d1]

事件

1
2
3
4
5
6
7
8
9
// 第一种
$('#d1').click(function () {
alert('别说话 吻我')
});

// 第二种(功能更加强大 还支持事件委托)
$('#d2').on('click',function () {
alert('借我钱买草莓 后面还你')
})

克隆事件

1
2
3
4
5
6
7
8
9
10
<button id="d1">屠龙宝刀,点击就送</button>

<script>
$('#d1').on('click',function () {
// console.log(this) // this指代是当前被操作的标签对象
// $(this).clone().insertAfter($('body')) // clone默认情况下只克隆html和css 不克隆事件
$(this).clone(true).insertAfter($('body')) // 括号内加true即可克隆事件

})
</script>

左侧菜单

1
2
3
4
5
6
7
8
<script>
$('.title').click(function () {
// 先给所有的items加hide
$('.items').addClass('hide')
// 然后将被点击标签内部的hide移除
$(this).children().removeClass('hide')
})
</script>

返回顶部

1
2
3
4
5
6
7
8
9
<script>
$(window).scroll(function () {
if($(window).scrollTop() > 300){
$('#d1').removeClass('hide')
}else{
$('#d1').addClass('hide')
}
})
</script>

自定义登陆校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 在获取用户的用户名和密码的时候 用户如果没有填写 应该给用户展示提示信息
<p>username:
<input type="text" id="username">
<span style="color: red"></span>
</p>
<p>password:
<input type="text" id="password">
<span style="color: red"></span>
</p>
<button id="d1">提交</button>

<script>
let $userName = $('#username')
let $passWord = $('#password')
$('#d1').click(function () {
// 获取用户输入的用户名和密码 做校验
let userName = $userName.val()
let passWord = $passWord.val()
if (!userName){
$userName.next().text("用户名不能为空")
}
if (!passWord){
$passWord.next().text('密码不能为空')
}
})
$('input').focus(function () {
$(this).next().text('')
})
</script>

input实时监控

1
2
3
4
5
6
7
<input type="text" id="d1">

<script>
$('#d1').on('input',function () {
console.log(this.value)
})
</script>

hover事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// $("#d1").hover(function () { // 鼠标悬浮 + 鼠标移开
// alert(123)
// })

$('#d1').hover(
function () {
alert('我来了') // 悬浮
},
function () {
alert('我溜了') // 移开
}
)
</script>

键盘按键按下事件

1
2
3
4
5
6
7
8
<script>
$(window).keydown(function (event) {
console.log(event.keyCode)
if (event.keyCode === 16){
alert('你按了shift键')
}
})
</script>

阻止后续事件执行

1
2
3
4
5
6
7
8
9
<script>
$('#d2').click(function (e) {
$('#d1').text('宝贝 你能看到我吗?')
// 阻止标签后续事件的执行 方式1
// return false
// 阻止标签后续事件的执行 方式2
// e.preventDefault()
})
</script>

阻止事件冒泡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
$('#d1').click(function () {
alert('div')
})
$('#d2').click(function () {
alert('p')
})
$('#d3').click(function (e) {
alert('span')
// 阻止事件冒泡的方式1
// return false
// 阻止事件冒泡的方式2
// e.stopPropagation()
})
</script>

事件委托

1
2
3
4
5
6
7
8
9
10
11
12
13
<button>是兄弟,就来砍我!!!</button>

<script>
// 给页面上所有的button标签绑定点击事件
// $('button').click(function () { // 无法影响到动态创建的标签
// alert(123)
// })

// 事件委托
$('body').on('click','button',function () {
alert(123) // 在指定的范围内 将事件委托给某个标签 无论该标签是事先写好的还是后面动态创建的
})
</script>

页面加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 等待页面加载完毕再执行代码
window.onload = function(){
// js代码
}

"""jQuery中等待页面加载完毕"""
# 第一种
$(document).ready(function(){
// js代码
})
# 第二种
$(function(){
// js代码
})
# 第三种
"""直接写在body内部最下方"""

动画效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('#d1').hide(5000)
w.fn.init [div#d1]
$('#d1').show(5000)
w.fn.init [div#d1]
$('#d1').slideUp(5000)
w.fn.init [div#d1]
$('#d1').slideDown(5000)
w.fn.init [div#d1]
$('#d1').fadeOut(5000)
w.fn.init [div#d1]
$('#d1').fadeIn(5000)
w.fn.init [div#d1]
$('#d1').fadeTo(5000,0.4)
w.fn.init [div#d1]

补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# each()
# 第一种方式
$('div')
w.fn.init(10) [div, div, div, div, div, div, div, div, div, div, prevObject: w.fn.init(1)]
$('div').each(function(index){console.log(index)})
VM181:1 0
VM181:1 1
VM181:1 2
VM181:1 3
VM181:1 4
VM181:1 5
VM181:1 6
VM181:1 7
VM181:1 8
VM181:1 9

$('div').each(function(index,obj){console.log(index,obj)})
VM243:1 0 <div>•1•</div>•
VM243:1 1 <div>•2•</div>•
VM243:1 2 <div>•3•</div>•
VM243:1 3 <div>•4•</div>•
VM243:1 4 <div>•5•</div>•
VM243:1 5 <div>•6•</div>•
VM243:1 6 <div>•7•</div>•
VM243:1 7 <div>•8•</div>•
VM243:1 8 <div>•9•</div>•
VM243:1 9 <div>•10•</div>•

# 第二种方式
$.each([111,222,333],function(index,obj){console.log(index,obj)})
VM484:1 0 111
VM484:1 1 222
VM484:1 2 333
(3) [111, 222, 333]
"""
有了each之后 就无需自己写for循环了 用它更加的方便
"""
# data()
"""
能够让标签帮我们存储数据 并且用户肉眼看不见
"""
$('div').data('info','回来吧,我原谅你了!')
w.fn.init(10) [div#d1, div, div, div, div, div, div, div, div, div, prevObject: w.fn.init(1)]

$('div').first().data('info')
"回来吧,我原谅你了!"
$('div').last().data('info')
"回来吧,我原谅你了!"

$('div').first().data('xxx')
undefined
$('div').first().removeData('info')
w.fn.init [div#d1, prevObject: w.fn.init(10)]

$('div').first().data('info')
undefined
$('div').last().data('info')
"回来吧,我原谅你了!"