Python 高级教程
# Python3 正则表达式
正则表达式是一个特殊的字符序列,它能帮助开发者检查一个字符串是否与某种模式匹配。在Python中,使用re模块来处理正则表达式。re模块提供了一组函数,允许在字符串中进行模式匹配、搜索和替换操作。re模块使Python语言拥有完整的正则表达式功能。
# re.match 函数
re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None。
函数语法: re.match(pattern, string, flags=0)
函数参数说明:
| 参数 | 描述 |
|---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串 |
flags | 标志位,用于控制正则表达式的匹配方式,如: 是否区分大小写,多行匹配等等。参见正则表达式修饰符-可选标志 |
匹配成功re.match方法返回一个匹配的对象,否则返回None。可以使用group(num)或groups()匹配对象函数来获取匹配表达式。
| 匹配对象方法 | 描述 |
|---|---|
group(num=0) | 匹配的整个表达式的字符串,group()可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从1到所含的小组号。 |
import re
print(re.match('www', 'www.runoob.com').span()) # 在起始位置匹配
print(re.match('com', 'www.runoob.com')) # 不在起始位置匹配
# 输出
# (0, 3)
# None
2
3
4
5
6
7
8
import re
line = "Cats are smarter than dogs"
# .* 表示任意匹配除换行符 (\n, \r) 之外的任何单个或多个字符
# (.*?) 表示"非贪婪"模式,只保存第一个匹配到的子串
matchObj = re.match(r'(.*) are (.*?) .*', line, re.M|re.I)
if matchObj:
print("matchObj.group(): ", matchObj.group())
print("matchObj.group(1): ", matchObj.group(1))
print("matchObj.group(2): ", matchObj.group(2))
else:
print("No match!!")
2
3
4
5
6
7
8
9
10
11
12
13
# re.search 方法
re.search扫描这个字符串并返回第一个成功的匹配。
函数语法: re.search(pattern, string, flags=0)
函数参数说明:
| 参数 | 描述 |
|---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串 |
flags | 标志位,用于控制正则表达式的匹配方式,如: 是否区分大小写,多行匹配等等。参见正则表达式修饰符-可选标志 |
匹配成功re.search方法返回一个匹配的对象,否则返回None。可以使用group(num)或groups()匹配对象函数来获取匹配表达式。
| 匹配对象方法 | 描述 |
|---|---|
group(num=0) | 匹配的整个表达式的字符串,group()可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从1到所含的小组号。 |
import re
print(re.search('www', 'www.runoob.com').span()) # 在其实位置匹配
print(re.search('com', 'www.runoob.com').span()) # 不在其实配置匹配
# (0, 3)
# (11, 14)
2
3
4
5
6
7
#!/usr/bin/python3
import re
line = "Cats are smarter than dogs"
searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)
if searchObj:
print ("searchObj.group() : ", searchObj.group())
print ("searchObj.group(1) : ", searchObj.group(1))
print ("searchObj.group(2) : ", searchObj.group(2))
else:
print ("Nothing found!!")
2
3
4
5
6
7
8
9
10
11
12
13
14
# re.match 与 re.search 的区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None,而re.search匹配整个字符串,直到找到一个匹配。
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'dogs', line, re.M|re.I)
if matchObj:
print("match --> matchObj.group(): ", matchObj.group())
else:
print("No match!!")
matchObj = re.search(r'dogs', line, re.M|re.I)
if matchObj:
print("search --> matchObj.group(): ", matchObj.group())
else:
print("No match!!")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检索和替换
Python的re模块提供了re.sub用于替换字符串中的匹配项。
语法: re.sub(pattern, repl, string, count=0, flags=0)
参数:
pattern: 正则中的模式字符串repl: 替换的字符串,也可为一个函数string: 要被查找替换的原始字符串count: 模式匹配后替换的最大次数,默认0表示替换所有的匹配flags: 编译时用的匹配模式,数字形式。
前三个为必选参数,后两个为可选参数。
import re
phone = "2004-959-559" # 这是一个电话号码
# 删除注释
num = re.sub(r'#.*$', "", phone)
print("电话号码: ", num)
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print("电话号码: ", num)
# 电话号码: 2004-959-559
# 电话号码: 2004959559
2
3
4
5
6
7
8
9
10
11
12
13
14
repl参数是一个函数:
import re
# 将匹配的数字乘以2
def double(matched):
value = int(matched.group('value'))
return str(value * 2)
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s))
# A46G8HFD1134
2
3
4
5
6
7
8
9
10
11
compile 函数: compile函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()和search()这两个函数使用。语法: re.compile(pattern[, flags])
参数:
pattern: 一个字符串形式的正则表达式flags: 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:re.IGNORECASE或re.I: 使匹配大小写不敏感re.L: 表示特殊字符集\w、\W、\b、\B、\s、\S依赖于当前环境re.MULTILINE或re.M: 多行模式,改变^和$的行为,使它们匹配字符串的每一行的开头和结尾re.DOTALL或re.S: 使.匹配包括换行符在内的任意字符re.ASCII: 使\w、\W、\b、\B、\d、\D、\s、\S仅匹配ASCII字符。re.VERBOSE或re.X: 忽略空格和注释,可以更清晰地组织复杂的正则表达式。
这些标志可以单独使用,也可以通过按位或(|)组合使用。例如,re.IGNORECASE|re.MULTILINE表示同时启用忽略大小写和多行模式。
import re
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
m = pattern.match('one12twothree34four') # 查找头部,没有匹配
print(m) # None
m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
print(m) # None
m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配,返回一个 Match 对象
print(m) # <_sre.SRE_Match object at 0x10a42aac0
a = m.group(0) # 可省略0
print(a) # '12'
b = m.start(0) # 可省略0
print(b) # 3
c = m.end(0) # 可省略0
print(c) # 5
d = m.span(0) # 可省略0
print(d) # (3, 5)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在上面,当匹配成功时返回一个Match对象,其中:
group([group1, ...]): 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用group()或group(0)start([group]): 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为0end([group]): 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为0span([group]): 方法返回(start(group), end(group))
import re
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写
m = pattern.match('Hello World Wide Web')
print(m) # 匹配成功,返回一个 Match 对象
a = m.group(0) # 返回匹配成功的整个子串
print(a) # 'Hello World'
b = m.span(0) # 返回匹配成功的整个子串的索引
print(b) # (0, 11)
c = m.group(1) # 返回第一个分组匹配成功的子串
print(c) # 'Hello'
d = m.span(1) # 返回第一个分组匹配成功的子串的索引
print(d) # (0, 5)
e = m.group(2) # 返回第二个分组匹配成功的子串
print(e) # 'World'
f = m.span(2) # 返回第二个分组匹配成功的子串索引
print(f) # (6, 11)
g = m.groups() # 等价于 (m.group(1), m.group(2), ...)
print(g) # ('Hello', 'World')
# h = m.group(3) # 不存在第三个分组
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# IndexError: no such group
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

findall: 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
注意
match 和 search 是匹配一次,而 findall 匹配所有,语法:
re.findall(pattern, string, flags=0)
# 或
pattern.findall(string[, pos[, endpos]])
2
3
参数:
pattern: 匹配模式string: 待匹配的字符串pos: 可选参数,指定字符串的起始位置,默认为0endpos: 可选参数,指定字符串的结束位置,默认为字符串的长度。
# 查找字符串的所有数字
import re
result1 = re.findall(r'\d+', 'runoob 123 google 456')
pattern = re.compile(r'\d+') # 查找数字
result2 = pattern.findall('runoob 123 google 456')
result3 = pattern.findall('run88oob123google456', 0, 10)
print(result1)
print(result2)
print(result3)
2
3
4
5
6
7
8
9
10
11
12
# 多个匹配模式,返回元组列表
import re
result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(result)
# [('width', '20'), ('height', '10')]
2
3
4
5
6
7
re.finditer: 和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。语法: re.finditer(pattern, string, flags=0)
参数:
| 参数 | 描述 |
|---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串 |
flags | 标志位,用于控制正则表达式的匹配方式,如: 是否区分大小写,多行匹配等等。参见: 正则表达式修饰符-可选标志 |
import re
it = re.finditer(r"\d+", "12a32bc43jf3")
for match in it:
print(match.group())
2
3
4
5
re.split: split方法按照能够匹配的子串将字符串分隔后返回列表,语法如下: re.split(pattern, string[, maxsplit=0, flags=0])
参数:
| 参数 | 描述 |
|---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串 |
maxsplit | 分隔次数,maxsplit=1 分隔一次,默认为0,不限制次数 |
flags | 标志位,用于控制正则表达式的匹配方式,如: 是否区分大小写,多行匹配等,参见: 正则表达式修饰符-可选标志 |
import re
a = re.split('\W+', 'runoob, runoob, runoob.')
print(a) # ['runoob', 'runoob', 'runoob', '']
b = re.split('(\W+)', ' runoob, runoob, runoob.')
print(b) # ['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', '']
c = re.split('\W+', ' runoob, runoob, runoob.', 1)
print(c) # ['', 'runoob, runoob, runoob.']
d = re.split('a', 'hello world') # 对于一个找不到匹配的字符串而言,split 不会对其做出分隔
print(d) # ['hello world']
2
3
4
5
6
7
8
9
10
11
12
13
# 正则表达式对象
re.RegexObject: re.compile()返回RegexObject对象。
re.MatchObject: group()返回被RE匹配的字符串。
start(): 返回匹配开始的位置end(): 返回匹配结束的位置span(): 返回一个元组包含匹配(开始, 结束)的位置
# 正则表达式修饰符 - 可选标志
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。以下标志可以单独使用,也可以通过按位或(|)组合使用。例如,re.IGNORECASE|re.MULTILINE表示同时启用忽略大小写和多行模式。
| 修饰符 | 描述 | 实例 |
|---|---|---|
re.IGNORECASE或re.I | 使匹配对大小写不敏感 | |
re.MULTILINE或re.M | 多行匹配,影响^和$,使它们匹配字符串的每一行的开头和结尾 | |
re.DOTALL或re.S | 使.匹配包括换行符在内的任意字符 | |
re.ASCII | 使\w、\W、\b、\B、\d、\D、\s、\S仅匹配ASCII字符。 | |
re.VERBOSE或re.X | 忽略空格和注释,可以更清晰地组织复杂的正则表达式。 |
# 正则表达式模式
模式字符串使用特殊的语法来表示一个正则表达式。字母和数字表示它们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。多数字母和数字前加一个反斜杠时会拥有不同的含义。标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。反斜杠本身需要使用反斜杠转义。由于正则表达式通常都包含反斜杠,所以最好使用原始字符串来表示它们。模式元素(如r'\t',等价于\\t)匹配相应的特殊字符。
下表列出了正则表达式模式语法中的特殊元素,如果使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。
| 模式 | 描述 |
|---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包含换行符的任意字符 |
[...] | 用来匹配所包含的任意一个字符,例如[amk]匹配a, m或k |
[^...] | 不在[]中的字符: [^abc]匹配除了a, b, c之外的字符 |
re* | 匹配0或多个表达式 |
re+ | 匹配1或多个表达式 |
re? | 匹配0或1个由前面的正则表达式定义的片段,非贪婪模式 |
re{n} | 匹配n个前面表达式,例如,o{2}不能匹配Bob中的o,但是能匹配food中的两个o |
re{n,} | 精确匹配n个前面表达式,例如,o{2,}不能匹配Bob中的o,但能匹配foooood中的所有o。 o{1,}等价于o+,o{0,}则等价于o* |
re{n, m} | 匹配n到m次由前面的正则表达式定义的片段,贪婪方式 |
a 竖线 b | 匹配a或b |
(re) | 匹配括号内的表达式,也表示一个组 |
(?imx) | 正则表达式包含三种可选标志: i, m, 或 x。只影响括号中的区域 |
(?-imx) | 正则表达式关闭i, m, 或 x 可选标志,只影响括号中的区域 |
(?:re) | 类似(...),但是不表示一个组 |
(?imx:re) | 在括号中使用i, m, 或 x 可选标志 |
(?-imx:re) | 在括号中不使用i, m, 或 x 可选标志 |
(?#...) | 注释 |
(?=re) | 前向肯定界定符。如果所含正则表达式,以...表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高,模式的剩余部分还要尝试界定符的右边 |
(?!re) | 前向否定界定符,与肯定界定符相反,当所含表达式不能在字符串当前位置匹配时成功 |
(?>re) | 匹配的独立模式,省去回溯 |
\w | 匹配数字字母下划线 |
\W | 匹配非数字字母下划线 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b可以匹配never中的er,但不能匹配verb中的er |
\B | 匹配非单词边界,er\B能匹配verb中的er,但不能匹配never中的er |
\n, \t 等 | 匹配一个换行符,匹配一个制表符等 |
\1...\9 | 匹配第n个分组的内容 |
\10 | 匹配第n个分组的内容,如果它经匹配,否则指的是八进制字符码的表达式 |
# 正则表达式实例
字符匹配:
| 实例 | 描述 |
|---|---|
python | 匹配"python" |
[Pp]ython | 匹配"Python"或"python" |
rub[ye] | 匹配"ruby"或"rube" |
[aeiou] | 匹配中括号内的任意一个字母 |
[0-9] | 匹配任何数字,类似0123456789 |
[a-z] | 匹配任何小写字母 |
[A-Z] | 匹配任何大写字母 |
[a-zA-Z0-9] | 匹配任何字母及数字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
[^0-9] | 匹配除了数字外的字符 |
. | 匹配除\n之外的任何单个字符,要匹配包括\n在内的任何字符,可以使用[.\n] |
\d | 匹配一个数字字符,等价于[0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\s | 匹配任何空白字符,包括空格、制表符、换页符德国,等价[\f\n\r\t\v] |
\S | 匹配包括下划线的任何单词字符,等价于[A-Za-z0-9] |
\w | 匹配包括下划线的任何单词字符,等价于[A-Za-z0-9] |
\W | 匹配任何非单词字符,等价于[A-Za-z0-9_] |
# Python3 CGI编程
# 什么是CGI
CGI目前由NCSA维护,NCSA定义CGI如下: CGI(Common Gateway Interface),通用网关接口,它是一段程序,运行在服务器上如: HTTP服务器,提供同客户端HTML页面的接口。
# 网页浏览
- 使用浏览器访问URL并连接到HTTP web服务器
- Web服务器接收到请求信息后会解析URL,并查找访问的文件在服务器上是否存在,如果存在返回文件内容,否则返回错误信息。
- 浏览器从服务器上接收信息,并显示接收的文件或者错误信息。
CGI程序可以是Python脚本,PERL脚本,SHELL脚本,C或者C++程序等。
# CGI架构图

# Web服务器支持及配置
在进行CGI编程前,需要确保Web服务器支持CGI及已经配置了CGI的处理程序。Apache支持CGI的配置:
设置号CGI目录: ScriptAlias /cgi-bin/ /var/www/cgi-bin/
所有的HTTP服务器执行CGI程序都保存在一个预先配置的目录。这个目录被称为CGI目录,并按照管理,被命名为/var/www/cgi-bin。CGI文件的扩展名为.cgi,python也可以使用.py扩展名。默认情况下,linux服务器配置巡行的cgi-bin目录为/var/www。如果想指定其他运行CGI脚本的目录,可以修改httpd.conf配置文件,如下:
<Directory "/var/www/cgi-bin">
AllowOverride None
Options +ExecCGI
Order allow, deny
Allow from all
</Directory>
2
3
4
5
6
在AddHandler中添加.py后缀,这样就可以访问.py结尾的python脚本文件: AddHandler cgi-script .cgi .pl .py
# 第一个CGI程序
使用Python创建第一个CGI程序,文件名为hello.py,文件位于/var/www/cgi-bin目录,内容:
#!/usr/bin/python3
print ("Content-type:text/html")
print () # 空行,告诉服务器结束头部
print ('<html>')
print ('<head>')
print ('<meta charset="utf-8">')
print ('<title>Hello Word - 我的第一个 CGI 程序!</title>')
print ('</head>')
print ('<body>')
print ('<h2>Hello Word! 我是来自菜鸟教程的第一CGI程序</h2>')
print ('</body>')
print ('</html>')
2
3
4
5
6
7
8
9
10
11
12
13
文件保存后修改文件权限为755: chmod 755 hello.py
然后通过浏览器访问: http://ip/cgi-bin/hello.py

这个脚本是一个简单的Python脚本,脚本第一行输出Content-Type:text/html发送到浏览器并告知浏览器显示的内容类型,用print输出一个空行告诉服务器结束头部信息。
# HTTP头部
hello.py文件内容中的Content-Type:text/html即为HTTP头部的一部分,它会发送给浏览器文件的内容类型,HTTP头部的格式如:
HTTP 字段名: 字段内容 如: Content-Type: text/html
以下表格介绍了CGI程序中HTTP头部经常使用的信息:
| 头 | 描述 |
|---|---|
Content-type | 请求的与实体对应的MIME信息,如: Content-type: text/html |
Expires: Date | 响应过期的日期和时间 |
Location: URL | 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 |
Last-modified: Date | 请求资源的最后修改时间 |
Content-length: N | 请求的内容长度 |
Set-Cookie: String | 设置Http Cookie |
# CGI环境变量
所有的CGI程序都接收以下的环境变量,这些变量在CGI程序中发挥了重要的作用:
| 变量名 | 描述 |
|---|---|
CONTENT_TYPE | 这个环境变量的值指示所传递来的信息MIME类型。目前,环境变量CONTENT_TYPE一般都是: application/x-www-form-urlencoded,表示数据来自HTML表单。 |
CONTENT_LENGTH | 如果服务器与CGI程序信息的传递方式是POST,这个环境变量指示从标准输入STDIN中可以读到有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。 |
HTTP_COOKIE | 客户机内的COOKIE内容 |
HTTP_USER_AGENT | 提供包含了版本数或其他专有数据的客户浏览器信息。 |
PATH_INFO | 这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。 |
QUERY_STRING | 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值既是所传递的信息。这个信息跟在CGI程序名的后面,两者中间用一个问号?分隔。 |
REMOTE_ADDR | 这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67.这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。 |
REMOTE_HOST | 这个环境变量的值包含发送CGI请求的客户机的主机名,也可以不定义此变量。 |
REQUEST_METHOD | 提供脚本被调用的方法。对于使用HTTP/1.0协议的脚本,仅GET/POST有意义 |
SCRIPT_FILENAME | CGI脚本的完整路径 |
SCRIPT_NAME | CGI脚本的名称 |
SERVER_NAME | WEB服务器的主机名、别名或IP地址 |
SERVER_SOFTWARE | 这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号,例如,上面的值为Apache/2.2.14(Unix) |
以下是一个简单的CGI脚本输出CGI的环境变量:
#!/usr/bin/python3
import os
print ("Content-type: text/html")
print ()
print ("<meta charset=\"utf-8\">")
print ("<b>环境变量</b><br>")
print ("<ul>")
for key in os.environ.keys():
print ("<li><span style='color:green'>%30s </span> : %s </li>" % (key,os.environ[key]))
print ("</ul>")
2
3
4
5
6
7
8
9
10
11
12
保存为test.py,并修改权限为755,访问查看结果:

# GET和POST方法
浏览器客户端通过两种方法向服务器传递信息,这两种方法就是GET和POST方法。
# 使用GET方法传输数据
GET方法发送编码后的用户信息到服务端,数据信息包含在请求页面的URL上,以?号分隔,如下: http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
有关GET请求的一些说明:
- 请求可被缓存
- 请求保留在浏览器历史中
- 请求可被收藏为书签
- 请求不应在处理敏感数据时使用
- 请求有长度限制
- 请求只应当用于取回数据
# 简单的URL实例: GET方法
/cgi-bin/hello_get.py?name=菜鸟教程&url=http://www.runoob.com
#!/usr/bin/python3
# CGI处理模块
import cgi, cgitb
# 创建 FieldStorage 的实例化
form = cgi.FieldStorage()
# 获取数据
site_name = form.getvalue('name')
site_url = form.getvalue('url')
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2>%s官网:%s</h2>" % (site_name, site_url))
print ("</body>")
print ("</html>")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 简单的表单实例: GET方法
以下是一个通过HTML表单使用GET方法向服务器发送两个数据,提交的服务器脚本同样是hello_get.py,html代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="get">
站点名称: <input type="text" name="name"> <br />
站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 使用POST方法传递数据
使用POST方法向服务器传递数据是更安全可靠的,像一些敏感信息如用户密码等需要使用POST传输数据。同样是hello_get.py,也可以处理浏览器提交的POST表单数据:
#!/usr/bin/python3
# CGI处理模块
import cgi, cgitb
# 创建 FieldStorage 的实例化
form = cgi.FieldStorage()
# 获取数据
site_name = form.getvalue('name')
site_url = form.getvalue('url')
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2>%s官网:%s</h2>" % (site_name, site_url))
print ("</body>")
print ("</html>")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
以下为表单通过POST方法(method="post")向服务器脚本hello_get.py提交数据:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="post">
站点名称: <input type="text" name="name"> <br />
站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>
</form>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 通过CGI程序传递checkbox数据
checkbox用于提交一个或者多个选项数据,HTML代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/checkbox.py" method="POST" target="_blank">
<input type="checkbox" name="runoob" value="on" /> 菜鸟教程
<input type="checkbox" name="google" value="on" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
以下为checkbox.py的代码:
#!/usr/bin/python3
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('google'):
google_flag = "是"
else:
google_flag = "否"
if form.getvalue('runoob'):
runoob_flag = "是"
else:
runoob_flag = "否"
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 菜鸟教程是否选择了 : %s</h2>" % runoob_flag)
print ("<h2> Google 是否选择了 : %s</h2>" % google_flag)
print ("</body>")
print ("</html>")
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

# 通过CGI程序传递Radio数据
Radio只向服务器传递一个数据,HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/radiobutton.py" method="post" target="_blank">
<input type="radio" name="site" value="runoob" /> 菜鸟教程
<input type="radio" name="site" value="google" /> Google
<input type="submit" value="提交" />
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
radiobutton.py代码:
#!/usr/bin/python3
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('site'):
site = form.getvalue('site')
else:
site = "提交数据为空"
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 选中的网站是 %s</h2>" % site)
print ("</body>")
print ("</html>")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 通过CGI程序传递Textarea数据
Textarea向服务器传递多行数据,HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/textarea.py" method="post" target="_blank">
<textarea name="textcontent" cols="40" rows="4">
在这里输入内容...
</textarea>
<input type="submit" value="提交" />
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
textarea.py:
#!/usr/bin/python3
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('textcontent'):
text_content = form.getvalue('textcontent')
else:
text_content = "没有内容"
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 输入的内容是:%s</h2>" % text_content)
print ("</body>")
print ("</html>")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 通过CGI传递下拉数据
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form action="/cgi-bin/dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="runoob" selected>菜鸟教程</option>
<option value="google">Google</option>
</select>
<input type="submit" value="提交"/>
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dropdown.py:
#!/usr/bin/python3
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('dropdown'):
dropdown_value = form.getvalue('dropdown')
else:
dropdown_value = "没有内容"
print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>菜鸟教程 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 选中的选项是:%s</h2>" % dropdown_value)
print ("</body>")
print ("</html>")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# CGI中使用Cookie
HTTP一个很大的缺点就是不对用户的身份进行判断,而COOKIE的出现弥补了这个缺点。cookie就是客户访问脚本的同时,通过客户的浏览器,在客户硬盘上写入记录数据,当下次客户访问脚本时取回数据信息,从而达到身份判断的功能,cookie常用在身份校验中。
cookie语法: http cookie的发送是通过http头部实现的,其早于文件传递,头部set-cookie的语法如下: Set-cookie:name=name;expires=date;path=path;domain=domain;secure
name=name: 需要设置cookie的值,有多个name时用;分隔,如: name1=name1;name2=name2;name3=name3。expires=date: cookie的有效期限,格式:expires="Wdy,DD-Mon-YYYY HH:MM:SS"path=path: 设置cookie支持的路径,如果path是一个路径,则cookie对这个目录下的所有文件及子目录生效,例如:path="/cgi-bin/",如果是一个文件,则cookie只对这个文件生效,如:path="/cgi-bin/cookie.cgi".domain=domain: 对cookie生效的域名,如:domain="www.runoob.com"secure: 如果给出此标志,表示cookie只能通过SSL协议的https服务器来传递。- cookie的接收是通过设置环境变量HTTP_COOKIE来实现,CGI程序可以通过检索该变量获取cookie信息。
# Cookie设置
Cookie设置非常简单,cookie会在http头部单独发送,如下实例在cookie中设置了name和expires:
#!/usr/bin/python3
print ('Set-Cookie: name="菜鸟教程";expires=Wed, 28 Aug 2016 18:30:00 GMT')
print ('Content-Type: text/html')
print ()
print ("""
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>Cookie set OK!</h1>
</body>
</html>
""")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

以上实例使用了Set-Cookie头信息来设置Cookie信息,可选项中设置了Cookie的其他属性,如过期时间Expires,域名Domain,路径Path,这些信息设置在Content-type:text/html。
# 检索Cookie信息
Cookie信息检索页非常简单,Cookie信息存储在CGI的环境变量HTTP_COOKIE中,存储格式如下: key1=value1;key2=value2;key3=value3...
#!/usr/bin/python3
# 导入模块
import os
import http.cookies
print ("Content-type: text/html")
print ()
print ("""
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>读取cookie信息</h1>
""")
if 'HTTP_COOKIE' in os.environ:
cookie_string=os.environ.get('HTTP_COOKIE')
c= http.cookies.SimpleCookie()
# c=Cookie.SimpleCookie()
c.load(cookie_string)
try:
data=c['name'].value
print ("cookie data: "+data+"<br>")
except KeyError:
print ("cookie 没有设置或者已过去<br>")
print ("""
</body>
</html>
""")
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

# 文件上传实例
HTML设置上传文件的表单需要设置enctype属性为multipart/form-data,代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<form enctype="multipart/form-data"
action="/cgi-bin/save_file.py" method="post">
<p>选中文件: <input type="file" name="filename" /></p>
<p><input type="submit" value="上传" /></p>
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python3
import cgi, os
import cgitb; cgitb.enable()
form = cgi.FieldStorage()
# 获取文件名
fileitem = form['filename']
# 检测文件是否上传
if fileitem.filename:
# 设置文件路径
fn = os.path.basename(fileitem.filename)
# 如果使用Unix/Linux,必须替换文件分隔符
# fn = os.path.basename(fileitem.filename.replace("\\", "/"))
open('/tmp/' + fn, 'wb').write(fileitem.file.read())
message = '文件 "' + fn + '" 上传成功'
else:
message = '文件没有上传'
print ("""\
Content-Type: text/html\n
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<p>%s</p>
</body>
</html>
""" % (message,))
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

# 文件下载对话框
先在当前目录创建foo.txt,用于程序下载。文件下载通过设置HTTP信息头来实现,代码:
#!/usr/bin/python3
# HTTP 头部
print ("Content-Disposition: attachment; filename=\"foo.txt\"")
print ()
# 打开文件
fo = open("foo.txt", "rb")
str = fo.read()
print (str)
# 关闭文件
fo.close()
2
3
4
5
6
7
8
9
10
11
12
13
# Python3 MySQL(mysql-connector)
mysql-connector连接MySQL,mysql-connector是MySQL官方提供的驱动器,可以使用python -m pip install mysql-connector安装。
使用以下代码demo_mysql_test.py测试安装是否成功:
import mysql.connector
# 执行以上代码,如果没有产生错误,证明安装成功
# 如果MySQL是8.0,密码插件验证方式发生了变化,早期版本为mysql_native_password,8.0版本为caching_sha2_password,需要做一些改变:
# 修改my.ini配置
# [mysqld]
# default_authentication_plugin=mysql_native_password
# 然后在mysql下执行以下命令修改密码
# ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';
2
3
4
5
6
7
8
# 创建数据库连接
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79", # 数据库主机地址
user="wind", # 数据库用户名
password="zlqf@2023!", # 数据库密码
database="ptrain" # 数据库名称
)
print(mydb)
2
3
4
5
6
7
8
9
10

# 创建数据库
创建数据库使用CREATE DATABASE语句,如:
import mysql.connector
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="123456"
)
mycursor = mydb.cursor()
mycursor.execute("CREATE DATABASE ptrain")
2
3
4
5
6
7
8
9
10
11
创建数据库前也可以使用SHOW DATABASES语句来查看数据库是否存在:
import mysql.connector
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="123456"
)
mycursor = mydb.cursor()
mycursor.execute("SHOW DATABASES")
for x in mycursor:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
或者可以直接连接数据库,如果数据库不存在,会输出错误:
import mysql.connector
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="123456",
database="runoob_db"
)
2
3
4
5
6
7
8
# 创建数据表
创建数据表使用CREATE TABLE语句,创建数据表前,需要确保数据库已存在,如:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79", # 数据库主机地址
user="wind", # 数据库用户名
password="zlqf@2023!", # 数据库密码
database="ptrain" # 数据库名称
)
mycursor = mydb.cursor()
mycursor.execute("create table sites (name VARCHAR(255), url VARCHAR(255))")
2
3
4
5
6
7
8
9
10
11
也可以使用SHOW TABLES来查看数据表是否已存在。
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79", # 数据库主机地址
user="wind", # 数据库用户名
password="zlqf@2023!", # 数据库密码
database="ptrain" # 数据库名称
)
mycursor = mydb.cursor()
mycursor.execute("show tables")
for x in mycursor:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
# 主键设置
创建表的时候一般都会设置一个主键(PRIMARY KEY),可以使用INT AUTO_INCREMENT PRIMARY KEY语句来创建一个主键,主键起始值为1,逐步递增。如果已经创建表,需要使用ALTER TABLE给表添加主键:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79", # 数据库主机地址
user="wind", # 数据库用户名
password="zlqf@2023!", # 数据库密码
database="ptrain" # 数据库名称
)
mycursor = mydb.cursor()
mycursor.execute("ALTER TABLE sites ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY")
2
3
4
5
6
7
8
9
10
11
如果还没创建表,可以使用如下代码创建:
import mysql.connector
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="123456",
database="runoob_db"
)
mycursor = mydb.cursor()
mycursor.execute("CREATE TABLE sites (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), url VARCHAR(255))")
2
3
4
5
6
7
8
9
10
11
# 插入数据
插入数据使用INSERT INTO:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79", # 数据库主机地址
user="wind", # 数据库用户名
password="zlqf@2023!", # 数据库密码
database="ptrain" # 数据库名称
)
mycursor = mydb.cursor()
sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"
val = ("RUNOOB", "https://www.runoob.com")
mycursor.execute(sql, val)
mydb.commit() # 数据表内容有更新,必须使用到该语句
print(mycursor.rowcount, "记录插入成功。")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 批量插入
批量插入使用executemany()方法,该方法的第二个参数是一个元组列表,包含了要插入的数据:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"
val = [
('Google', 'https://www.google.com'),
('Microsoft', 'https://www.microsoft.com'),
('Facebook', 'https://www.facebook.com'),
('Twitter', 'https://www.twitter.com')
]
mycursor.executemany(sql, val)
mydb.commit()
print(mycursor.rowcount, "record inserted.")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
如果想在数据记录插入后,获取该记录的ID,可以使用以下代码:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password ="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"
val = ('Zhihu', 'https://www.zhihu.com')
mycursor.execute(sql, val)
mydb.commit()
print("1 record inserted, ID:", mycursor.lastrowid)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查询数据
查询数据使用SELECT语句:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM sites")
myresult = mycursor.fetchall() # fetchall() 获取所有记录
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

也可以读取指定的字段:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT name, url FROM sites")
myresult = mycursor.fetchall()
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果只想读取一条数据,可以使用fetchone()方法:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM sites")
myresult = mycursor.fetchone() # fetchone() 获取一条记录
print(myresult)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# where 条件语句
读取指定的数据,可以使用where语句:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "SELECT * FROM sites WHERE name = 'RUNOOB'"
mycursor.execute(sql)
myresult = mycursor.fetchall()
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
也可以使用通配符%:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "SELECT * FROM sites WHERE url LIKE '%oo%'"
mycursor.execute(sql)
myresult = mycursor.fetchall()
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
未了防止数据库查询发生SQL注入,可以使用%s占位符来转义查询条件:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "SELECT * FROM sites WHERE name = %s"
na = ("RUNOOB",)
mycursor.execute(sql, na)
myresult = mycursor.fetchall()
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 排序
查询结果排序可以使用ORDER BY语句,默认的排序方式为升序,关键字为ASC,降序关键字为DESC:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
# sql = "SELECT * FROM sites ORDER BY name" # 升序
sql = "SELECT * FROM sites ORDER BY name DESC" # 降序
mycursor.execute(sql)
myresult = mycursor.fetchall()
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# LIMIT
如果要设置查询的数据量,使用LIMIT语句:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM sites LIMIT 3")
myresult = mycursor.fetchall() # fetchall() 获取所有记录
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# OFFSET
LIMIT和OFFSET配合,可以指定起始位置:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM sites LIMIT 3 OFFSET 1") # LIMIT 3 OFFSET 1 从第二条记录开始,获取三条记录
myresult = mycursor.fetchall() # fetchall() 获取所有记录
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除记录
删除记录使用DELETE FROM语句:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "DELETE FROM sites WHERE name = 'stackoverflow'"
mycursor.execute(sql)
mydb.commit()
print(mycursor.rowcount, " 条记录删除")
# 要慎重使用删除语句,删除语句要确保指定了WHERE条件语句,否则会导致整表数据删除。为了防止数据库查询发生SQL注入攻击,使用`%s`占位符来转义删除语句的条件
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "DELETE FROM sites WHERE name = %s"
na = ("Google", )
mycursor.execute(sql, na)
mydb.commit()
print(mycursor.rowcount, " 条记录删除")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 更新表数据
数据表更新使用UPDATE语句:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "UPDATE sites SET name = 'ZH' WHERE name = 'Zhihu'"
mycursor.execute(sql)
mydb.commit()
print(mycursor.rowcount, " 条记录被修改")
# UPDATE语句要确保指定了WHERE条件语句,否则会导致整表数据被更新。为了防止数据库发生SQL注入,使用`%s`
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 删除表
删除表使用DROP TABLE语句,IF EXISTS关键字用于判断表是否存在,只有在存在的情况下才删除:
import mysql.connector
mydb = mysql.connector.connect(
host="192.168.1.79",
user="wind",
password="zlqf@2023!",
database="ptrain"
)
mycursor = mydb.cursor()
sql = "DROP TABLE IF EXISTS sites" # 删除数据表 sites
mycursor.execute(sql)
2
3
4
5
6
7
8
9
10
11
12
# Python3 MySQL(PyMySQL)
PyMySQL是在Python3.x版本中用于连接MySQL服务器的一个库,Python2中使用的是mysqldb。 PyMySQL遵循Python数据库API v2.0 规范,并包含了 pure-Python MySQL客户端库。
# PyMySQL 安装
PyMySQL下载地址: https://github.com/PyMySQL/PyMySQL,如果还未安装,可以使用pip install PyMySQL安装最新版本。
如果系统不支持pip命令,可以使用以下方式安装:
- 使用git命令下载安装包(也可以手动下载)
$> git clone https://github.com/PyMySQL/PyMySQL
$> cd PyMySQL/
$> python setup.py install
2
3
- 如果需要指定版本号,使用
curl命令来安装:
$> curl -L https://github.com/PyMySQL/PyMySQL/tarball/pymysql-X.X | tar xz
$> cd PyMySQL*
$> python setup.py install
$> # 现在可以删除 PyMySQL* 目录
2
3
4
注意
安装的过程中可能会出现ImportError: No module named setuptools的错误提示,意思是没有安装setuptools,可以访问https://pypi.python.org/pypi/setuptools找各个系统的安装方法。
Linux系统安装实例:
$> wget https://bootstrap.pypa.io/ez_setup.py
$> python ez_setup.py
2
# 数据库连接
实例:
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. 执行SQL语句
cursor.execute("SELECT VERSION()")
# 4. 获取单条数据
data = cursor.fetchone()
print("Database version : %s " % data)
# 5. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建数据库表
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. 执行SQL语句,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS employee")
# 4. 使用预处理语句创建表
sql = """CREATE TABLE employee (
FIRST_NAME CHAR(20) NOT NULL,
LAST_NAME CHAR(20),
AGE INT,
SEX CHAR(1),
INCOME FLOAT )"""
cursor.execute(sql)
# 5. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 数据库插入操作
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. SQL 插入语句
sql = """INSERT INTO employee(FIRST_NAME,
LAST_NAME, AGE, SEX, INCOME)
VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 4. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
以上例子也可以写成:
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. SQL 插入语句
sql = """INSERT INTO employee(FIRST_NAME,
LAST_NAME, AGE, SEX, INCOME)
VALUES (%s, %s, %s, %s, %s)""" % \
('Mac', 'Mohan', 20, 'M', 2000)
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 4. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 数据库查询操作
PyMySQL查询MySQL使用fetchone()方法获取单条数据,使用fetchall()方法获取多条数据:
fetchone(): 该方法获取下一个查询结果集,结果集是一个对象fetchall(): 接收全部的返回结果行。rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. SQL 查询语句
sql = "SELECT * FROM employee \
WHERE INCOME > %s" % (1000)
try:
# 执行SQL语句
cursor.execute(sql)
# 获取所有记录列表
results = cursor.fetchall()
for row in results:
first_name = row[0]
last_name = row[1]
age = row[2]
sex = row[3]
income = row[4]
# 打印结果
print("first_name=%s,last_name=%s,age=%d,sex=%s,income=%d" % \
(first_name, last_name, age, sex, income))
except:
print("Error: unable to fetch data")
# 4. 关闭数据库连接
db.close()
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
# 数据库更新操作
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. SQL 更新语句
sql = "UPDATE employee SET AGE = AGE + 1 WHERE SEX = '%c'" % ('M')
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 4. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 删除操作
import pymysql
# 1. 创建数据库连接
db = pymysql.connect(host="192.168.1.79", user="wind", password="zlqf@2023!", database="ptrain")
# 2. 创建游标对象
cursor = db.cursor()
# 3. SQL 删除语句
sql = "DELETE FROM employee WHERE AGE > %s" % (20)
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 4. 关闭数据库连接
db.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 执行事务
事务机制可以确保数据一致性。事务应该具有4个属性: 原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
- 原子性(atomicity): 一个事务是一个不可分隔的工作单位,事务中包括的诸操作要么都成功,要么都失败。
- 一致性(consistency): 事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
- 隔离性(isolation): 一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability): 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
# SQL删除记录语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:
# 执行SQL语句
cursor.execute(sql)
# 向数据库提交
db.commit()
except:
# 发生错误时回滚
db.rollback()
2
3
4
5
6
7
8
9
10
对于支持事务的数据库,在Python数据库编程中,当游标建立时,就自动开启了一个隐形的事务。commit()提交游标的所有更新操作,rollback()回滚当前游标的所有操作。
# 错误处理
DB API中定义了一些数据库操作的错误及异常:
| 异常 | 描述 |
|---|---|
Warning | 当有严重警告时触发,例如插入数据是被截断的等等,必须是StandardError的子类 |
Error | 警告以外所有其他错误类,必须是StandardError的子类 |
InterfaceError | 当有数据库接口模块本身的错误(而不是数据库的错误)发生时触发,必须是Error的子类 |
DatabaseError | 和数据库有关的错误发生时触发,必须是Error的子类 |
DataError | 当有数据处理时的错误发生时触发,例如: 除零错误,数据超范围等等,必须是DatabaseError的子类 |
OperationalError | 指非用户控制的,而是操作数据库时发生的错误。例如: 链接意外断开、数据库名未找到、事务处理失败、内存分配错误等等操作数据库时发生的错误。必须是DatabaseError的子类 |
IntegrityError | 完整性相关的错误,例如外键检查失败等,必须是DatabaseError的子类 |
InternalError | 数据库的内部错误,例如游标失效、事务同步失败等,必须是DatabaseError的子类 |
ProgrammingError | 程序错误,例如数据表(table)未找到或已存在、SQL语句语法错误、参数数量错误等,必须是DatabaseError的子类 |
NotSupportedError | 不支持错误,指使用了数据库不支持的函数或API等,例如在连接对象上使用.rollback()函数,或数据库并不支持事务或事务已关闭等,必须是DatabaseError的子类。 |
Exception
|__Warning
|__Error
|__InterfaceError
|__DatabaseError
|__DataError
|__OperationalError
|__IntegrityError
|__InternalError
|__ProgrammingError
|__NotSupportedError
2
3
4
5
6
7
8
9
10
11
# Python3 PostgreSQL
# psycopg2
import psycopg2
from psycopg2 import sql
# 数据库连接参数
db_config = {
'database': 'ds_0',
'user': 'postgres',
'password': 'zqlf@2023!',
'host': '192.168.1.79',
'port': '5432'
}
try:
# 建立连接
conn = psycopg2.connect(**db_config)
# 创建游标
cursor = conn.cursor()
# 执行查询
cursor.execute("SELECT version();")
# 获取查询结果
record = cursor.fetchone()
print("You are connected to - ", record, "\n")
# 插入数据实例
cursor.execute("INSERT INTO sys_user (id, username, tenant_key, password) VALUES (1, 'Tom', 0, 'password');")
# 提交事务
conn.commit()
except Exception as e:
print(f"Error: {e}")
finally:
# 关闭游标
# if 'cursor' in locals():
if cursor:
cursor.close()
# 关闭连接
# if 'conn' in locals():
if conn:
conn.close()
print("Connection closed.")
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
# SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from user import User
# 数据库连接信息
db_url = 'postgresql+psycopg2://postgres:zqlf%402023!@192.168.1.79:5432/ds_0'
# 创建数据库引擎
engine = create_engine(db_url)
# 创建会话
Session = sessionmaker(bind=engine)
session = Session()
# 插入新记录
new_user = User(id=2, username='Jerry', tenant_key=0, password='password')
session.add(new_user)
session.commit()
print(f"New user added: {new_user}")
# 查询所有记录
users = session.query(User).all()
print("All users:")
for user in users:
print(user)
# 根据条件查询
filter_users = session.query(User).filter_by(username='Tom').all()
print("Filtered users:")
for user in filter_users:
print(user)
# 更新记录
user = session.query(User).filter_by(username='Tom').first()
user.password = 'new_password'
session.commit()
print("User updated: ", user)
# 关闭会话
session.close()
print("Session closed.")
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

# Python3 网络编程
Python提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的Socket,它提供了标准的BSD Sockets API,可以访问底层操作系统Socket接口的全部方法
- 高级别的网络服务模块SocketServer,它提供了服务器中心类,可以简化网络服务器的开发。
# 什么是Socket
Socket又称套接字,应用程序通常通过套接字向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
# socket() 函数
Python中,用socket()来创建套接字,语法如:
socket.socket([family[, type[, proto]]])
参数:
family: 套接字家族可以是AF_UNIX或者AF_INETtype: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAMproto: 一般不填默认为0
Socket对象(内建)方法:
| 服务器端函数 | 描述 |
|---|---|
s.bind() | 绑定地址(host, port)到套接字,在AF_INET下,以元组(host, port)的形式表示地址。 |
s.listen() | 开始TCP监听,backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量,该值至少为1,大部分应用程序设为5就可以了 |
s.accept() | 被动接收TCP客户端连接,(阻塞式)等待连接的到来 |
| 客户端函数 | 描述 |
|---|---|
s.connect() | 主动初始化TCP服务器连接,一般address的格式为元组(hostname, port),如果连接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常。 |
| 公共用途函数 | 描述 |
|---|---|
s.recv() | 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关信息的其他信息,通常可以忽略。 |
s.send() | 发送TCP数据,将string中的数据发送到连接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小 |
s.sendall() | 完整发送TCP数据,将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据,成功返回None,失败抛出异常。 |
s.recvfrom() | 接收UDP数据,与recv()类似,但返回值是(data, address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr, port)的元组,指定远程地址。返回值是发送的字节数。 |
s.close() | 关闭套接字 |
s.getpeername() | 返回连接套接字的远程地址,返回值通常是元组(ipaddr, port)。 |
s.getsockname() | 返回套接字自己的地址,通常是一个元组(ipaddr, port) |
s.setsockopt(level, optname, value) | 设置给定套接字选项的值 |
s.getsockopt(level, optname[, buflen]) | 返回套接字选项的值 |
s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期,一般,超时期应该在刚创建套接字时设置,因为它可能用于连接的操作(如connect()) |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,返回None |
s.fileno() | 返回套接字的文件描述符 |
s.setblocking(flag) | 如果flag为False,则将套接字设置为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常 |
s.makefile() | 创建一个与该套接字相关连的文件 |
# 简单实例
# 服务端
使用socket模块的socket函数创建一个socket对象,socket对象可以通过调用其他函数来设置一个socket服务。现在可以通过调用bind(hostname, port)函数来指定服务的port端口。接着,调用socket对象的accept方法,该方法等待客户端的连接,并返回connection对象,表示已连接到客户端。
# 导入socket, sys模块
import socket
import sys
# 创建socket对象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
print('client host:', host)
# 设置端口
port = 9999
# 绑定端口
serversocket.bind((host, port))
# 设置最大连接数,超过后排队
serversocket.listen(5)
while True:
# 建立客户端连接
clientsocket, addr = serversocket.accept()
print("连接地址: %s" % str(addr))
msg = '欢迎访问菜鸟教程!' + "\r\n"
clientsocket.send(msg.encode('utf-8'))
clientsocket.close()
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
# 客户端
socket.connect(hostname, port)方法打开一个TCP连接到主机为hostname端口为port的套接字,连接后即可从服务端获取数据,注意操作后需要关闭连接。

# Python Internet模块
以下列出了Python网络编程的一些重要模块:
| 协议 | 功能用途 | 端口号 | Python模块 |
|---|---|---|---|
HTTP | 网页访问 | 80 | httplib, urllib, xmlrpclib |
NNTP | 阅读和张贴新闻文章 | 119 | nntplib |
FTP | 文件传输 | 21 | ftplib, urllib |
SMTP | 发送邮件 | 25 | smtplib |
POP3 | 接收邮件 | 110 | poplib |
IMAP4 | 获取邮件 | 143 | imaplib |
Telnet | 命令行 | 23 | telnetlib |
Gopher | 信息查找 | 70 | gopherlib, urllib |
# Python3 SMTP发送邮件
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。Python的smtplib提供了一种很方便的途径发送电子邮件,它对smtp协议进行了简单的封装。其语法如下:
import smtplib
smtpObj = smtplib.SMTP([host, [, port[, local_hostname]]])
2
3
参数说明:
host: SMTP服务器主机,可以指定主机的ip地址或者域名如:runoob.com,这个是可选参数。port: 如果提供了host参数,需要指定SMTP服务使用的端口号,一般情况其为25.local_hostname: 如果SMTP在本机,只需指定服务器地址为localhost即可。
Python SMTP对象使用sendmail方法发送邮件,语法如:
SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])
参数说明:
from_addr: 邮件发送者地址to_addrs: 字符串列表,邮件发送地址msg: 发送消息
需要注意第三个参数,msg是字符串,表示邮件。邮件一般由标题、发信人、收件人、邮件内容、附件等构成,发送邮件的时候,要注意msg的格式,这个格式就是smtp协议中定义的格式。
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.header import Header
sender = 'from@runoob.com'
receivers = ['429240967@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码
message = MIMEText('Python 邮件发送测试...', 'plain', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8') # 发送者
message['To'] = Header("测试", 'utf-8') # 接收者
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
标准邮件需要三个头部信息: From、To和Subject,每个信息直接使用空行分隔。通过实例化smtplib模块的SMTP对象smtpObj来连接到SMTP访问,并使用sendmail方法发送信息。
如果本机没有sendmail访问,也可以使用其他服务商的SMTP访问。
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 第三方 SMTP 服务
mail_host="smtp.XXX.com" #设置服务器
mail_user="XXXX" #用户名
mail_pass="XXXXXX" #口令
sender = 'from@runoob.com'
receivers = ['429240967@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
message = MIMEText('Python 邮件发送测试...', 'plain', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25) # 25 为 SMTP 端口号
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")
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
# 使用Python发送HTML格式的邮件
Python发送HTML格式的邮件与发送纯文本消息的邮件不同之处就是将MIMEText中_subtype设置为html,如:
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.header import Header
sender = 'from@runoob.com'
receivers = ['429240967@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
mail_msg = """
<p>Python 邮件发送测试...</p>
<p><a href="http://www.runoob.com">这是一个链接</a></p>
"""
message = MIMEText(mail_msg, 'html', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")
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
# Python发送带附件的邮件
发送带附件的邮件,首先要创建MIMEMultipart()实例,然后构造附件,如果有多个附件,可一次构造,最后利用smtplib.smtp发送。
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
sender = 'from@runoob.com'
receivers = ['429240967@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
#创建一个带附件的实例
message = MIMEMultipart()
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
#邮件正文内容
message.attach(MIMEText('这是菜鸟教程Python 邮件发送测试……', 'plain', 'utf-8'))
# 构造附件1,传送当前目录下的 test.txt 文件
att1 = MIMEText(open('test.txt', 'rb').read(), 'base64', 'utf-8')
att1["Content-Type"] = 'application/octet-stream'
# 这里的filename可以任意写,写什么名字,邮件中显示什么名字
att1["Content-Disposition"] = 'attachment; filename="test.txt"'
message.attach(att1)
# 构造附件2,传送当前目录下的 runoob.txt 文件
att2 = MIMEText(open('runoob.txt', 'rb').read(), 'base64', 'utf-8')
att2["Content-Type"] = 'application/octet-stream'
att2["Content-Disposition"] = 'attachment; filename="runoob.txt"'
message.attach(att2)
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")
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
# 在HTML文本中添加图片
邮件的HTML文本中一般邮件服务商添加外链是无效的,正确添加图片的实例如下:
#!/usr/bin/python3
import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
sender = 'from@runoob.com'
receivers = ['429240967@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
msgRoot = MIMEMultipart('related')
msgRoot['From'] = Header("菜鸟教程", 'utf-8')
msgRoot['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
msgRoot['Subject'] = Header(subject, 'utf-8')
msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
mail_msg = """
<p>Python 邮件发送测试...</p>
<p><a href="http://www.runoob.com">菜鸟教程链接</a></p>
<p>图片演示:</p>
<p><img src="cid:image1"></p>
"""
msgAlternative.attach(MIMEText(mail_msg, 'html', 'utf-8'))
# 指定图片为当前目录
fp = open('test.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
# 定义图片 ID,在 HTML 文本中引用
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, msgRoot.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")
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
# 使用第三方SMTP服务发送
QQ邮箱通过生成授权码来设置密码:

QQ邮箱SMTP服务器地址: smtp.qq.com,SSL端口: 465,如下实例,需要修改: 发件人邮箱,密码,收件人邮箱:
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
my_sender = '**@qq.com' # 发件人邮箱账号
my_pass = '**' # 发件人邮箱密码
my_user = '****@sina.com' # 收件人邮箱账号
def mail():
ret = True
try:
msg = MIMEText('填写邮件内容', 'plain', 'utf-8')
msg['From'] = formataddr(["abc", my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['To'] = formataddr(["def", my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['Subject'] = "SMTP发送邮件测试" # 邮件的主题,也可以说是标题
server = smtplib.SMTP_SSL("smtp.qq.com", 465) # 发件人邮箱中的SMTP服务器,端口是25
server.login(my_sender, my_pass)
server.sendmail(my_sender, my_user, msg.as_string())
server.quit()
except Exception: # 如果 try 中的语句没有执行,则会执行下面的 ret=False
ret = False
return ret
ret = mail()
if ret:
print("邮件发送成功")
else:
print("邮件发送失败")
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
# Python3 多线程
多线程类似于同时执行多个不同的程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台取处理
- 用户界面可以更加吸引人,比如用户点击了一个按钮取触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程在这种情况下可以释放一些珍贵的资源必须内存等。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立运行,必须依存在应用程序中,由应用程序提供多个线程执行控制。每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反应了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
- 线程可以被抢占(中断)
- 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠)
线程可以分:
- 内核线程: 由操作系统内核创建和销毁
- 用户线程: 不需要内核只支持而在用户程序中实现的线程。
Python3线程中常用的两个模块为:
_threadthreading(推荐使用)
thread模块已被废弃,用户可以使用threading模块代替,在Python3中不能再使用thread模块,为了兼容,其将thread重名为_thread
# Python 线程
Python中使用线程有两种方式: 函数或者用类来包装线程对象。函数式: 调用_thread模块中的start_new_thread()函数来产生新线程,语法:
_thread.start_new_thread(function, args[, kwargs])
参数说明:
function: 线程函数args: 传递给线程函数的参数,必须是tuple类型kwargs: 可选参数
import _thread
import time
# 为线程定义一个函数
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
# 创建两个线程
try:
_thread.start_new_thread(print_time, ("Thread-1", 2,))
_thread.start_new_thread(print_time, ("Thread-2", 4,))
except:
print("Error: 无法启动线程")
while 1:
pass
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 线程模块
Python3通过两个标准库_thread和threading提供对线程支持。_thread提供了低级别的、原始的线程以及一个简单的锁,它相比于threading模块的功能还是比较有限的。threading模块除了包含_thread模块中的所有方法外,还提供的其他方法:
threading.current_thread(): 返回当前的线程变量threading.enumerate(): 返回一个包含正在运行的线程的列表,正在运行指线程启动后、结束前,不包括启动前和终止后的线程。threading.active_count(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。threading.Thread(target, args=(), kwargs={}, daemon=None):- 创建Thread类的实例
target: 线程将要执行的目标函数args: 目标函数的参数,以元组形式传递kwargs: 目标函数的关键字参数,以字典形式传递daemon: 指定线程是否为守护线程。
threading.Thread类提供了以下方法与属性:
__init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):- 初始化Thread对象
group: 线程组,暂时未使用,保留为将来的扩展target: 线程将要执行的目标函数name: 线程的名称args: 目标函数的参数,以元组形式传递kwargs: 目标函数的关键字参数,以字典形式传递daemon: 指定线程是否为守护线程
start(self): 启动线程,将调用线程的run()方法run(self): 线程在此方法中定义要执行的代码join(self, timeout=None): 等待线程终止,默认情况下,join()会一直阻塞,直到被调用线程终止,如果指定了timeout参数,则最多等待timeout秒is_alive(self): 返回线程是否正在运行,如果线程已经启动且尚未终止,则返回True,否则返回False。getName(self): 返回线程的名称setName(self): 设置线程的名称ident属性: 线程的唯一标识符daemon属性: 线程的守护标志,用于指示是否是守护线程isDaemon():
import threading
import time
def print_numbers():
for i in range(5):
time.sleep(1)
print(i)
# 创建线程
thread = threading.Thread(target=print_numbers)
# 启动线程
thread.start()
# 等待线程结束
thread.join()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用 threading 模块创建线程
可以通过直接从threading.Thread继承创建一个新的子类,并实例化后调用start()方法启动新线程,即它调用了线程的run()方法:
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, delay):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.delay = delay
def run(self):
print("开始线程:" + self.name)
print_time(self.name, self.delay, 5)
print("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("退出主线程")
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

# 线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间,如:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样),但当线程需要共享数据时,可能存在数据不同步的问题。考虑这种情况: 一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态--锁定和未锁定,没当一个线程比如"set"要访问共享数据时,必须先获得锁定,如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞; 等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
import threading
import time
class MyThread(threading.Thread):
def __init__(self, threadID, name, delay):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.delay = delay
def run(self):
print("开始线程:" + self.name)
# 获取锁,用于线程同步
threadLock.acquire()
print_time(self.name, self.delay, 3)
# 释放锁,开启下一个线程
threadLock.release()
print("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
# 创建新线程
thread1 = MyThread(1, "Thread-1", 1)
thread2 = MyThread(2, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)
# 等待所有线程完成
for t in threads:
t.join()
print("退出主线程")
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

# 线程优先级队列(Queue)
Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
Queue模块中的常用方法:
- Queue.qsize()返回队列的大小
- Queue.empty()如果队列为空,返回True,反之False
- Queue.full()如果队列满了,返回True,反之False
- Queue.full与maxsize大小对应
- Queue.get([block[, timeout]])获取队列,timeout等待时间
- Queue.get_nowait()相当于Queue.get(False)
- Queue.put(item)写入队列,timeout等待时间
- Queue.put_nowait(item)相当Queue.put(item, False)
- Queue.task_done()在完成一项工作后,Queue.task_done()函数向任务已经完成的队列发送一个信号
- Queue.join()实际上意味着等到队列为空,再执行别的操作
import queue
import threading
import time
exitFlag = 0
class MyThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("开始线程:" + self.name)
process_data(self.name, self.q)
print("退出线程:" + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1
# 创建新线程
for tName in threadList:
thread = MyThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# 填充队列
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# 等待队列清空
while not workQueue.empty():
pass
# 通知线程是时候退出
exitFlag = 1
# 等待所有线程完成
for t in threads:
t.join()
print("退出主线程")
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

# Python3 XML解析
# 什么是XML?
XML指可扩展标记语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言,XML被设计用来传输和存储数据。XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。它也是元标记语言,即定义了用于定义其他与特定领域有关的、语义的、结构化的标记语言的句法语言。
# Python对XML的解析
常见的XML编程接口有DOM和SAX,这两种接口处理XML文件的方式不同,适用场合也不同。
Python有三种方法解析XML: ElementTree,SAX以及DOM。
- ElementTree
xml.etree.ElementTree是Python标准库中用于处理XML的模块,它提供了简单而高效的API,用于解析和生成XML文档。
- SAX(simple API for XML)
Python标准库包含SAX解析器,SAX用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
- DOM(Document Object Model)
将XML数据在内存中解析成一个树,通过对树的操作来解析XML。
<collection shelf="New Arrivals">
<movie title="Enemy Behind">
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</movie>
<movie title="Transformers">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>R</rating>
<stars>8</stars>
<description>A schientific fiction</description>
</movie>
<movie title="Trigun">
<type>Anime, Action</type>
<format>DVD</format>
<episodes>4</episodes>
<rating>PG</rating>
<stars>10</stars>
<description>Vash the Stampede!</description>
</movie>
<movie title="Ishtar">
<type>Comedy</type>
<format>VHS</format>
<rating>PG</rating>
<stars>2</stars>
<description>Viewable boredom</description>
</movie>
</collection>
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
# Python使用ElementTree解析xml
xml.etree.ElementTree是Python标准库中用于处理XML的模块,以下是xml.etree.ElementTree模块的一些关键概念和用法:
ElementTree和Element对象:
ElementTree: ElementTree类是XML文档的树形表示。它包含一个或多个Element对象,代表整个XML文档。Element: Element对象是XML文档中元素的表示,每个元素都有一个标签、一组属性和零个或多个子元素。
# 解析XML
fromstring(): 该方法可以将包含XML数据的字符串转换为Element对象
import xml.etree.ElementTree as ET
xml_string = "<root><element>Some data</element></root>"
root = ET.fromstring(xml_string)
2
3
4
parse(): 如果XML数据存储在文件中,可以使用parse()方法来解析整个XML文档
tree = ET.parse('example.xml')
root = tree.getroot()
2
# 遍历XML树
find(): 该方法可以查找具有指定标签的第一个子元素
title_element = root.find('title')
findall(): 该方法可以查找具有指定标签的所有子元素
book_elements = root.findall('book')
# 访问元素的属性和文本内容
attrib: 通过attrib属性可以访问元素的属性
price = book_element.attrib['price']
text: 通过text属性可以访问元素的文本内容
title_text = title_element.text
# 创建 XML
Element()构造函数: 使用Element()构造函数可以创建新元素
new_element = ET.Element('new_element')
SubElement()函数: 使用SubElement()函数可以创建具有指定标签的子元素
new_sub_element = ET.SubElement(root, 'new_sub_element')
# 修改 XML
修改元素的属性和文本内容: 直接修改元素的 attrib 和 text 属性。删除元素: 使用 remove() 可以删除元素。root.remove(title_element)
import xml.etree.ElementTree as ET
# 定义一个 XML 字符串
xml_string = '''
<bookstore>
<book>
<title>Introduction to Python</title>
<author>John Doe</author>
<price>29.99</price>
</book>
<book>
<title>Data Science with Python</title>
<author>Jane Smith</author>
<price>39.95</price>
</book>
</bookstore>
'''
# 使用 ElementTree 解析 XML 字符串
root = ET.fromstring(xml_string)
# 遍历 XML 树
for book in root.findall('book'):
title = book.find('title').text
author = book.find('author').text
price = book.find('price').text
print(f'Title: {title}, Author: {author}, Price: {price}')
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
import xml.etree.ElementTree as ET
# 创建一个XML文档
root = ET.Element('bookstore')
# 添加第一本书
book1 = ET.SubElement(root, 'book')
title1 = ET.SubElement(book1, 'title')
title1.text = 'Introduction to Python'
author1 = ET.SubElement(book1, 'author')
author1.text = 'John Doe'
price1 = ET.SubElement(book1, 'price')
price1.text = '29.99'
# 添加第二本书
book2 = ET.SubElement(root, 'book')
title2 = ET.SubElement(book2, 'title')
title2.text = 'Data Science with Python'
author2 = ET.SubElement(book2, 'author')
author2.text = 'Jane Smith'
price2 = ET.SubElement(book2, 'price')
price2.text = '39.95'
# 将XML文档保存到文件
tree = ET.ElementTree(root)
tree.write('/home/sunyy/Sources/gitee/ptrain/xml/books.xml')
# 从文件中解析XML文档
parsed_tree = ET.parse('/home/sunyy/Sources/gitee/ptrain/xml/books.xml')
parsed_root = parsed_tree.getroot()
# 遍历XML树并打印书籍信息
for book in parsed_root.findall('book'):
title = book.find('title').text
author = book.find('author').text
price = book.find('price').text
print(f'Title: {title}, Author: {author}, Price: {price}')
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
# Python使用SAX解析XML
SAX是一种基于事件驱动的API,利用SAX解析XML文档牵涉两个部分: 解析器和事件处理器。解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件。而事件处理器则负责对事件做出响应,对传递的XML数据进行处理。
- 对大型文件进行处理
- 只需要文件的部分内容,或者只需从文件中得到特定信息
- 想建立自己的对象模型的时候
在Python中使用sax方式处理xml要先引入xml.sax中的parse函数,还有xml.sax.handler中的ContentHandler。
# ContentHandler 类方法介绍
characters(content)方法
从行开始,遇到标签之前,存在字符,content的值为这些字符串。从一个标签,遇到下一个标签之前,存在字符,content的值为这些字符串。从一个标签,遇到结束符之前,存在字符,content的值为这些字符串。标签可以是开始标签,也可以是结束标签。
startDocument()方法: 文档启动的时候调用endDocument()方法: 解析器到达文件结尾时调用。startElement(name, attrs)方法: 遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。endElement(name)方法: 遇到XML结束标签时调用。make_parser方法
以下方法创建一个新的解析器对象并返回。xml.sax.make_parser([parser_list])
参数说明:
parser_list: 可选参数,解析器列表
parser方法
以下方法创建一个SAX解析器并解析xml文档: xml.sax.parse(xmlfile, contenthandler[, errorhandler])
参数说明:
xmlfile: xml文件名contenthandler: 必须是一个ContentHandler的对象errorhandler: 如果指定该参数,errorHandler必须是一个SAX ErrorHandler对象
parseString方法: parseString方法创建一个XML解析器并解析xml字符串:xml.sax.parseString(xmlString, contenthandler[, errorhandler])
参数说明:
xmlstring: xml字符串contenthandler: 必须是一个ContentHandler对象errorhandler: 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
# Python解析XML实例
<?xml version="1.0" encoding="UTF-8"?>
<movies>
<movie title="Enemy Behind">
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</movie>
<movie title="Transformers">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>R</rating>
<stars>8</stars>
<description>A scientific fiction</description>
</movie>
<movie title="Trigun">
<type>Anime, Action</type>
<format>DVD</format>
<rating>PG</rating>
<stars>10</stars>
<description>Vash the Stampede!</description>
</movie>
<movie title="Ishtar">
<type>Comedy</type>
<format>VHS</format>
<rating>PG</rating>
<stars>2</stars>
<description>Viewable boredom</description>
</movie>
</movies>
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
import xml.sax
class MovieHandler(xml.sax.ContentHandler):
def __init__(self):
self.CurrentData = ""
self.type = ""
self.format = ""
self.year = ""
self.rating = ""
self.stars = ""
self.description = ""
# 元素开始调用
def startElement(self, tag, attrs):
self.CurrentData = tag
if tag == "movie":
print("******Movie******")
title = attrs["title"]
print("Title: ", title)
# 元素结束调用
def endElement(self, tag):
if self.CurrentData == "type":
print("Type: ", self.type)
elif self.CurrentData == "year":
print("Year: ", self.year)
elif self.CurrentData == "rating":
print("Rating: ", self.rating)
elif self.CurrentData == "stars":
print("Stars: ", self.stars)
elif self.CurrentData == "description":
print("Description: ", self.description)
self.CurrentData = ""
# 读取字符时调用
def characters(self, content):
if self.CurrentData == "type":
self.type = content
elif self.CurrentData == "format":
self.format = content
elif self.CurrentData == "year":
self.year = content
elif self.CurrentData == "rating":
self.rating = content
elif self.CurrentData == "stars":
self.stars = content
elif self.CurrentData == "description":
self.description = content
if ( __name__ == "__main__" ):
# 创建一个 XMLReader
parser = xml.sax.make_parser()
# 关闭命名空间
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# 重写 ContextHandler
Handler = MovieHandler()
parser.setContentHandler( Handler )
parser.parse("/home/sunyy/Sources/gitee/ptrain/xml/movies.xml")
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
# 使用xml.dom解析xml
文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。一个DOM的解析器在解析一个XML文档时,一次性读取整个文档,把文档中的所有元素保存在内存中的一个树结构里,之后可以利用DOM提供的不同的函数来读取和修改文档的内容和结构,也可以把修改过的内容写入XML文件。
from xml.dom.minidom import parse
# import xml.dom.minidom
# 使用minidom解析器打开XML文档
DOMTree = parse("/home/sunyy/Sources/gitee/ptrain/xml/movies.xml")
collection = DOMTree.documentElement
# 在集合中获取所有电影
movies = collection.getElementsByTagName("movie")
# 打印每部电影的详细信息
for movie in movies:
print("******Movie******")
if movie.hasAttribute("title"):
print("Title: %s " % movie.getAttribute("title"))
type = movie.getElementsByTagName('type')[0]
print("Type: %s " % type.childNodes[0].data)
format = movie.getElementsByTagName('format')[0]
print("Format: %s " % format.childNodes[0].data)
rating = movie.getElementsByTagName('rating')[0]
print("Rating: %s " % rating.childNodes[0].data)
description = movie.getElementsByTagName('description')[0]
print("Description: %s " % description.childNodes[0].data)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Python3 JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。Python3中可以使用json模块对JSON数据进行编码,其包含两个函数:
json.dumps(): 对数据进行编码json.loads(): 对数据进行解码

在json的编解码的过程中,Python的原始类型与JSON类型会相互转换:
Python编码为JSON类型转换对应表:
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int- & float-derivied Enums | number |
True | true |
False | false |
None | null |
JSON解码为Python类型转换对应表:
| JSON | Python |
|---|---|
object | dict |
array | list |
string | str |
number(int) | int |
number(real) | float |
True | True |
false | False |
null | None |
json.dumps与json.loads实例
import json
# Python 字典类型转换为 JSON 对象
data = {
'no': 1,
'name': 'Runoob',
'url': 'https://www.runoob.com'
}
json_str = json.dumps(data)
print("Python 原始数据: ", repr(data))
print("JSON 对象: ", json_str)
# 简单类型通过编码后跟其原始的repr()输出结果非常相似
2
3
4
5
6
7
8
9
10
11
12
13

import json
# Python 字典类型转换为 JSON 对象
data1 = {
'no': 1,
'name': 'Runoob',
'url': 'http://www.runoob.com'
}
json_str = json.dumps(data1)
print("Python 原始数据: ", repr(data1))
print("JSON 对象: ", json_str)
# 将 JSON 对象转换为 Python 字典
data2 = json.loads(json_str)
print("data2['name']: ", data2['name'])
print("data2['url']: ", data2['url'])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

如果要处理的是文件而不是字符串,可以使用json.dump()和json.load()来编码和解码JSON数据:
# 写入 JSON 数据
with open('data.json', 'w') as f:
json.dump(data, f)
# 读取数据
with open('data.json', 'r') as f:
data = json.load(f)
2
3
4
5
6
7
# Python3 日期和时间
Python有很多方式处理日期和时间,转换日期格式是一个常见的功能。Python提供了一个time和calendar模块可以用于格式化日期和时间。时间间隔是以秒为单位的浮点数。每个时间戳都以自从1970年1月1日午夜经过了多长时间来表示。Python的time模块有很多函数可以转换常见日期格式:
import time # 引入time模块
ticks = time.time()
print("当前时间戳为: ", ticks)
# 当前时间戳为: 1741251210.1287994
2
3
4
5
时间戳单位最适用与做日期运算,但1970年之前的日期无法表示,太遥远的日期也不行,UNIX和Windows只支持到2038年。
# 什么是时间元组
很多Python函数用一个元组组装起来的9组数字处理时间:
| 序号 | 字段 | 值 |
|---|---|---|
| 0 | 4位数年 | 2008 |
| 1 | 月 | 1到12 |
| 2 | 日 | 1到31 |
| 3 | 小时 | 0到23 |
| 4 | 分钟 | 0到59 |
| 5 | 秒 | 0到61(60或61是闰秒) |
| 6 | 一周的第几日 | 0到6(0是周一) |
| 7 | 一年的第几日 | 1到366 |
| 8 | 夏令时 | -1, 0, 1, -1决定是否为夏令时的标识 |
上述也就是struct_time元组,这种结构具有如下属性:
| 序号 | 属性 | 值 |
|---|---|---|
| 0 | tm_year | 2008 |
| 1 | tm_mon | 1到12 |
| 2 | tm_mday | 1到31 |
| 3 | tm_hour | 0到23 |
| 4 | tm_min | 0到59 |
| 5 | tm_sec | 0到61(60或61是闰秒) |
| 6 | tm_wday | 0到6(0是周一) |
| 7 | tm_yday | 1到366 |
| 8 | tm_isdst | 是否为夏令时: 1-是, 0-不是, -1-位置,默认为-1 |
# 获取当前时间
从返回浮点数的时间戳向时间元组转换,只需将浮点数传递给如localtime之类的函数:
import time
localtime = time.localtime(time.time())
print("本地时间为: ", localtime)
# 本地时间为: time.struct_time(tm_year=2025, tm_mon=3, tm_mday=6, tm_hour=17, tm_min=2, tm_sec=4, tm_wday=3, tm_yday=65, tm_isdst=0)
2
3
4
5
# 获取格式化时间
可以根据需求选取各种格式,但最简单的获取可读的时间模式的函数是asctime():
import time
localtime = time.asctime(time.localtime(time.time()))
print("本地时间为: ", localtime)
# 本地时间为: Thu Mar 6 17:03:50 2025
2
3
4
5
# 格式化日期
可以使用time模块的strftime格式化日期: time.strftime(format[, t])
import time
# 格式化为 yyyy-MM-dd HH:mm:ss
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 格式化为 Sat Mar 28 22:24:24 2016
print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
# 将格式化字符串转为时间戳
a = "Sat Mar 28 22:24:24 2016"
print(time.mktime(time.strptime(a, "%a %b %d %H:%M:%S %Y")))
2
3
4
5
6
7
8
9
10
11
Python中时间日期格式化符号:
%y: 两位数的年份表示(00-99)%Y: 四位数的年份表示(000-999)%m: 月份(01-12)%d: 月内中的一天(0-31)%H: 24小时制小时数(0-23)%I: 12小时制小时数(0-12)%M: 分钟数(0-59)%S: 秒(0-59)%a: 本地简化星期名称%A: 本地完整星期名称%b: 本地简化的月份名称%B: 本地完整的月份名称%c: 本地响应的日期表示和时间表示%j: 年内的一天(001-366)%p: 本地A.M.或P.M.的等价符%U: 一年中的星期数(00-53)星期天为星期的开始%w: 星期(0-6),星期天为星期的开始%W: 一年中的星期数(00-53)星期一为星期的开始%x: 本地相应的日期表示%X: 本地相应的时间表示%Z: 当前时区的名称%%: %号本身
# 获取某月日历
Calendar模块有很广泛的方法来处理年历和月历,如:
import calendar
cal = calendar.month(2025, 3)
print("以下为2025年3月的日历: ")
print(cal)
2
3
4
5

# Time模块
Timce模块包含了以下内置函数:
| 函数 | 描述 | 实例 |
|---|---|---|
time.altzone | 返回格林威治西部的夏令时地区的偏移秒数。如果该地区在格林威治东部会返回负值。对夏令时启用地区才能使用。 | import time print("time.altone %d " % time.altzone)time.altzone -28800 |
time.asctime([tupletime]) | 接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"的24个字符的字符串 | import timet = time.localtime()print("time.asctime(t): %s " % time.asctime(t))time.asctime(t): Thu Apr 10:36:20 2016 |
time.clock() | 用以浮点数计算的秒数返回当前的CPU时间,用来衡量不同程序的耗时,比time.time()更有用。 | 由于该方法依赖操作系统,在Python3.3后不被推荐,在3.8版本被移除,由下列两个函数替代: time.perf_counter() # 返回系统运行时间time.process_time() # 返回进程运行时间 |
time.ctime([secs]) | 作用相当于asctime(localtime(secs)), 未给参数相当于asctime() | import timeprint("time.ctime(): %s " % time.ctime())time.ctime(): Thu Apr 7 10:51:58 2016 |
time.gmtime([secs]) | 接收时间戳(1970纪元后经过的浮点秒数)并返回格林威治天文时间下的时间元组t。注: t.tm_isdst始终为0 | import timeprint("gmtime: ", time.gmtime(1455508609.34375))gmtime: time.struct_time(tm_year=2016, tm_mon=2, tm_mday=15, tm_hour=3, tm_min=56, tm_sec=49, tm_wday=0, tm_yday=46, tm_isdst=0) |
time.localtime([secs]) | 接收时间戳(1970纪元后经过的浮点秒数)并返回当地时间下的时间元组t(t.tm_isdst可以取0或1,取决于当地当时是不是夏令时) | import timeprint("localtime(): ", time.localtime(1455508609.34375))localtime(): time.struct_time(tm_year=2016, tm_mon=2, tm_mday=15, tm_hour=11, tm_min=56, tm_sec=49, tm_wday=0, tm_yday=46, tm_isdst=0) |
time.mktime(tupletime) | 接收时间元组并返回时间戳(1970纪元后经过的浮点秒数) | |
time.sleep(secs) | 推迟调用线程的运行,secs指秒数 | import timeprint("Start: %s" % time.ctime())time.sleep(5)print("End: %s" % time.ctime()) |
time.strftime(fmt[, tupletime]) | 接收时间元组,并返回以可读字符串表示的当地时间,格式由fmt决定 | import timeprint(time.strftime(%Y-%m-%d %H:%M:%S, time.localtime()))2016-04-07 11:18:05 |
time.strptime(str, fmt='%a %b %d %H:%M:%S %Y') | 根据fmt格式把一个字符串解析为时间元组 | import timestruct_time = time.strptime("30 Nov 00", "%d %b %y")print("返回元组: ", struct_time)返回元组: time.struct_time(tm_year=2000, tm_mon=11, tm_mday=30, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=335, tm_isdst=-1) |
time.time() | 返回当前时间的时间戳(1970纪元后经过的浮点秒数) | import timeprint(time.time())149999336.1963577 |
time.tzset() | 根据环境变量TZ重新初始化时间相关设置 | |
time.perf_counter() | 返回计时器的精准时间(系统的运行时间),包含整个系统的睡眠时间,由于返回值的基准点是未定义的,所以,只有连续调用的结果之间的差才是有效的 | |
time.process_time() | 返回当前进程执行CPU的时间总和,不包含睡眠时间。有与返回值的基准点是未定义的,所以只有连续调用的结果之间的差才是有效的。 |
| 属性 | 描述 | 实例 |
|---|---|---|
time.timezone | 属性time.timezone是当地时区(未启动夏令时)距离格林威治的偏移秒数(>0, 美洲; <=0大部分欧洲,亚洲,非洲) | |
time.tzname | 属性time.tzname包含一对根据情况不同而不同的字符串,分别是带夏令时的本地时区名称,和不带的。 |
# 日历模块(Calendar)
此模块的函数都是日历相关的,如打印某月的字符月历。星期一是默认的每周第一天,星期天是默认的最后一天。更改设置需要调用calendar.setfirstweekday()函数,模块包含:
| 函数 | 描述 |
|---|---|
calendar.calendar(year, w=2, l=1, c=6) | 返回一个多行字符串格式的year年年历,3个月一行,间隔距离为c。每日宽度间隔为w字符。每行长度为21*W+18+2*c,l是每星期行数 |
calendar.firstweekday() | 返回当前每周起始日期的设置,默认情况,首次载入calendar模块时返回0,即星期一 |
calendar.isleap(year) | 闰年返回True,否则返回False |
calendar.leapdays(y1, y2) | 返回在y1, y2两年之间的闰年总数 |
calendar.month(year, month, w=2, l=1) | 返回一个多行字符串格式的year年month月日历,两行标题,一周一行,每日宽度间隔为w字符,每行的长度为7*w+6,l是每星期行数 |
calendar.monthcalendar(year, month) | 返回一个整数的单层嵌套列表,每个子列表装载代表一个星期的整数,Year年month月外的日期都设为0;范围内的日都由该月第几日表示,从1开始 |
calendar.monthrange(year, month) | 返回两个整数,第一个是该月第一天星期几,第二个是该月有几天。星期几是从0(星期一)到6(星期日)import calendarcalendar.monthrange(2024, 11)(5, 30)(5, 30)解释: 5表示2014年11月份的第一天是周六,30表示2014年11月共有30天 |
calendar.prcal(year, w=0, l=0, c=6, m=3) | 相当于print(calendar.calendar(year, w=0, l=0, c=6, m=3)) |
calendar.prmonth(theyear, themonth, w=0, l=0) | 相当于print(calendar.month(theyear, themonth, w=0, l=0)) |
calendar.setfirstweekday(weekday) | 设置每周的起始日期, 0(星期一)到6(星期日) |
calendar.timegm(tupletime) | 和time.gmtime相反,接收一个时间元组,返回该时刻的时间戳(1970纪元后的浮点秒数) |
calendar.weekday(year, month, day) | 返回给定日期的日期码,0(星期一)到6(星期日),月份为1(一月)到12(十二月) |
# 其他相关模块和函数
# Python3 内置函数
# abs()
# dict()
# help()
# min()
# setattr()
# all()
# dir()
# hex()
# next()
# slice()
# any()
# divmod()
# id()
# object()
# sorted()
# ascii()
# enumerate()
# input()
# oct()
# staticmethod()
# bin()
# eval()
# int()
# open()
# str()
# bool()
# exec()
# isinstance()
# ord()
# sum()
# bytearray()
# filter()
# issubclass()
# pow()
# super()
# bytes()
# float()
# iter()
# print()
# tuple()
# callable()
# format()
# len()
# property()
# type()
# chr()
# frozenset()
# list()
# range()
# vars()
# classmethod()
# getattr()
# locals()
# repr()
# zip()
# compile()
# globals()
# map()
# reversed()
# __import__()
# complex()
# hasattr()
# max()
# round()
# reload()
# delattr()
# hash()
# memoryview()
# set()
# Python3 MongoDB
MongoDB是目前最流行的NoSQL数据库之一,使用的数据类型BSON(类似JSON)。MongoDB数据库安装与介绍可以查看MongoDB教程以及Mongodb。
# PyMongo
Python要连接MongoDB需要MongoDB驱动,这里使用PyMongo驱动连接。
- pip安装: pip是一个通用的Python包管理工具,提供了对Python包的查找、下载、安装、卸载的功能
$> python3 -m pip3 install pymongo
也可以指定安装版本
$> python3 -m pip3 install pymongo==3.5.1
更新pymongo命令:
$> python3 -m pip3 install --upgrade pymongo
- 测试PyMongo
import pymongo
# 执行以上代码文件,如果没有出现错误,表示安装成功
2
# 创建数据库
创建数据库需要使用MongoClient对象,并且指定连接的URL地址和要创建的数据库名。如:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
# 注意: 在MongoDB中,数据库只有在内容插入后才会创建,数据库创建后要创建集合(数据表)并插入一个文档(就),数据库才会真正创建
2
3
4
5
6
- 判断数据库是否已存在,可以读取MongoDB中的所有数据库,并判断指定的数据库是否存在:
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
dblist = myclient.list_database_names()
# 注意: database_names在最新版本的Python中已废弃,Python3.7+之后的版本修改为了list_database_names()
if "runoobdb" in dblist:
print("数据库已存在")
2
3
4
5
6
7
8
# 创建集合
MongoDB中的集合类似SQL的表
- 创建一个集合
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient["runoobdb"]
mytable = mydb["sites"]
# 注意,在MongoDB中,集合只有在内容插入后才会创建,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建
2
3
4
5
6
7
8
- 判断集合是否已存在
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
collist = mydb.list_collection_names()
# collection_names在最新版本的Python中已废弃,Python3.7+之后的版本改为了list_collection_names()
print(collist)
if "sites" in collist:
print("集合已存在")
2
3
4
5
6
7
8
9
10
11
# 增、删、改、查等操作
# Python Mongodb插入文档
MongoDB中的一个文档雷士SQL表中的一条记录。集合中插入文档使用insert_one()方法,该方法的第一个参数是字典name => value对,如:
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
mydict = {"name": "RUNOOB", "alexa": "10000", "url": "https://www.runoob.com"}
x = mycol.insert_one(mydict)
print(x)
2
3
4
5
6
7
8
9
10
返回_id字段: insert_one()方法返回InsertOneResult对象,该对象包含inserted_id属性,它是插入文档的id值。
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
mydict = { "name": "Google", "alexa": "1", "url": "https://www.google.com" }
x = mycol.insert_one(mydict)
print(x.inserted_id)
# 输出: 67ceb59660ae7932ba340c20
2
3
4
5
6
7
8
9
10
11
12
13
如果在插入文档时没有指定_id,MongoDB会为每个文档添加一个唯一的id。
插入多个文档: 集合中插入多个文档使用insert_many()方法,该方法的第一个参数是字典列表。
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
mylist = [
{ "name": "Taobao", "alexa": "100", "url": "https://www.taobao.com" },
{ "name": "QQ", "alexa": "101", "url": "https://www.qq.com" },
{ "name": "Facebook", "alexa": "10", "url": "https://www.facebook.com" },
{ "name": "知乎", "alexa": "103", "url": "https://www.zhihu.com" },
{ "name": "Github", "alexa": "109", "url": "https://www.github.com" }
]
x = mycol.insert_many(mylist)
# 输出插入的所有文档对应的 _id 值
print(x.inserted_ids)
# 输出
# [ObjectId('67ceb78306087fde8c721086'), ObjectId('67ceb78306087fde8c721087'), ObjectId('67ceb78306087fde8c721088'),
# ObjectId('67ceb78306087fde8c721089'), ObjectId('67ceb78306087fde8c72108a')]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
insert_many()方法返回InsertManyResult对象,该对象包含inserted_ids属性,该属性保存所有插入文档的id值,执行完上述示例,可以在命令终端查看数据是否已插入:
mongosh
use runoobdb
2

插入指定_id的多个文档: 可以自己指定id插入,如下_id为指定值:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["site2"]
mylist = [
{ "_id": 1, "name": "RUNOOB", "cn_name": "菜鸟教程"},
{ "_id": 2, "name": "Google", "address": "Google 搜索"},
{ "_id": 3, "name": "Facebook", "address": "脸书"},
{ "_id": 4, "name": "Taobao", "address": "淘宝"},
{ "_id": 5, "name": "Zhihu", "address": "知乎"}
]
x = mycol.insert_many(mylist)
# 输出插入的所有文档对应的 _id 值
print(x.inserted_ids)
# 输出
# [1, 2, 3, 4, 5]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Python Mongodb查询文档
MongoDB中使用了find和find_one方法来查询集合中的数据,类似SQL中的SELECT语句。
查询一条数据: find_one()方法查询集合中的一条数据
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017')
mydb = myclient['runoobdb']
mycol = mydb['sites']
x = mycol.find_one()
print(x)
2
3
4
5
6
7
8
9

查询集合中所有数据: find()方法可以查询集合中的所有数据,类似SQL中的select *
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
for x in mycol.find():
print(x)
2
3
4
5
6
7
8

查询指定字段的数据: find()查询指定字段的数据,将要返回的字段对应值设置为1。
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
for x in mycol.find({}, { "_id": 0, "name": 1, "alexa": 1 }):
print(x)
2
3
4
5
6
7
8

除了_id,不能在一个对象中同时指定0和1,如果设置了一个字段为0,则其他都为1,反之亦然。
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
for x in mycol.find({}, {"alexa": 0}):
print(x)
2
3
4
5
6
7
8

以下代码同时指定了0和1则会报错:
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
for x in mycol.find({}, {"name": 1, "alexa": 0}):
print(x)
2
3
4
5
6
7
8

根据指定条件查询: find()中设置参数来过滤数据
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
myquery = {"name": "RUNOOB"}
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
2
3
4
5
6
7
8
9
10
11
12

高级查询: 查询条件中可以使用修饰符,如读取name字段中第一个字母ASCII值大于H的数据,大于的修饰符条件为{"$gt": "H"}:
import pymongo
myclient = pymongo.MongoClient('mongodb://192.168.1.79:27017/')
mydb = myclient['runoobdb']
mycol = mydb['sites']
myquery = {"name": {"$gt": "H"}}
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
2
3
4
5
6
7
8
9
10
11
12

使用正则表达式查询: 可以使用正则表达式作为修饰符,正则表达式修饰符只用于搜索字符串的字段,如读取name字段中第一个字母为R的数据,正则表达式修饰符条件为{"$regex": "^R"}:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myquery = {"name": {"$regex": "^R"}}
mydoc = mycol.find(myquery)
for x in mydoc:
print(x)
2
3
4
5
6
7
8
9
10
11
12

返回指定条数记录: 对查询结果设置指定条数的记录可以使用limit()方法,该方法只接收一个数字参数:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myresult = mycol.find().limit(3)
for x in myresult:
print(x)
2
3
4
5
6
7
8
9
10
# Python Mongodb修改文档
使用update_one()方法修改文档中的记录,盖房第一个参数为查询的条件,第二个参数为要修改的字段。如果查找到的匹配数据多于一条,则只会修改第一条。
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myquery = {"alexa": "10000"}
newvalues = {"$set": {"alexa": "12345"}}
mycol.update_one(myquery, newvalues)
for x in mycol.find():
print(x)
2
3
4
5
6
7
8
9
10
11
12
13
update_one()方法只能修改匹配到的第一条记录,如果要修改所有匹配的记录,可以使用update_many()。以下实例将查找所有以F开头的name字段,并将匹配到所有记录的alexa字段修改为123:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myquery = {"name": {"$regex": "^F"}}
newvalues = {"$set": {"alexa": "123"}}
x = mycol.update_many(myquery, newvalues)
print(x.modified_count, "文档已修改")
2
3
4
5
6
7
8
9
10
11
12
# Python Mongodb删除数据
delete_one()方法删除一个文档,该方法第一个参数为查询对象,指定要删除哪些数据。
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myquery = {"name": "Taobao"}
mycol.delete_one(myquery)
for x in mycol.find():
print(x)
2
3
4
5
6
7
8
9
10
11
12
可以使用delete_many()方法删除多个文档,第一个参数为查询对象,指定要删除哪些数据,删除所有name字段中以F开头的文档:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
myquery = {"name": {"$regex": "^F"}}
x = mycol.delete_many(myquery)
print(x.deleted_count, "个文档已删除")
2
3
4
5
6
7
8
9
10
11
delete_many()方法如果传入的是一个空的查询对象,则会删除集合中的所有文档:
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
x = mycol.delete_many({})
print(x.deleted_count, "个文档已删除")
2
3
4
5
6
7
8
9
可以使用drop()方法删除一个集合。
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
mycol.drop()
2
3
4
5
6
7
# Python Mongodb排序
sort()可以指定升序或降序,第一个参数为要排序的字段,第二个字段指定排序规则,1为升序,-1为降序,默认为升序。
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
mydoc = mycol.find().sort("alexa")
for x in mydoc:
print(x)
2
3
4
5
6
7
8
9
import pymongo
myclient = pymongo.MongoClient("mongodb://192.168.1.79:27017/")
mydb = myclient["runoobdb"]
mycol = mydb["sites"]
mydoc = mycol.find().sort("alexa", -1)
for x in mydoc:
print(x)
2
3
4
5
6
7
8
9
# Python3 urllib
Python urllib库用于操作网页URL,并对网页的内容进行抓取处理。
urllib包含以下几个模块:
urllib.request: 打开和读取URLurllib.error: 包含urllib.request抛出的异常urllib.parse: 解析URLurllib.robotparser: 解析robots.txt文件
# urllib.request
urllib.request定义了一些打开URL的函数和类,包含授权验证、重定向、浏览器cookies等。
urllib.request可以模拟浏览器的一个请求发起过程。
可以使用urllib.request的urlopen方法打开一个URL,语法:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
url: url地址data: 发送到服务器的其他数据对象,默认为Nonetimeout: 设置访问超时时间cafile和capath: cafile为CA证书,capath为CA证书的路径,使用HTTPS需要用到。cadefault: 已经被弃用context: ssl.SSLContext类型,用来指定SSL设置
from urllib.request import urlopen
myURL = urlopen("https://www.runoob.com/")
print(myURL.read())
2
3
4
以上代码使用urlopen打开一个URL,然后使用read()函数获取网页的HTML实体代码。read()是读取整个网页的内容,也可以指定读取的长度:
from urllib.request import urlopen
myURL = urlopen("https://www.runoob.com/")
print(myURL.read(300))
2
3
4
除了read()外,还可以用下面两个函数:
readline(): 读取文件的一行内容
from urllib.request import urlopen
myURL = urlopen("https://www.runoob.com/")
print(myURL.readline())
2
3
4
readlines(): 读取文件的全部内容,其会把读取的内容赋值给一个列表变量。
from urllib.request import urlopen
myURL = urlopen("https://www.runoob.com/")
lines = myURL.readlines()
for line in lines:
print(line)
2
3
4
5
6
在对网页进行抓取时,经常需要判断网页是否可以正常访问,可以使用getcode()函数获取网页状态码,返回200说明网页正常,返回404说明网页不存在。
from urllib.request import urlopen
from urllib.error import HTTPError
myURL1 = urlopen("https://www.runoob.com/")
print(myURL1.getcode())
try:
myURL2 = urlopen("https://www.runoob.com/no.html")
except HTTPError as e:
if e.code == 404:
print(404)
2
3
4
5
6
7
8
9
10
11
如果要将抓取的网页保存到本地,可以使用Python3 File write()函数:
from urllib.request import urlopen
myURL = urlopen("https://www.runoob.com/")
f = open("runoob_urllib_test.html", "wb")
content = myURL.read()
f.write(content)
f.close()
2
3
4
5
6
7
执行以上代码,在本地就会生成一个runoob_urllib_test.html文件,里面包含了https://www.runoob.com/网页的内容。
URL的编码与解码可以使用urllib.parse.quote()与urllib.parse.unquote()方法:
from urllib.parse import quote, unquote
encode_url = quote("https://www.runoob.com/")
print(encode_url)
unencode_url = unquote(encode_url)
print(unencode_url)
2
3
4
5
6
7
抓取网页一般需要对headers(网页头信息)进行模拟,这就需要使用urllib.request.Request:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
url: url地址data: 发送到服务器的其他数据对象,默认为Noneheaders: HTTP请求的头部信息,字典格式origin_req_host: 请求的主机地址,IP或域名unverifiable: 很少用到这个参数,用于设置网页是否需要验证,默认是Falsemethod: 请求方法,如GET、POST、DELETE、PUT等
from urllib.request import urlopen, Request
from urllib.parse import quote
url = "https://www.runoob.com/?s="
keyword = "Python 教程"
key_code = quote(keyword)
url_all = url + key_code
header = {
"User-Agent": "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
request = Request(url_all, headers=header)
response = urlopen(request).read()
fh = open("./urllib_test_runoob_search.html", "wb")
fh.write(response)
fh.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
执行以上代码,会在当前目录生成urllib_test_runoob_search.html文件,此文件可以使用浏览器打开。
表单POST传递数据,使用PHP先创建一个表单,代码如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com) urllib POST 测试</title>
</head>
<body>
<form action="" method="post" name="myForm">
Name: <input type="text" name="name"><br>
Tag: <input type="text" name="tag"><br>
<input type="submit" value="提交">
</form>
<hr>
<?php
// 使用 PHP 来获取表单提交的数据,你可以换成其他的
if(isset($_POST['name']) && $_POST['tag'] ) {
echo $_POST["name"] . ', ' . $_POST['tag'];
}
?>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import urllib.request
import urllib.parse
url = 'https://www.runoob.com/try/py3/py3_urllib_test.php' # 提交到表单页面
data = {'name':'RUNOOB', 'tag' : '菜鸟教程'} # 提交数据
header = {
'User-Agent':'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
} #头部信息
data = urllib.parse.urlencode(data).encode('utf8') # 对参数进行编码,解码使用 urllib.parse.urldecode
request=urllib.request.Request(url, data, header) # 请求处理
reponse=urllib.request.urlopen(request).read() # 读取结果
fh = open("./urllib_test_post_runoob.html","wb") # 将文件写入到当前目录中
fh.write(reponse)
fh.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# urllib.error
urllib.error模块为urllib.request所引发的异常定义了异常类,基础异常类是URLError。
urllib.error包含了两个方法,URLError和HTTPError。URLError是OSError的一个子类,用于处理程序在遇到问题时会引发此异常(或其派生的异常),包含的属性reason为引发异常的原因。
HTTPError是URLError的一个子类,用于处理特殊HTTP错误例如认证请求,包含的属性code为HTTP的状态码,reason为引发异常的原因,headers为导致HTTPError的特定HTTP请求的HTTP响应头。
import urllib.request
import urllib.error
myURL1 = urllib.request.urlopen("https://www.runoob.com/")
print(myURL1.getcode())
try:
myURL2 = urllib.request.urlopen("https://www.runoob.com/no.html")
except urllib.error.HTTPError as e:
if e.code == 404:
print(404)
2
3
4
5
6
7
8
9
10
11
# urllib.parse
urllib.parse用于解析URL,格式:
urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
urlstring为字符串的url地址,schema为协议类型,allow_fragments参数为False,则无法识别片段标识符。相反,它们被解析为路径,参数或查询组件的一部分,并fragment在返回值中设置为空字符串。
from urllib.parse import urlparse
o = urlparse("https://www.runoob.com/?s=python+%E6%95%99%E7%A8%8B")
print(o)
# ParseResult(scheme='https', netloc='www.runoob.com', path='/', params='', query='s=python+%E6%95%99%E7%A8%8B', fragment='')
2
3
4
5
6
从结果可以看出,内容是一个元组,包含6个字符串: 协议,位置,路径,参数,查询,判断。
| 属性 | 索引 | 值 | 值(如果不存在) |
|---|---|---|---|
scheme | 0 | URL协议 | scheme参数 |
netloc | 1 | 网络位置部分 | 空字符串 |
path | 2 | 分层路径 | 空字符串 |
params | 3 | 最后路径元素的参数 | 空字符串 |
query | 4 | 查询组件 | 空字符串 |
fragment | 5 | 片段识别 | 空字符串 |
username | 用户名 | None | |
password | 密码 | None | |
hostname | 主机名(小写) | None | |
port | 端口号为整数(如果存在) | None |
# urllib.robotparser
urllib.robotparser用于解析robots.txt文件。robots.txt(统一小写)是一种存放于根目录下的robots协议,它通常用于告诉搜索引擎对网站的抓取规则。urllib.robotparser提供了RobotFileParser类,语法:
class urllib.robotparser.RobotFileParser(url='')
这个类提供了一些可以读取、解析robots.txt文件的方法:
set_url(url): 设置robots.txt文件的URLread(): 读取robots.txt URL并将其输入解析器parse(lines): 解析行参数can_fetch(useragent, url): 如果允许useragent按照被解析robots.txt文件中的规则来获取url则返回Truemtime(): 返回最近一次获取robots.txt文件的时间。这适用于需要定期检查robots.txt文件更新情况的长时间运行的网页爬虫。modified(): 将最近一次获取robots.txt文件的时间设置为当前时间crawl_delay(useragent): 为指定的useragent从robots.txt返回Crawl-delay形参。如果此形参不存在或不适用于指定的useragent或者此形参的robots.txt条目存在语法错误,则返回None。request_rate(useragent): 以named tuple RequestRate(requests, seconds)的形式从robots.txt返回Request-rate形参的内容。如果此形参不存在或不适用于指定的useragent或者此形参的robots.txt条目存在语法错误,则返回None。site_maps(): 以list()形式从robots.txt返回Sitemap形参的内容。如果此形参不存在或者此形参的robots.txt存在语法错误,返回None。
import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()
rp.set_url("http://www.musi-cal.com/robots.txt")
rp.read()
rrate = rp.request_rate("*")
print(rrate.requests)
print(rrate.seconds)
print(rp.crawl_delay("*"))
print(rp.can_fetch("*", "http://www.musi-cal.com/cgi-bin/search?city=San+Francisco"))
print(rp.can_fetch("*", "http://www.musi-cal.com/"))
2
3
4
5
6
7
8
9
10
11
# Python uWSGI安装配置
部署简单的WSGI应用核常见的WEB框架。
以Ubuntu/Debin为例,首先安装依赖:
apt-get install build-essential python-dev
- 通过pip命令
pip install uwsgi
- 下载安装脚本
curl http://uwsgi.it/install | bash -s default /tmp/uwsgi
- 源代码安装
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar -zxvf uwsgi-latest.tar.gz
cd uwsgi-latest
make
2
3
4
# 第一个WSGI应用
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b"Hello World"]
2
3
uWSGI Python加载器会搜索默认函数application,启动uWSGI运行一个HTTP服务器,将程序部署在HTTP端口9090上:
uwsgi --http :9090 --wsgi-file foobar.py
添加并发和监控: 默认情况下,uWSGI启动一个单一的进程和一个单一的线程。可以用--processes选项添加更多的进程,或者使用--threads选项添加更多的线程,也可以两者同时使用。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
可以安装uwsgitop(类似Linux top命令)来查看监控数据: pip install uwsgitop
# 结合Web服务器使用
可以将uWSGI和Nginx Web服务器结合使用,时限更高的并发性能。
一个常用的nginx配置:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
}
2
3
4
以上代码表示使用nginx接收的Web请求传递给端口为3031的uWSGI服务来处理,可以生成uWSGI来本地使用uwsgi协议:
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
如果Web服务器适应HTTP,那么必须告诉uWSGI本地使用http协议
uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
- 部署Django: Django是最常用的Python web框架,假设Django项目位于/home/foobar/myproject:
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --wsgi-file myproject/wsgi.py
--master --processes 4 --threads 2 --stats 127.0.0.1:9191
2
--chdir用于指定项目路径,可以吧以上的命令弄成一个yourfile.ini配置文件:
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
wsgi-file = myproject/wsgi.py
processes = 4
threads = 2
stats = 127.0.0.1:9191
2
3
4
5
6
7
接下来只需要执行以下命令即可: uwsgi yourfile.ini
- 部署Flask: Flask是一个流行的Python web框架,创建文件myflaskapp.py, 如:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "<span style='color:red'>I am app 1</span>"
2
3
4
5
6
7
执行以下命令:
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191
# Python3 pip
pip是Python包管理工具,该工具提供了对Python包的查找、下载、安装、卸载的功能。软件包可以在https://pypi.org/中找到。目前最新的Python版本已经预装了pip。
pip --version
pip install some-package-name
pip install numpy
pip uninstall some-package-name
pip uninstall some-package-name
pip uninstall numpy
pip list
2
3
4
5
6
7
导出当前Python环境的配置: 要导出当前Python环境的配置,可以使用pip freeze命令。pip freeze命令会列出当前环境中已安装的所有Python包及其版本信息,可以将其保存到文件中,如requirements.txt,如:
pip freeze > requirements.txt
以上命令将在当前目录下创建一个名为requirements.txt的文件,其中包含当前环境中已安装的所有包及其版本信息。然后,可以在其他地方使用该文件来重新创建相同的环境,命令:
pip install -r requirements.txt
以上命令会根据requirements.txt中列出的包及其版本信息重新安装所有必需的包,从而重建相同的环境。
# 扩展内容: Anaconda
Anaconda发行版包含了Python,它是一个集成的数据科学核机器学习环境,其中包括了Python解释器以及大量常用的数据科学核工具。Anaconda包及其依赖项和环境的管理工具为conda命令,与pip相比conda可以更方便地在不同环境之间进行切换,环境管理较为简单。
# Python3 operator
Python2.x版本中,使用cmp()函数来比较两个列表、数字或字符串等的大小关系。Python3.x已经没有cmp()函数,如果需要实现比较功能,需要引入operator模块,适合任何对象,包含的方法有:
# operator模块包含的方法
operator.lt(a, b)
operator.le(a, b)
operator.eq(a, b)
operator.ne(a, b)
operator.ge(a, b)
operator.gt(a, b)
operator.__lt__(a, b)
operator.__le__(a, b)
operator.__eq__(a, b)
operator.__ne__(a, b)
operator.__ge__(a, b)
operator.__gt__(a, b)
2
3
4
5
6
7
8
9
10
11
12
13
operator.lt(a, b)与a < b相同,operator.le(a, b)与a <= b相同,operator.eq(a, b)与a == b相同,operator.ne(a, b)与a != b相同,operator.gt(a, b)与a > b相同,operator.ge(a, b)与a >= b相同。
import operator
x = 10
y = 20
print("x: ", x, ", y: ", y)
print("operator.lt(x, y): ", operator.lt(x, y))
print("operator.gt(y, x): ", operator.gt(y, x))
print("operator.eq(x, x): ", operator.eq(x, x))
print("operator.ne(y, y): ", operator.ne(y, y))
print("operator.le(x, y): ", operator.le(x, y))
print("operator.ge(y, x): ", operator.ge(y, x))
x = "Google"
y = "Runoob"
print("x: ", x, ", y: ", y)
print("operator.lt(x, y): ", operator.lt(x, y))
print("operator.gt(y, x): ", operator.gt(y, x))
print("operator.eq(x, x): ", operator.eq(x, x))
print("operator.ne(y, y): ", operator.ne(y, y))
print("operator.le(x, y): ", operator.le(x, y))
print("operator.ge(y, x): ", operator.ge(y, x))
print()
print("type(operator.lt(x, y)): ", type(operator.lt(x, y)))
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

import operator
a = [1, 2]
b = [2, 3]
c = [2, 3]
print("operator.eq(a, b): ", operator.eq(a, b))
print("operator.eq(c, b): ", operator.eq(c, b))
2
3
4
5
6
7
8
# 运算符函数
operator模块提供了一套与Python的内置运算符对应的高效率函数。如: operator.add(x, y)与表达式x+y相同。函数包含的种类有: 对象的比较运算、逻辑运算、数学运算以及序列运算。对象比较函数适用于所有的对象,函数名根据它们对应的比较运算符命名。许多函数名与特殊方法名相同,只是没有双下划线。为了向后兼容,也保留了许多包含双下划线的函数,为了表述清楚,建议使用没有双下划线的函数。
import operator
a = 4
b = 3
print("add()运算结果: ", end="")
print(operator.add(a, b))
print("sub()运算结果: ", end="")
print(operator.sub(a, b))
print("mul()运算结果: ", end="")
print(operator.mul(a, b))
2
3
4
5
6
7
8
9
10
11
12
13
| 运算 | 语法 | 函数 |
|---|---|---|
| 加法 | a + b | add(a, b) |
| 字符串拼接 | seq1 +seq2 | concat(seq1, seq2) |
| 包含测试 | obj in seq | concats(seq, obj) |
| 除法 | a / b | truediv(a, b) |
| 除法 | a // b | floordiv(a, b) |
| 按位与 | a & b | and_(a, b) |
| 按位异或 | a ^ b | xor(a, b) |
| 按位取反 | ~a | invert(a) |
| 按位或 | a \| b | or_(a, b) |
| 取幂 | a ** b | pow(a, b) |
| 标识 | a is b | is_(a, b) |
| 标识 | a is not b | is_not(a, b) |
| 索引赋值 | obj[k] = v | setitem(obj, k, v) |
| 索引删除 | del obj[k] | delitem(obj, k) |
| 索引取值 | obj[k] | getitem(obj, k) |
| 左移 | a << b | lshift(a, b) |
| 取模 | a % b | mod(a, b) |
| 乘法 | a * b | mul(a, b) |
| 矩阵乘法 | a @ b | matmul(a, b) |
| 取反(算术) | -a | neg(a) |
| 取反(逻辑) | not a | not_(a) |
| 正数 | +a | pos(a) |
| 右移 | a >> b | rshift(a, b) |
| 切片赋值 | seq[i:j] = values | setitem(seq, slice(i, j), values) |
| 切片删除 | del seq[i:j] | delitem(seq, slice(i, j)) |
| 切片取值 | seq[i:j] | getitem(seq, slice(i, j)) |
| 字符串格式化 | s % obj | mod(s, obj) |
| 减法 | a - b | sub(a, b) |
| 真值测试 | obj | truth(obj) |
| 比较 | a < b | lt(a, b) |
| 比较 | a <= b | le(a, b) |
| 相等 | a == b | eq(a, b) |
| 不等 | a != b | ne(a, b) |
| 比较 | a >= b | ge(a, b) |
| 比较 | a > b | gt(a, b) |
# Python math
Python math模块提供了许多对浮点数的数学运算函数。math模块下的函数,返回值均为浮点数,除非另有明确说明。如果需要计算复数,需使用cmath模块中的同名函数。
import math
print(dir(math))
2

# math模块常量
| 常量 | 描述 |
|---|---|
math.e | 返回欧拉数(2.7182...) |
math.inf | 返回正无穷大浮点数 |
math.nan | 返回一个浮点值NaN(not a number) |
math.pi | π一般指圆周率 |
math.tau | 数学常数 τ = 6.283185...,精确到可用精度。Tau 是一个圆周常数,等于 2π,圆的周长与半径之比。 |
# math模块方法
| 方法 | 描述 |
|---|---|
math.acos(x) | 返回x的反余弦,结果范围在0到pi之间 |
math.acosh(x) | 返回x的反双曲余弦值 |
math.asin(x) | 返回x的反正弦值,结果范围在-pi/2到pi/2之间 |
math.asinh(x) | 返回x的反双曲正弦值 |
math.atan(x) | 返回x的反正切值,结果范围在-pi/2到pi/2之间 |
math.atan2(y, x) | 返回给定的x及y坐标值的反正切值,结果在-pi到pi之间 |
math.atanh(x) | 返回x的反双曲正切值 |
math.ceil(x) | 将x向上舍入到最接近的整数 |
math.comb(n, k) | 返回不重复且无顺序地从n项中选择k项的方式总数 |
math.copysign(x, y) | 返回一个基于x的绝对值和y的符号的浮点数 |
math.cos() | 返回x弧度的余弦值 |
math.cosh(x) | 返回x的双曲余弦值 |
math.degrees(x) | 将角度x从弧度转换为度数 |
math.dist(p, q) | 返回p与q两点之间的欧几里得距离,以一个坐标序列(或可迭代对象)的形式给出。两个点必须具有相同的维度。 |
math.erf(x) | 返回一个数的误差函数 |
math.erfc(x) | 返回x处的互补误差函数 |
math.exp(x) | 返回e的x次幂,E^x, 其中 e = 2.718281... 是自然对数的基数。 |
math.expm1() | 返回E^x-1,e的x次幂,这通常比 math.e ** x 或 pow(math.e, x) 更精确。 |
math.fabs(x) | 返回x的绝对值 |
math.factorial(x) | 返回x的阶乘,如果x不是整数或为负数时,引发ValueError |
math.floor() | 将数字向下舍入到最接近的整数 |
math.fmod(x, y) | 返回x/y的余数 |
math.frexp(x) | 以(m, e)对的形式返回x的尾数核指数。m是一个浮点数,e是一个整数,正好是 x == m * 2**e 。 如果 x 为零,则返回 (0.0, 0) ,否则返回 0.5 <= abs(m) < 1 。 |
math.fsum(iterable) | 返回可迭代对象(元组,数组,列表,等)中的元素总和,是浮点值 |
math.gamma(x) | 返回x处的伽马函数值 |
math.gcd() | 返回给定的整数参数的最大公约数 |
math.hypot() | 返回欧几里得范数,sqrt(sum(x**2 for x in coordinates))。 这是从原点到坐标给定点的向量长度。 |
math.isclose(a, b) | 检查两个值是否彼此阶级恩,若a和b的值比较接近则返回True,否则False |
math.isfinite(x) | 判断x是否有限,如果x既不是无穷大也不是NaN,则返回True,否则返回False |
math.isinf(x) | 判断x是否是无穷大,如果x是正或负无穷大,返回True,否则False |
math.isnan() | 判断数字是否为NaN |
math.isqrt() | 将平方根数向下舍入到最接近的整数 |
math.ldexp(x, i) | 返回 x * (2**i) 。 这基本上是函数 math.frexp() 的反函数。 |
math.lgamma() | 返回伽马函数在x绝对值的自然对数 |
math.log(x[, base]) | 使用一个参数,返回x的自然对数(底为e) |
math.log10(x) | 返回x底为10的对数 |
math.log1p(x) | 返回1+x的自然对数(以e为底) |
math.log2(x) | 返回x以2为底的对数 |
math.perm(n, k=None) | 返回不重复且有顺序地从n项中选择k项的方式总数 |
math.pow(x, y) | 返回x的y次幂 |
math.prod(iterable) | 计算可迭代对象中所有元素的积 |
math.radians(x) | 将角度x从度数转换为弧度 |
math.remainder(x, y) | 返回IEEE 754风格的x除以y的余数 |
math.sin(x) | 返回x弧度的正弦值 |
math.sinh(x) | 返回x的双曲正弦值 |
math.sqrt(x) | 返回x的平方根 |
math.tan(x) | 放回x弧度的正切值 |
math.tanh(x) | 返回x的双曲正切值 |
math.trunc(x) | 返回x截断整数的部分,即返回整数部分,删除小数部分 |
# Python requests
Python requests是一个常用的HTTP请求库,可以方便地项网站发送HTTP请求,并获取相应结果。requests比urllib模块更简洁。
from requests import get
x = get("https://www.runoob.com/")
print(x.text)
2
3
每次调用requests请求之后,会返回一个response对象,该对象包含了具体的响应信息,如状态码、响应头、响应内容等:
print(response.status_code)
print(response.headers)
print(response.content)
2
3
| 属性或方法 | 说明 |
|---|---|
apparent_encoding | 编码方式 |
close() | 关闭与服务器的连接 |
content | 返回响应的内容,以字节为单位 |
cookies | 返回一个CookieJar对象,包含了从服务器发回的cookie |
elapsed | 返回一个timedelta对象,包含了从发送请求到响应到达之间经过的时间量,可以用于测试响应速度,比如r.elapsed.microseconds表示响应到达需要多少微秒 |
encoding | 解码r.text的编码方式 |
headers | 返回响应头,字典格式 |
history | 返回包含请求历史的响应对象列表(url) |
is_permanent_redirect | 如果响应是永久重定向的url,返回True,否则False |
is_redirect | 如果响应被重定向,返回True,否则False |
iter_content() | 迭代响应 |
iter_lines() | 迭代响应行 |
json() | 返回结果的JSON对象(结果需要以JSON格式编写,否则引发错误) |
links | 返回响应的解析头链接 |
next | 返回重定向链中下一个请求的PreparedRequest对象 |
ok | 检查status_code的值,如果小于400,则返回True,否则False |
raise_for_status() | 如果发生错误,返回一个HTTPError对象 |
reason | 响应状态的描述,如Not Found或OK |
request | 返回请求此响应的请求对象 |
status_code | 返回http的状态码 |
text | 返回响应的内容,unicode类型数据 |
url | 返回响应的URL |
from requests import get
x = get("https://www.runoob.com/")
print(x.status_code)
print(x.reason)
print(x.apparent_encoding)
2
3
4
5
from requests import get
x = get("https://www.runoob.com/try/ajax/json_demo.json")
print(x.json())
2
3
4
# request方法
| 方法 | 描述 |
|---|---|
delete(url, args) | 发送DELETE请求到指定url |
get(url, params, args) | 发送GET请求到指定url |
head(url, args) | 发送HEAD请求到指定url |
patch(url, data, args) | 发送PATCH请求到指定url |
post(url, data, json, args) | 发送POST请求到指定url |
put(url, data, args) | 发送PUT请求到指定url |
request(method, url, args) | 向指定的url发送指定的请求方法 |
import requests
x = requests.request("get", "https://www.runoob.com/")
print(x.status_code)
2
3
4
# 导入 requests 包
import requests
kw = {'s':'python 教程'}
# 设置请求头
headers = {"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("https://www.runoob.com/", params = kw, headers = headers)
# 查看响应状态码
print (response.status_code)
# 查看响应头部字符编码
print (response.encoding)
# 查看完整url地址
print (response.url)
# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
post()格式:
url: 请求urldata: 要发送到指定url的字典、元组列表、字节或文件对象json: 要发送到指定url的JSON对象args: 其他参数,如cookies,headers,verify
# 导入 requests 包
import requests
# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post.php')
# 返回网页内容
print(x.text)
2
3
4
5
6
7
8
# 导入 requests 包
import requests
# 表单参数,参数名为 fname 和 lname
myobj = {'fname': 'RUNOOB','lname': 'Boy'}
# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post2.php', data = myobj)
# 返回网页内容
print(x.text)
2
3
4
5
6
7
8
9
10
11
# 附加请求参数
headers = {'User-Agent': 'Mozilla/5.0'} # 设置请求头
params = {'key1': 'value1', 'key2': 'value2'} # 设置查询参数
data = {'username': 'example', 'password': '123456'} # 设置请求体
response = requests.post('https://www.runoob.com', headers=headers, params=params, data=data)
2
3
4
# Python random
Python random模块主要用于生成随机数。random模块实现了各种分布的伪随机数生成器。
import random
print(dir(random))
2
3

import random
print(random.random())
2
3
seed()方法改变随机数生成器的种子,可以在调用其他随机模块函数之前调用此函数。
import random
random.seed()
print("使用默认种子生成随机数: ", random.random())
print("使用默认种子生成随机数: ", random.random())
random.seed(10)
print("使用整数 10 种子生成随机数: ", random.random())
random.seed(10)
print("使用整数 10 种子生成随机数: ", random.random())
random.seed("hello", 2)
print("使用字符串种子生成随机数: ", random.random())
2
3
4
5
6
7
8
9
10
11
12
13

# random 模块方法
| 方法 | 描述 |
|---|---|
seed() | 初始化随机数生成器 |
getstate() | 返回捕获生成器当前内部状态的对象 |
setstate() | state应该是从之前调用getstate()获得的,并且setstate()将生成器的内部状态恢复到getstate()被调用时的状态 |
getrandbits(k) | 返回具有k个随机比特位的非负Python整数。此方法随MersenneTwister生成器一起提供,其他一些生成器也可将其作为API的可选部分提供。在可能的情况下,getrandbits()会启用randrange()来处理任意大的区间。 |
randrange() | 从range(start, stop, step)返回一个随机选择的元素 |
randint(a, b) | 返回随机整数N满足 a <= N <= b |
choice(seq) | 从非空序列seq返回一个随机元素。如果seq为空,则引发IndexError |
choices(population, weights=None, *, cum_weights=None, k=1) | 从population中选择替换,返回大小为k的元素列表。如果population为空,则引发IndexError |
shuffle(x[, random]) | 将序列x随机打乱位置 |
sample(population, k, *, counts=None) | 返回从总体序列或集合中选择的唯一元素的k长度列表,用于无重复的随机抽样。 |
random() | 返回[0.0, 1.0]范围内的下一个随机浮点数 |
uniform() | 返回一个随机浮点数N,当 a <= b 时 a <= N <=b ,当 b < a 时 b <= N <= a |
triangular(low, high, mode) | 返回一个随机浮点数N,使得 low <= N <= high 并在这些边界之间使用指定的mode。low和high边界默认为零和一。mode参数默认为边界之间的中点,给出对称分布。 |
betavariate(alpha, beta) | Beta分布,参数的条件时alpha>0和beta>0.返回值的范围介于0和1之间。 |
expovariate(lambd) | 指数分布。lambd是1.0除以所需的平均值,他应该非零的。 |
gammavariate() | Gamma分布(不是伽马函数)参数的条件是alpha>0和beta>0 |
gauss(mu, sigma) | 正态分布,也成高斯分布。mu为平均值,而sigma为标准差 |
lognormvariate(mu, sigma) | 对数正态分布。如果采用这个分布的自然对数,将得到一个正态分布,平均值为mu和标准差为sigma。mu可以是任何值,sigma必须大于零 |
normalvariate(mu, sigma) | 正态分布。mu是平均值,sigma时标准差 |
vonmisesvariate(mu, kappa) | 冯米塞斯分布,mu是平均角度,以弧度表示,介于0和2*pi之间,kappa是浓度参数,必须大于或等于零。如果kappa等于零,则该分布在0到2*pi的范围内减小到均匀的随机角度 |
paretovariate(alpha) | 帕累托分布,alpha是形状参数。 |
weibullvariate(alpha, beta) | 威布尔分布,alpha是比例参数,beta是形状参数。 |
# Python 有用的资源
Python 3.6.3 中文手册 Python3 最新文档 Python 算法学习
# Python AI 绘画
Stable Diffusion Web UI,它是基于Gradio库的Stable Diffusion浏览器界面。Stable Diffusion Web UI GitHub地址: https://github.com/AUTOMATIC1111/stable-diffusion-webui。
运行Stable Diffusion需要硬件要求比较高,运行时会消耗较大的资源,特别是显卡。
# Windows 环境安装
本地环境要求安装Python 3.10.6或以上版本,并把它加入到本机的环境变量中。
下载Stable Diffusion Web UI GitHub源码:
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
接下来需要下载模型,下载地址: https://huggingface.co/CompVis/stable-diffusion-v-1-4-original
将下载的模型移动到stable-diffusion-webui/models/Stable-diffusion目录下,进入stable-diffusion-webui目录,Windows使用非管理员运行: webui-user.bat,Linux和Mac OS执行: ./webui.sh
接下来程序会自动安装并启动,启动成功会看到一个可访问的URL地址http://127.0.0.1:7860
# Civitai 介绍
Civitai有许多定制好的模型,而且可以免费下载,比如使用国风3模型测试,下载地址: https://civitai.com/models/10415/3-guofeng3?modelVersionId=36644,下载完成后,将模型移动到stable-diffusion-webui/models/Stable-diffusion目录下,重新启动: ./webui.sh,这样就可以在模型列表中选择国风3模型了。选择后,可以到模型介绍页面拷贝一些提示词和测试参数.
# Python statistics
在数据分析和科学计算中,统计学是一个非常重要的工具。Python提供了一个内置的statistics模块,专门用于处理基本的统计计算。statistics模块提供了许多常用的统计函数,如均值、中位数、方差、标准差等。
import statistics
print(dir(statistics))
2
3

# 常用的统计函数
- 均值(Mean): 均值是数据集中所有数值的平均值,
mean() - 中位数(Median): 中位数是将数据集按大小顺序排列后位于中间位置的数值,
median(),如果数据集的长度为偶数,其会自动计算中间两个数的平均值 - 众数(Mode): 众数是数据集中出现频率最高的数值,
mode(),如果数据集中没有重复的数值,其会抛出StatisticsError异常 - 方差(Variance): 方差是衡量数据集中数值离散程度的指标,
variance() - 标准差(Standard Deviation): 标准差是方差的平方根,用于衡量数据集的离散程度,
stdev() - 调和平均数(Harmonic Mean): 调和平均数是一种特殊的平均数,适用于计算速率等场景,
harmonic_mean() - 几何平均数(Geometric Mean): 几何平均数是一种用于计算增长率或比例的平均数,
geometric_mean()
from statistics import mean, median, mode, variance, stdev, harmonic_mean, geometric_mean
data = [1, 2, 3, 4, 5]
mean_value = mean(data)
print("均值: ", mean_value)
median_value = median(data)
print("中位数: ", median_value)
data1 = [1, 2, 3, 4]
median_value1 = median(data1)
print("中位数1: ", median_value1)
data2 = [1, 2, 2, 3, 4]
mode_value = mode(data2)
print("众数: ", mode_value)
variance_value = variance(data)
print("方差: ", variance_value)
stdev_value = stdev(data)
print("标准差: ", stdev_value)
data3 = [1, 2, 4]
harmonic_mean_value = harmonic_mean(data3)
print("调和平均数: ", harmonic_mean_value)
geometric_mean_value = geometric_mean(data3)
print("几何平均数: ", geometric_mean_value)
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

# 其他常用函数
- 中位数低(Median Low)和中位数高(Median High): 用于计算数据集的中位数低和中位数高
- 分位数(Quantiles): 分位数将数据集分成若干等分的数值,
quantiles()
from statistics import median_low, median_high, quantiles
data = [1, 2, 3, 4]
median_low_value = median_low(data)
median_high_value = median_high(data)
print("中位数低: ", median_low_value) # 2
print("中位数高: ", median_high_value) # 3
data1 = [1, 2, 3, 4, 5]
quantiles_value = quantiles(data1)
print("四分位数: ", quantiles_value) # [1.5, 3.0, 4.5]
2
3
4
5
6
7
8
9
10
11
# Python hashlib
hashlib模块主要用于进行哈希(hash)操作。哈希(hash)是一种将任意长度的输入数据映射为固定长度输出数据的算法。哈希通常用于验证数据的完整性、安全存储密码等场景。哈希函数的输出通常是一串看似随机的字母核数字。
import hashlib
print(dir(hashlib))
2
3

# 常用方法
hashlib.new(name, data=None): 创建一个哈希对象,name是哈希算法的名称,data是要被哈希的数据
from hashlib import new
sha256_hash = new("sha256")
sha256_hash.update(b"RUNOOB")
print(sha256_hash.hexdigest())
# 673dc967d03201db7fe47b7eabd56c47ca5bc694222de303106a5504e5d0daa8
2
3
4
5
6
hashlib.md5() / hashlib.sha1() / hashlib.sha256(): 可以直接使用特定的哈希算法创建哈希对象。
from hashlib import md5
md5_hash = md5(b"RUNOOB")
print(md5_hash.hexdigest())
# 18fa661e2a4a7dd6471cc1407290cf6e
2
3
4
5
# 哈希对象方法
update(data): 更新哈希对象的消息内容
from hashlib import sha256
sha256_hash = sha256()
sha256_hash.update(b"Hello, ")
print(sha256_hash.hexdigest())
# 23429bd9ba98dd5140309bb9b0094b3aad642430fff6fb3ca61f008ce644f34a
sha256_hash.update(b"Runoob!")
print(sha256_hash.hexdigest())
# 1b56561022276e9a5a8e1cda72e1b39fca6f6074326a74d39f6dfd9540c8ecd7
2
3
4
5
6
7
8
9
10
hexdigest(): 获取十六进制表示的哈希值digest(): 获取二进制表示的哈希值
from hashlib import sha1
sha1_hash = sha1()
sha1_hash.update(b"RUNOOB")
print(sha1_hash.digest())
2
3
4
5
# 常见哈希算法
- MD5
- SHA-1
- SHA-256
- SHA-512
在实际应用中,选择合适的哈希算法取决于具体的需求,需要注意的是,MD5和SHA-1已经被认为不安全的,推荐使用更强大的算法,如SHA-256或SHA-512.
| 算法名称 | 摘要长度(位) | 输出长度(字节) | 安全性 | 用途 |
|---|---|---|---|---|
md5 | 128 | 16 | 不安全 | 数据完整性验证、密码存储等 |
sha1 | 160 | 20 | 不安全 | 数据完整性验证、密码存储等 |
sha224 | 224 | 28 | 低 | 数据完整性验证、数字签名 |
sha256 | 256 | 32 | 中 | 数据完整性验证、数字签名等 |
sha384 | 384 | 48 | 高 | 数字签名、加密算法等 |
sha512 | 512 | 64 | 高 | 数字签名、加密算法等 |
sha3_224 | 224 | 28 | 高 | 未来标准的SHA-3家族成员,适用于数字签名等 |
sha3_256 | 256 | 32 | 高 | 未来标准的SHA-3家族成员,适用于数字签名等 |
sha3_384 | 384 | 48 | 高 | 未来标准的SHA-3家族成员,适用于数字签名等 |
sha3_512 | 512 | 64 | 高 | 未来标准的SHA-3家族成员,适用于数字签名等 |
shake_128 | 可变 | 可变 | 高 | SHAKE系列是SHA-3家族的可变长度版本,适用于各种应用 |
shake_256 | 可变 | 可变 | 高 | SHAKE系列是SHA-3家族的可变长度版本,适用于各种应用 |
说明:
- 摘要长度(位): 表示哈希算法输出的摘要长度,以位为单位
- 输出长度(字节): 表示哈希算法输出的摘要长度,以字节为单位
- 安全性: 表示哈希算法的安全性级别,包括不安全、低、中、高。这是一个一般性分类,具体的安全性还要考虑算法的用途核具体的共计场景。
# Python 量化
Python量化是指利用Python编程语言以及相关的库和工具来进行金融市场数据分析、策略开发和交易执行的过程。Python由于其简洁、易学、强大的生态系统和丰富的金融库而成为量化交易的首选编程语言之一。量化交易在金融领域得到广泛应用,它允许交易者通过系统性的方法来制定和执行交易策略,提高交易效率和决策的科学性。量化主要是通过数学和统计的方法,利用计算机技术对金融市场进行量化分析,从而制定和执行交易策略。量化交易
下面可以先看一个Python量化简单的应用实例,可以使用移动平均策略,使用雅虎金融数据来实现。该策略的基本思想是通过比较短期和长期移动平均线来生成买入和卖出信号。需要先安装三个包: pip install pandas yfinance matplotlib
pandas: 一个功能强大的开源数据处理和分析库,专门设计用于高效地进行数据分析和操作。yfinance: 一个用于获取金融数据的库,支持从Yahoo Finance获取股票、指数和其他金融市场数据。matplotlib: 一个二维图库,用于创建静态、动态和交互式的数据可视化图表。
# 获取历史股票数据
使用yfinance获取历史股票数据,如:
import yfinance as yf
symbol = "600519.SS"
start_date = "2025-04-01"
end_date = "2025-04-20"
data = yf.download(symbol, start=start_date, end=end_date, auto_adjust=False)
print(data.head())
2
3
4
5
6
7
8
# 简单的数据分析和可视化
使用pandas进行数据分析和matplotlib进行可视化:
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
# 获取股票数据
symbol = "600519.SS"
start_date = "2022-01-01"
end_date = "2023-01-01"
data = yf.download(symbol, start=start_date, end=end_date)
# 简单的数据分析
print(data.describe())
# 绘制股价走势图
data['Close'].plot(figsize=(10, 6), label=symbol)
plt.title(f"{symbol} Stock Price")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.show()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 移动平均交叉策略回测
回测是在历史市场数据上模拟和评估一个交易策略的过程,以下是一个简单的移动平均交叉策略回测的实例代码,策略是在50日均线上穿越200日均线时买入,下穿越时卖出,策略的表现输出了总收益、年化收益和最大回撤等指标。
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
# 获取股票数据
symbol = "600519.SS"
start_date = "2021-01-01"
end_date = "2023-01-01"
data = yf.download(symbol, start=start_date, end=end_date)
# 计算移动平均
data['SMA_50'] = data['Close'].rolling(window=50).mean()
data['SMA_200'] = data['Close'].rolling(window=200).mean()
# 初始化交叉信号列
data['Signal'] = 0
# 计算交叉信号
data.loc[data['SMA_50'] > data['SMA_200'], 'Signal'] = 1
data.loc[data['SMA_50'] < data['SMA_200'], 'Signal'] = -1
# 计算每日收益率
data['Daily_Return'] = data['Close'].pct_change()
# 计算策略信号的收益率(shift(1) 是为了避免未来数据的偏差)
data['Strategy_Return'] = data['Signal'].shift(1) * data['Daily_Return']
# 计算累计收益
data['Cumulative_Return'] = (1 + data['Strategy_Return']).cumprod()
# 输出策略表现
strategy_performance = {
'Total Return': data['Cumulative_Return'].iloc[-1] - 1,
'Annualized Return': (data['Cumulative_Return'].iloc[-1] ** (252 / len(data))) - 1,
'Max Drawdown': (data['Cumulative_Return'] / data['Cumulative_Return'].cummax() - 1).min(),
}
print("策略表现:")
for key, value in strategy_performance.items():
print(f"{key}: {value:.4f}")
# 绘制累计收益曲线
plt.figure(figsize=(10, 6))
plt.plot(data['Cumulative_Return'], label='Strategy Cumulative Return', color='b')
plt.plot(data['Close'] / data['Close'].iloc[0], label='Stock Cumulative Return', color='g')
plt.title("Cumulative Return of Strategy vs. Stock")
plt.xlabel("Date")
plt.ylabel("Cumulative Return")
plt.legend()
plt.show()
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
# Python pyecharts
pyecharts是一个基于ECharts的Python数据可视化库,允许用户使用Python语言生成各种类型的交互式图表和数据可视化。ECharts是一个使用JavaScript实现的开源可视化库,而Pyecharts是ECharts的Python封装。
pyecharts提供了一组简单而灵活的API,能够轻松地创建各种图表,包括但不限于折线图、柱状图、散点图、饼图、地图等。通过pyecharts,用户可以使用Python语言处理和准备数据,然后使用简洁的代码生成交互式的图表,这些图表可以嵌入到WEB应用程序中或保存为静态文件。
pyecharts特点与功能:
- 简单易用
- 丰富的图表类型
- 支持主流数据格式
- 交互性
- 丰富的配置选项
- 支持主题
# pyecharts安装
pip install pyecharts
# 源码安装
git clone https://github.com/pyecharts/pyecharts.git
cd pyecharts
pip install -r requirements.txt
python setup.py install
# 或 python install.py
2
3
4
5
6
7
8
安装成功后可以查看pyecharts版本:
import pyecharts
print(pyecharts.__version__)
# 2.0.8
2
3
4
# pyecharts图表类型
| 图表类型 | pyecharts类 | 包引入 |
|---|---|---|
| 折线图 | Line | from pyecharts.charts import Line |
| 柱状图 | Bar | from pyecharts.charts import Bar |
| 散点图 | Scatter | from pyecharts.charts import Scatter |
| 饼图 | Pie | from pyecharts.charts import Pie |
| 雷达图 | Radar | from pyecharts.charts import Radar |
| 热力图 | HeatMap | from pyecharts.charts import HeatMap |
| K线图 | Kline | from pyecharts.charts import Kline |
| 箱线图 | Boxplot | from pyecharts.charts import Boxplot |
| 地图 | Map | from pyecharts.charts import Map |
| 词云图 | WordCloud | from pyecharts.charts import WordCloud |
| 仪表盘 | Gauge | from pyecharts.charts import Gauge |
| 漏斗图 | Funnel | from pyecharts.charts import Funnel |
| 树图 | Tree | from pyecharts.charts import Tree |
| 平行坐标系图 | Parallel | from pyecharts.charts import Parallel |
| 桑基图 | Sankey | from pyecharts.charts import Sankey |
| 地理坐标系图 | Geo | from pyecharts.charts import Geo |
| 时间线图 | Timeline | from pyecharts.charts import Timeline |
| 3D散点图 | Scatter3D | from pyecharts.charts import Scatter3D |
| 3D柱状图 | Bar3D | from pyecharts.charts import Bar3D |
| 3D曲面图 | Surface3D | from pyecharts.charts import Surface3D |
# 创建第一个图表
from pyecharts.charts import Bar
# 准备数据
x_data = ["一月", "二月", "三月", "四月", "五月"]
y_data = [10, 20, 15, 25, 30]
# 创建柱状图
bar_chart = Bar()
bar_chart.add_xaxis(x_data)
bar_chart.add_yaxis("销售额", y_data)
# 也可以传入路径参数, 如 bar_charts.render("bar_chart.html")
bar_chart.render()
2
3
4
5
6
7
8
9
10
11
12
13
如果在bar_chart.render()中不指定文件路径,Pyecharts默认会在当前工作目录下生成一个名为render.html的文件,即生成的图表将保存在render.html文件中。如果希望图表的文件名有一定的规范,或希望指定保存的路径,可以在render()方法中提供文件路径参数,如: bar_chart.render("my_bar_chart.html")
实例中图表的标题是月度销售额柱状图,横轴是月份,纵轴是销售额,可以根据实际需求调整数据和图表配置:
from pyecharts.charts import Bar
from pyecharts import options as opts
# 准备数据
x_data = ["一月", "二月", "三月", "四月", "五月"]
y_data = [10, 20, 15, 25, 30]
# 创建柱状图
bar_chart = Bar()
bar_chart.add_xaxis(x_data)
bar_chart.add_yaxis("销售额", y_data)
# 配置图表
bar_chart.set_global_opts(title_opts=opts.TitleOpts(title="月度销售额柱状图"), xaxis_opts=opts.AxisOpts(name="月份"), yaxis_opts=opts.AxisOpts(name="销售额(万元)"))
# 也可以传入路径参数, 如 bar_charts.render("bar_chart.html")
bar_chart.render("bar_chart.html")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
说明:
Bar(): 创建一个柱状图对象add_xaxis和add_yaxis: 分别用于添加横轴和纵轴的数据set_global_opts: 配置全局选项,包括标题、坐标轴的名称等。
pyecharts支持主题切换,可以根据需求选择合适的主题改变图表样式。pyecharts提供了10+中内置主题,开发者也可以定制自己喜欢的主题。
from pyecharts.charts import Bar
from pyecharts import options as opts
from pyecharts.globals import ThemeType
# 准备数据
x_data = ["一月", "二月", "三月", "四月", "五月"]
y_data = [10, 20, 15, 25, 30]
# 创建柱状图
bar_chart = Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
bar_chart.add_xaxis(x_data)
bar_chart.add_yaxis("销售额", y_data)
# 配置图表
bar_chart.set_global_opts(title_opts=opts.TitleOpts(title="月度销售额柱状图"), xaxis_opts=opts.AxisOpts(name="月份"), yaxis_opts=opts.AxisOpts(name="销售额(万元)"))
# 也可以传入路径参数, 如 bar_charts.render("bar_chart.html")
bar_chart.render("dark_bar_chart.html")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
以下是pyecharts支持的主题列表:
- Light Themes(亮色系主题):
- LIGHT: 亮色系默认主题
- WESTEROS: 经典的暖色调主题
- CHALK: 粉笔风格主题
- ESSOS: 温和的绿色调主题
- INFOGRAPHIC: 信息图形主题
- MACARONS: 美味糖果色主题
- Dark Themes(暗色系主题):
- DARK: 暗色系默认主题
- PURPLE-PASSION: 深紫色主题
- SHINE: 简洁的黑色主题
- VINTAGE: 复古风格主题
- ROMA: 古罗马风格主题
- WALDEN: 森林深色主题
用户可以通过设置自定义的颜色和样式创建自定义主题。
# 设置全局配置项
set_global_opts是pyecharts中用于设置全局配置项的方法,该方法允许配置整个图表的一些全局属性,如标题、坐标轴、图例等:
bar_chart.set_global_opts(
title_opts=opts.TitleOpts(title="月度销售额柱状图", subtitle="副标题"), # 标题配置项,可以设置主标题和副标题,一级样式
xaxis_opts=opts.AxisOpts(name="月份"), # x轴,可以设置轴的名称和轴线样式
yaxis_opts=opts.AxisOpts(name="销售额(万元)"), # y轴,可以设置轴的名称和轴线样式
legend_opts=opts.LegendOpts(pos_left="center", pos_top="top"), # 图例配置项,可以设置图例的位置、样式
toolbox_opts=opts.ToolboxOpts(), # 工具箱配置项,用于添加一些交互工具,如保存为图片、数据视图等
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"), # 提示框配置项,可以设置提示框的触发方式、样式等
)
2
3
4
5
6
7
8
from pyecharts import options as opts
from pyecharts.charts import Bar
# 准备数据
x_data = ['一月', '二月', '三月', '四月', '五月']
y_data = [10, 20, 15, 25, 30]
# 创建柱状图
bar_chart = Bar()
bar_chart.add_xaxis(x_data)
bar_chart.add_yaxis("销售额", y_data)
# 配置全局属性
bar_chart.set_global_opts(
title_opts=opts.TitleOpts(title="月度销售额柱状图", subtitle="副标题"),
xaxis_opts=opts.AxisOpts(name="月份"),
yaxis_opts=opts.AxisOpts(name="销售额(万元)"),
legend_opts=opts.LegendOpts(pos_left="center", pos_top="top"),
toolbox_opts=opts.ToolboxOpts(),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
)
# 渲染图表
bar_chart.render("global_options_bar_chart.html")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Python selenium库
Selenium是一个用于自动化Web浏览器操作的强大工具,广泛用于Web应用程序测试、网页数据抓取和任务自动化等场景。Selenium为各种编程语言提供了API,用作测试。目前官方的文档有C#, JavaScript, Java, Python, Ruby。 Selenium教程
# 安装Selenium和WebDriver
使用pip安装Selenium: pip install selenium,安装后,可以使用一下命令查看selenium版本信息: pip show selenium,也可以使用代码查看:
import selenium
print(selenium.__version__)
2
Selenium需要一个WebDriver与浏览器交互。不同的浏览器需要不同的WebDriver,如Chrome需要ChromeDriver,需要根据浏览器下载对应的WebDriver,病确保它在系统PATH中:
- Chrome: ChromeDriver
- Firefox: GeckoDriver
- Edge: EdgeDriver
- Safari: SafariDriver
from selenium import webdriver
# 使用Chrome浏览器
driver = webdriver.Chrome()
# 或者 Firefox
# driver = webdriver.Firefox()
# 或者 Edge
# driver = webdriver.Edge()
2
3
4
5
6
7
8
9
10
:::info 从Selenium 4开始,在浏览器驱动的管理方式上发生了变化: Selenium 4尝试自动检测系统中安装的浏览器版本,并下载相应的驱动程序,这意味着用户不再需要手动下载和设置驱动程序路径,除非需要特定版本的驱动程序。
不过,自动检测下载驱动需要不一样的网络环境,因此建议手动下载驱动,然后指定驱动路径
在Selenium 4中,不再直接在webdriver.Chrome中设置驱动程序路径,而是通过引入Service对象来设置,这样可以避免弃用警告,并确保驱动程序的正确加载。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
service = ChromeService(executable_path="PATH_TO_DRIVER")
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=service, options=options)
2
3
4
5
6
:::
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
# 设置正确的驱动路径
# service = ChromeService(executable_path="/home/sunyy/Tools/chrome-linux64")
# options = webdriver.ChromeOptions()
# driver = webdriver.Chrome(service=service, options=options)
# 上面的写法有问题,原因未知
driver = webdriver.Chrome()
# 打开一个网站
driver.get("https://www.baidu.com")
# 获取页面标题
print(driver.title)
# 关闭浏览器
driver.quit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 基本用法
- 初始化WebDriver: 选择浏览器并初始化WebDriver
- 使用get()方法打开网页
- 可以通过多种方式查找页面元素,例如使用ID,类名,标签名等
- Selenium可以模拟用户在浏览器中操作,如点击,输入文本等
- 获取元素属性和文本
# 获取元素的文本
element_text = search_box.text
# 获取元素的属性值
element_attribute = search_box.get_attribute("placeholder")
2
3
4
- 有时页面加载需要时间,可以使用显式等待或隐式等待来确保元素可操作
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 显式等待
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "kw"))
)
# 隐式等待
driver.implicitly_wait(10)
2
3
4
5
6
7
8
9
10
11
- 关闭浏览器:
driver.quit()
from selenium import webdriver
from selenium.webdriver.common.by import By
# 使用 Chrome 浏览器
driver = webdriver.Chrome()
# 打开网页
driver.get("https://www.baidu.com")
# 查找页面元素
# 通过 ID 查找元素
search_box = driver.find_element(By.ID, "kw")
# 通过标签名查找元素
links = driver.find_elements(By.TAG_NAME, "a")
# 在搜索框中输入文本
search_box.send_keys("Selenium Python")
# 通过类名查找元素
search_button = driver.find_element(By.CLASS_NAME, "s_btn")
# 点击搜索按钮
search_button.click()
driver.quit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 简单的网页自动化
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
try:
# 打开百度首页
driver.get("https://www.baidu.com")
# 查找搜索框元素
search_box = driver.find_element(By.ID, "kw")
# 输入搜索内容
search_box.send_keys("Selenium Python")
# 提交搜索表单
search_box.send_keys(Keys.RETURN)
# 等待搜索结果加载
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "content_left"))
)
# 打开页面标题
print("页面标题是: ", driver.title)
except Exception as e:
print(f"{str(e)}")
finally:
driver.quit()
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
# selenium常用方法
| 方法 | 说明 | 实例代码 |
|---|---|---|
webdriver.Chrome() | 初始化Chrome浏览器实例 | driver = webdriver.Chrome() |
driver.get(url) | 访问指定的URL地址 | driver.get("https://example.com") |
driver.find_element(By, value) | 查找第一个匹配元素 | element = driver.find_element(By.ID, "id") |
driver.find_elements(By, value) | 查找所有匹配的元素 | elements = driver.find_elements(By.CLASS_NAME, "class") |
element.click() | 点击元素 | element.click() |
element.send_keys(value) | 项输入框中发送键盘输入 | element.send_keys("text") |
element.text | 获取元素的文本内容 | text = element.text |
driver.back() | 浏览器后退 | driver.back() |
driver.forward() | 浏览器前进 | driver.forward() |
driver.refresh() | 刷新当前页面 | driver.refresh() |
driver.execute_script(script, *args) | 执行JavaScript脚本 | driver.execute_script("alert('Hello!')") |
driver.switch_to.frame(frame_reference) | 切换到指定的iframe | driver.switch_to.frame("frame_id") |
driver.switch_to.default_content() | 切换回主文档 | driver.switch_to.default_content() |
driver.quit() | 关闭浏览器并退出 | driver.quit() |
driver.close() | 关闭当前窗口 | driver.close() |
# Python 爬虫
Python爬虫(Web Scraping)是指通过编写Python程序从互联网上自动提取信息的过程。爬虫的基本流程通常包括发送HTTP请求获取网页内容、解析网页并提取数据,然后存储数据。Python的丰富生态使其成为开发爬虫的热门语言。爬虫的流程可以分为以下几个步骤:
- 发送HTTP请求: 爬虫通过HTTP请求从目标网站获取HTML页面,常用的库包括requests
- 解析HTML内容: 获取HTML页面后,爬虫需要解析内容并提取数据,常用的库有BeautifulSoup, lxml, Scrapy等
- 提取数据: 通过定位HTML元素(如标签、属性、类名等)来提取所需的数据
- 存储数据: 将提取的数据存储到数据库、CSV文件、JSON文件等,以便后续使用或分析。
BeautifulSoup是一个用于解析HTML和XML文档的Python库,能够从网页中提取数据,常用于网页抓取和数据挖掘。

# BeautifulSoup
BeautifulSoup是一个用于从网页中提取数据的Python库,特别适用于解析HTML和XML文件。其能够通过简单的API来提取和操作网页中的内容,非常适合用于网页抓取和数据提取的任务。
- 安装BeautifulSoup
使用BeautifulSoup需要安装beautifulsoup4和lxml或html.parser(一个HTML解析器)
pip install beautifulsoup4
pip install lxml # 推荐使用(速度更快),如果没有lxml,可以使用Python内置的html.parser作为解析器
2
- 基本用法
BeautifulSoup用于解析HTML或XML数据,并提供了一些方法来导航、搜索和修改解析树。BeautifulSoup常见的操作包括查找标签、获取标签属性、提取文本等。使用BeautifulSoup,需要先导入BeautifulSoup,并将HTML页面加载到BeautifulSoup对象中。
from bs4 import BeautifulSoup
import requests
# 指定想要获取标题的网站
url = "https://cn.bing.com/"
# 发送HTTP请求获取网页内容
response = requests.get(url)
# 中文乱码问题
response.encoding = "utf-8"
# 确保请求成功
if response.status_code == 200:
# 使用BeautifulSoup解析网页内容
soup = BeautifulSoup(response.text, "lxml")
# 查找<title>标签
title_tag = soup.find("title")
# 打印标题文本
if title_tag:
print(title_tag.get_text())
else:
print("未找到<title>标签")
else:
print("请求失败,状态码: ", response.status_code)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 中文乱码问题
使用requests库抓取中文网页时,可能会遇到编码问题,导致中文内容无法正确显示,为了确保能正确抓取并显示中文网页,通常需要处理网页的字符编码。自动检测编码requests通常会自动根据响应头中的Content-Type来推测网页的编码,但有时可能不准确,此时可以使用chardet来自动检测编码。
import requests
url = "https://cn.bing.com/"
response = requests.get(url)
# 使用chardet自动检测编码
import chardet
encoding = chardet.detect(response.content)['encoding']
print(encoding) # utf-8
response.encoding = encoding
# 如果知道网页的编码可以直接设置`reponse.encoding`
# response.encoding = "utf-8"
2
3
4
5
6
7
8
9
10
11
- 查找标签
BeautifulSoup提供了多种方法来查找网页中的标签,最常用的包括find()和find_all()
find(): 返回第一个匹配的标签find_all(): 返回所有匹配的标签
from bs4 import BeautifulSoup
import requests
# 指定想要获取标题的网站
url = "https://www.baidu.com/"
# 发送HTTP请求获取网页内容
response = requests.get(url)
# 中文乱码问题
response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "lxml")
# 查找第一个<a>标签
first_link = soup.find("a")
print(first_link)
print("--------------------------------")
# 获取第一个<a>标签的href属性
print(first_link.attrs)
first_link_url = first_link.get("href")
print(first_link_url)
print("--------------------------------")
# 查找所有<a>标签
all_links = soup.find_all("a")
print(all_links)
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

- 获取标签的文本:
get_text()提取标签中的文本内容
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com/"
response = requests.get(url)
response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "lxml")
paragraph_text = soup.find("p").get_text()
all_text = soup.get_text()
print(all_text)
2
3
4
5
6
7
8
9
10

- 查找子标签和父标签: 可以通过
parent和children属性访问的父标签和子标签
# 获取当前标签的父标签
parent_tag = first_link.parent
# 获取当前标签的所有子标签
children = first_link.children
2
3
4
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com/"
response = requests.get(url)
response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "lxml")
first_link = soup.find("a")
print(first_link)
print("----------------------------")
parent_tag = first_link.parent
print(parent_tag.get_text())
2
3
4
5
6
7
8
9
10
11
12
13
- 查找具有特定属性的标签: 可以通过传递属性来查找具有特定属性的标签,如:
# 查找所有 class="example-class" 的 <div> 标签
divs_with_class = soup.find_all("div", class_="example-class")
# 查找具有 id="unique-id" 的 <p> 标签
unique_paragraph = soup.find("p", id="unique-id")
2
3
4
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com/"
response = requests.get(url)
response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "lxml")
unique_input = soup.find("input", id="su")
input_value = unique_input["value"]
print(input_value)
2
3
4
5
6
7
8
9
10
11
# 高级用法
- CSS选择器: BeautifulSoup也支持通过CSS选择器查找标签,
select()方法允许使用类似jQuery的选择器语法来查找标签
# 使用CSS选择器查找所有class为"example"的<div>标签
example_divs = soup.select("div.example")
# 查找所有<a>标签中的href属性
links = soup.select("a[href]")
2
3
4
- 处理嵌套标签: BeautifulSoup支持深度嵌套的HTML结构,可以通过递归查找子标签来处理这些结构:
# 查找嵌套的<div>标签
nested_divs = soup.find_all("div", class_="nested")
for div in nested_divs:
print(div.get_text())
2
3
4
- 修改网页内容: BeautifulSoup允许修改HTML内容,可以修改标签的属性,文本或删除标签
# 修改第一个<a>标签的href属性
first_link["href"] = "http://new-url.com"
# 修改第一个<p>标签的文本内容
first_paragraph = soup.find("p")
first_paragraph.string = "Updated content"
# 删除某个标签
first_paragraph.decompose()
2
3
4
5
6
7
- 转换为字符串: 可以解析BeautifulSoup对象转换回HTML字符串
# 转换为字符串
html_str = str(soup)
2
# BeautifulSoup属性与方法
- 常用属性和方法
| 方法/属性 | 描述 | 示例 |
|---|---|---|
BeautifulSoup() | 用于解析HTML或XML文档并返回一个BeautifulSoup对象 | soup = BeautifulSoup(html_doc, 'html_parser') |
.prettify() | 格式化并美化文档内容,生成结构化的字符串 | print(soup.prettify()) |
.find() | 查找第一个匹配的标签 | tag = soup.find("a") |
.find_all() | 查找所有匹配的标签,返回一个列表 | tags = soup.find_all("a") |
.find_all_next() | 查找当前标签后所有符合条件的标签 | tags = soup.find("div").find_all_text("p") |
.find_all_previous() | 查找当前标签前所有符合条件的标签 | tags = soup.find("div").find_all_previous("p") |
.find_parent() | 返回当前标签的父标签 | parent = tag.find_parent() |
.find_all_parents() | 查找当前标签的所有父标签 | parents = tag.find_all_parents() |
.find_next_sibling() | 查找当前标签的下一个兄弟标签 | next_sibling = tag.find_next_sibling() |
.find_previous_sibling() | 查找当前标签的前一个兄弟标签 | prev_sibling = tag.find_previous_sibling() |
.parent | 获取当前标签的父标签 | parent = tag.parent |
.next_sibling | 获取当前标签的下一个兄弟标签 | next_sibling = tag.next_sibling |
.previous_sibling | 获取当前标签的前一个兄弟标签 | prev_sibling = tag.previous_sibling |
.get_text() | 提取标签内的文本内容,忽略所有HTML标签 | text = tag.get_text() |
.attrs | 返回标签的所有属性,以字典形式表示 | href = tag.attrs["href"] |
.string | 获取标签内的字符串内容 | string_content = tag.string |
.name | 返回标签的名称 | tag_name = tag.name |
.contents | 返回标签的所有子元素,以列表形式返回 | children = tag.contents |
.descendants | 返回标签的所有后代元素,生成器形式 | for child in tag.descendants: print(child) |
.parent | 获取当前标签的父标签 | parent = tag.parent |
.previous_element | 获取当前标签的前一个元素(不包括文本) | prev_elem = tag.previous_element |
.next_element | 获取当前标签的下一个元素(不包括文本) | next_elem = tag.next_element |
.decompose() | 从树中删除当前标签及其内容 | tag.decompose() |
.unwrap() | 移除标签本身,只保留其子内容 | tag.unwrap() |
.insert() | 向标签内插入新标签新标签或文本 | tag.insert(0, new_tag) |
.insert_before() | 在当前标签前插入新标签 | tag.insert_before(new_tag) |
.insert_after() | 在当前标签后插入新标签 | tag.insert_after(new_tag) |
.extract() | 删除标签并返回该标签 | extracted_tag = tag.extract() |
.replace_with() | 替换当前标签及其内容 | tag.replace_with(new_tag) |
.has_attr() | 检查标签是否有指定的属性 | if tag.has_attr("href") |
.get() | 获取指定属性的值 | href = tag.get("href") |
.clear() | 清空标签的所有内容 | tag.clear() |
.encode() | 编码标签内容为字节流 | encoded = tag.encode() |
.is_empty_element | 检查标签是否是空元素(例如<br>, <img>等) | if tag.is_empty_element: |
.is_ancestor_of() | 检查当前标签是否是指定标签的祖先元素 | if tag.is_ancestor_of(another_tag): |
.is_descendant_of() | 检查当前标签是否是指定标签的后代元素 | if tag.is_descendant_of(another_tag): |
- 其他属性
| 方法/属性 | 描述 | 示例 |
|---|---|---|
.style | 获取标签的内联样式 | style = tag["style"] |
.id | 获取标签的id属性 | id = tag["id"] |
.class_ | 获取标签的class属性 | class_name = tag["class"] |
.string | 获取标签内部的字符串内容,忽略其他标签 | content = tag.string |
.parent | 获取标签的父元素 | parent = tag.parent |
- 其他
| 方法/属性 | 描述 | 示例 |
|---|---|---|
find_all(string) | 使用字符串查找匹配的标签 | tag = soup.find_all("div", class_="container") |
find_all(id) | 查找指定id的标签 | tag = soup.find_all(id="main") |
find_all(attrs) | 查找具有指定属性的标签 | tag = soup.find_all(attrs={"href": "http://example.com"}) |
# Python Scrapy库
Scrapy是一个功能强大的Python爬虫框架,专门用于抓取网页数据并提取信息,常被用于数据挖掘、信息处理或存储历史数据等应用。其内置了许多有用的功能,如处理请求、跟踪状态、处理错误、处理请求频率限制等,非常适合进行高效、分布式的网页爬取。与简单的爬虫库(requests和BeautifulSoup)不同,Scrapy是一个全功能的爬虫框架,具有高度的可扩展性和灵活性,使用于复杂和大规模的网页抓取任务。
Scrapy架构图(绿线是数据流向):

Scrapy的工作基于以下几个核心组件:
Spider: 爬虫类,用于定义如何从网页中提取数据一级如何跟踪网页的链接Item: 用来定义和存储抓取的数据,相当于数据模型Pipeline: 用于处理抓取到的数据,常用于清洗、存储数据等操作Middleware: 用来处理请求和响应,可以用于设置代理、处理cookies、用户代理等Settings: 用来配置Scrapy的各项设置,如请求延迟、并发请求数等
- 安装Scrapy:
pip install scrapy - Scrapy项目结构
Scrapy项目是一个结构化的目录,其中包含多个文件夹和模块,旨在帮助组织爬虫代码。Scrapy使用命令行工具创建和管理爬虫项目,可以使用如下命令创建一个新的Scrapy爬虫项目: scrapy startproject scrapy_test
这将创建一个名为scrapy_test的项目,项目结构大致如下:
scrapy_test/
scrapy.cfg # 项目的配置文件
scrapy_test/ # 项目源代码文件夹
__init__.py
items.py # 定义抓取的数据结构
middlewares.py # 定义中间件
pipelines.py # 定义数据处理管道
settings.py # 项目的设置文件
spiders/ # 存放爬虫代码的文件夹
__init__.py
... # 自定义的爬虫代码
2
3
4
5
6
7
8
9
10
11
# 编写一个简单的Scrapy爬虫
下面是一个简单的爬虫示例,展示如何从网页中抓取数据。创建一个爬虫项目: scrapy startproject scrapy_test
进入scrapy_test(项目源代码文件夹)目录,通过scrapy genspider命令创建一个爬虫: scrapy genspider douban_spider movie.douban.com

import scrapy
class DoubanSpiderSpider(scrapy.Spider):
name = "douban_spider"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com"]
def parse(self, response):
pass
2
3
4
5
6
7
8
9
10
11
代码说明:
name: 定义爬虫的名称,必须是唯一的allowed_domains: 限制爬虫的访问域名,防止爬虫爬取其他域名的网页start_urls: 定义爬虫的起始页面,爬虫将从这些页面开始抓取parse: parse方法是每个爬虫的核心,用于处理响应并提取数据,它接受一个response对象,表示服务器返回的页面内容
编写爬虫代码,需要注意:
- 豆瓣等网站可能会检测爬虫行为,建议设置USER_AGENT和DOWNLOAD_DELAY来模拟正常用户行为
- 在爬取数据时,请遵守目标网站的robots.txt文件规定,避免对服务器造成过大压力
- 如果频繁爬取,可能会触发IP封禁
修改settings.py配置: 在settings.py中添加如下配置,以模拟浏览器请求并绕过反爬虫机制
# 设置 User-Agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
# 不遵守 robots.txt 规则
ROBOTSTXT_OBEY = False
# 设置下载延迟,避免过快请求
DOWNLOAD_DELAY = 2
# 启用自动限速扩展
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2
AUTOTHROTTLE_MAX_DELAY = 5
2
3
4
5
6
7
8
9
10
11
12
13
- 在爬虫代码中,添加自定义请求头(如User-Agent何Referer),进一步模拟浏览器行为,打开douban_spider.py
from collections.abc import Iterable
from scrapy.http.request import Request
from scrapy.http.response import Response
import scrapy
class DoubanSpiderSpider(scrapy.Spider):
name = "douban_spider"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com/top250"]
def start_requests(self) -> Iterable[Request]:
headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Referer": "https://movie.douban.com/",
}
for url in self.start_urls:
yield Request(url, headers=headers, callback=self.parse)
# return super().start_requests()
def parse(self, response: Response):
for movie in response.css("div.item"):
yield {
"title": movie.css("span.title::text").get(),
"rating": movie.css("span.rating_num::text").get(),
"quote": movie.css("span.inq::text").get(),
}
# 处理分页
next_page = response.css("span.next a::attr(href)").get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
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
代码解析:
name = "douban_spider": 定义爬虫名称start_urls: 定义爬虫开始爬取的初始URL(豆瓣电影Top250页面)parse:- 使用CSS选择器提取每部电影的标题、评分和简介
span.title::text: 提取电影标题span.rating_num::text: 提取电影评分span.inq::text: 提取电影简介
分页处理:- 使用
span.next a::attr(href)提取下一页的链接 - 如果存在下一页,使用
response.follow继续爬取
- 使用
在命令行中使用如下命令启动爬虫: scrapy crawl douban_spider -o douban_movies.csv,这将启动爬虫并将提取的数据保存到douban_movies.csv文件中。
# 常用方法
- 爬虫方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
start_requests() | 生成初始请求,可以自定义请求头、请求方法等 | yield scrapy.Request(url, callback=self.parse) |
parse(response) | 处理响应并提取数据,是爬虫的核心方法 | yield {"title": response.css("h1::text").get()} |
follow(url, callback) | 自动处理相对URL并生成新的请求,用于分页或链接跳转 | yield response.follow(next_page, callback=self.parse) |
closed(reason) | 爬虫关闭时调用,用于清理资源或记录日志 | def closed(self, reason): print("Spider closed:", reason) |
log(message) | 记录日志信息 | self.log("This is a log message") |
- 数据提取方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
response.css(selector) | 使用CSS选择器提取数据 | title = response.css("h1::text").get() |
response.xpath(selector) | 使用XPath选择器提取数据 | title = response.xpath("//h1/text()").get() |
get() | 从SelectorList中提取第一个匹配的结果(字符串) | title = response.css("h1::text").get() |
getall() | 从SelectorList中提取所有匹配的结果(列表) | titles = response.css("h1::text").getall() |
attrib | 提取当前节点的属性 | link = response.css("a::attr(href)").get() |
- 请求与响应方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
scrapy.Request(url, callback, method, headers, meta) | 创建一个新的请求 | yield scrapy.Request(url, callback=self.parse, headers=headers) |
response.url | 获取当前响应的URL | current_url = response.url |
response.status | 获取响应的状态码 | if response.status == 200: print("Success") |
response.meta | 获取请求中传递的额外数据 | value = response.meta.get("key") |
response.headers | 获取响应的头信息 | content_type = response.headers.get("Content-Type") |
- 中间件与管道方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
process_request(request, spider) | 在请求发送前处理请求(下载器中间件) | request.headers["User-Agent"] = "Mozilla/5.0 |
process_response(request, response, spider) | 在响应返回后处理响应(下载器中间件) | if response.status == 403: return request.replace(dont_filter=True) |
process_item(item, spider) | 处理提取的数据(管道) | if item["price"] < 0: raise DropItem("Invalid price") |
open_spider(spider) | 爬虫启动时调用(管道) | def open_spider(self, spider): self.file = open("items.json", "w") |
close_spider(spider) | 爬虫关闭时调用(管道) | def close_spider(self, spider): self.file.close() |
- 工具与扩展方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
scrapy shell | 启动交互式Shell, 用于调试和测试选择器 | scrapy shell "http://example.com" |
scrapy crawl <spider_name> | 运行指定的爬虫 | scrapy crawl myspider -o output.json |
scrapy check | 检查爬虫代码的正确性 | scrapy check |
scrapy fetch | 下载指定URL的内容 | scrapy fetch "http://example.com" |
scrapy view | 在浏览器中查看Scrapy下载的页面 | scrapy view "http://example.com" |
- 常用设置(settings.py)
| 设置项 | 作用描述 | 示例 |
|---|---|---|
USER_AGENT | 设置请求头中的User-Agent | USER_AGENT = "Mozilla/5.0 |
ROBOTSTXT_OBEY | 是否遵守robots.txt规则 | ROBOTSTXT_OBEY = False |
DOWNLOAD_DELAY | 设置下载延迟,避免过快请求 | DOWNLOAD_DELAY = 2 |
CONCURRENT_REQUESTS | 设置并发请求数 | CONCURRENT_REQUESTS = 16 |
ITEM_PIPELINES | 启用管道 | ITEM_PIPELINES = {"myproject.pipelines.MyPipeline": 300} |
AUTOTHROTTLE_ENABLED | 启用自动限速扩展 | AUTOTHROTTLE_ENABLED = True |
- 其他常用方法
| 方法名 | 作用描述 | 示例 |
|---|---|---|
response.follow_all(links, callback) | 批量处理链接并生成请求 | yield from response.follow_all(links, callback=self.parse) |
response.json() | 将响应内容解析为JSON格式 | data = response.json() |
response.text | 获取响应的文本内容 | html = response.text |
response.selector | 获取响应内容的Selector对象 | title = response.selector.css("h1::text").get() |
以上表格列出了Scrapy中常用的方法及其作用。这些方法涵盖了爬虫开发的各个方面,包括请求生成、数据提取、中间件处理、管道操作等。
# 方法使用举例
start_requests(): Scrapy爬虫的入口点,用于生成初始请求,通常在这个方法中定义爬虫的起始URL
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
def start_requests(self):
urls = [
'http://example.com/page1',
'http://example.com/page2',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
2
3
4
5
6
7
8
9
10
11
12
parse(): 是默认的响应处理方法,用于解析响应并提取数据或生成新的请求
def parse(self, response):
# 提取页面标题
title = response.css('title::text').get()
yield {
'title': title
}
2
3
4
5
6
parse_item(): 用于解析单个项目(Item)的响应,通常用于提取结构化数据
def parse_item(self, response):
item = {}
item['name'] = response.css('div.name::text').get()
item['price'] = response.css('div.price::text').get()
yield item
2
3
4
5
follow(): 用于生成新的请求并自动处理响应,通常用于跟踪链接
def parse(self, response):
for link in response.css('a::attr(href)'):
yield response.follow(link, self.parse_item)
2
3
yield: 用于生成请求或项目(Item),并将其传递给Scrapy引擎进行处理
def parse(self, response):
yield {
'title': response.css('title::text').get()
}
2
3
4
Item: 用于定义数据结构,通常用于存储从网页中提取的数据
import scrapy
class MyItem(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
2
3
4
5
ItemLoader: 用于加载和填充Item对象,简化数据提取和处理的流程
from scrapy.loader import ItemLoader
from myproject.items import MyItem
def parse(self, response):
loader = ItemLoader(item=MyItem(), response=response)
loader.add_css('name', 'div.name::text')
loader.add_css('price', 'div.price::text')
yield loader.load_item()
2
3
4
5
6
7
8
Request: 用于生成HTTP请求对象,通常用于定义请求的URL,回调方法等
import scrapy
def parse(self, response):
yield scrapy.Request(url='http://example.com/page3', callback=self.parse_item)
2
3
4
Response: 表示HTTP响应对象,包含从服务器返回的HTML内容、状态码等信息
def parse(self, response):
print(response.status) # 打印响应状态码
print(response.body) # 打印响应内容
2
3
Selector: 用于从HTML或XML文档中提取数据,支持XPath和CSS选择器
def parse(self, response):
title = response.xpath('//title/text()').get()
yield {
'title': title
}
2
3
4
5
CrawlSpider: 一种特殊的Spider类,用于处理复杂的爬取规则和链接跟踪
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class MyCrawlSpider(CrawlSpider):
name = 'mycrawlspider'
allowed_domains = ['example.com']
start_urls = ['http://example.com']
rules = (
Rule(LinkExtractor(allow=('page/\d+',)), callback='parse_item'),
)
def parse_item(self, response):
yield {
'title': response.css('title::text').get()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LinkExtractor: 用于从响应中提取链接,通常用于自动跟踪页面中的链接
from scrapy.linkextractors import LinkExtractor
def parse(self, response):
extractor = LinkExtractor(allow=('page/\d+',))
links = extractor.extract_links(response)
for link in links:
yield scrapy.Request(link.url, callback=self.parse_item)
2
3
4
5
6
7
Pipeline: 用于处理爬取到的数据,通常用于数据清洗、存储等操作
class MyPipeline:
def process_item(self, item, spider):
# 处理 item 数据
return item
2
3
4
Middleware: 用于处理请求和响应的中间件,通常用于修改请求头、处理异常等
class MyMiddleware:
def process_request(self, request, spider):
# 修改请求头
request.headers['User-Agent'] = 'MyCustomUserAgent'
return None
2
3
4
5
# Python Markdown
Markdown是一种轻量级的标记语言,它允许使用易读易写的纯文本格式编写文档。Markdown的语法简单直观,常用于编写博客、文档、README文件等。Python可以使用markdown模块将Markdown文本转换为HTML。
# 将Markdown转换为HTML的步骤
- 安装markdown:
pip install markdown - 编写Python脚本
import markdown
# 定义Markdown文本
md_text = """
# 这是标题
这是 **加粗** 的文本
这是 *斜体* 的文本
- 列表项 1
- 列表项 2
[点击这里](https://www.runoob.com)访问网站
"""
# 转换为 HTML
html_output = markdown.markdown(md_text)
# 输出 HTML
print(html_output)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

也可以编写一个简单的Python脚本将Markdown文件转换为HTML文件:
import markdown
# 读取 Markdown 文件
with open('example.md', 'r', encoding='utf-8') as file:
markdown_text = file.read()
# 将 Markdown 转换为 HTML
html = markdown.markdown(markdown_text)
# 将 HTML 写入文件
with open('example.html', 'w', encoding='utf-8') as file:
file.write(html)
print("Markdown 文件已成功转换为 HTML 文件!")
2
3
4
5
6
7
8
9
10
11
12
13
14
- 运行脚本:
python convert_markdown_to_html.py
# 扩展功能
markdown库支持多种扩展,如表格、代码高亮等,可以通过如下方式启用扩展:
html = markdown.markdown(markdown_text, extensions["tables", "fenced_code"])
# 参考资源
# Python sys模块
sys 是 Python 标准库中的一个模块,提供了与Python解释器及其环境交互的功能。通过 sys 库,可以访问与Python解释器相关的变量和函数,例如命令行参数,标准输入输出,程序退出等。
- 导入sys库:
import sys
# sys 库的常用功能
- 命令行参数:
sys.argv是一个包含命令行参数的列表,sys.argv[0]是脚本的名称,后续元素是传递给脚本的参数
import sys
print("脚本名称: ", sys.argv[0])
print("参数列表: ", sys.argv[1:])
2
3
4
python script.py arg1 arg2
# 脚本名称: script.py
# 参数列表: ["arg1", "arg2"]
2
3
- 程序退出:
sys.exit()用于退出程序,可以传递一个整数作为退出状态码,通常0表示成功,非零值表示错误。
import sys
print("程序开始")
sys.exit(0)
print("这行代码不会执行")
2
3
4
5
- 标准输入输出:
sys.stdin,sys.stdout和sys.stderr分别代表标准输入、输出和错误流。可以重定向这些流以实现自定义的输入输出行为
import sys
# 重定向标准输出到文件
with open('output.txt', 'w') as f:
sys.stdout = f
print("这行内容将写入 output.txt")
# 恢复标准输出
sys.stdout = sys.__stdout__
print("这行内容将显示在控制台")
2
3
4
5
6
7
8
9
10
- Python 版本信息:
sys.version和sys.version_info提供了当前Python解释器的版本信息
import sys
print("Python 版本:", sys.version)
print("版本信息:", sys.version_info)
2
3
4
- 模块搜索路径:
sys.path是一个列表,包含了Python解释器在导入模块时搜索的路径,可以修改这个列表来添加自定义的模块搜索路径
import sys
print("模块搜索路径:", sys.path)
sys.path.append('/custom/path')
print("更新后的模块搜索路径:", sys.path)
2
3
4
5
# sys 模块常用属性
| 属性 | 说明 |
|---|---|
sys.argv | 命令行参数列表,sys.argv[0]是脚本名称 |
sys.path | Python模块搜索路径(PYTHONPATH) |
sys.modules | 已加载模块的字典 |
sys.platform | 操作系统平台标识(如"win32", "linux", "darwin") |
sys.version | Python解释器版本信息 |
sys.executable | Python解释器的绝对路径 |
sys.stdin | 标准输入流(文件对象) |
sys.stdout | 标准输出流(文件对象) |
sys.stderr | 标准错误流(文件对象) |
sys.byteorder | 字节序("little"或"big") |
sys.maxsize | 最大整数值(2**31-1或2**63-1) |
# sys 模块常用方法
| 方法 | 说明 |
|---|---|
sys.exit([status]) | 退出程序,status=0表示正常退出 |
sys.getsizeof(obj) | 返回对象占用的字节数 |
sys.getdefaultencoding() | 获取默认字符串编码(通常"utf-8") |
sys.setrecursionlimit(limit) | 设置递归深度限制(默认1000) |
sys.getrecursionlimit() | 获取当前递归深度限制 |
sys.getrefcount(obj) | 返回对象的引用计数 |
sys.exc_info() | 获取当前异常信息((type, value, traceback)) |
sys.settrace(tracefunc) | 设置调试跟踪函数 |
sys.setprofile(profilefunc) | 设置性能分析函数 |
# Python pikle模块
Python的pickle模块是一个用于序列化和反序列化Python对象的标准库模块。序列化是指将Python对象转换为字节流的过程,而反序列化则是将字节流转换回Python对象的过程。pickle模块可以将几乎所有的Python对象(如列表、字典、类实例等)保存到文件中,或者通过网络传输,然后在需要时重新加载。
为什么使用Pickle模块*?
- 数据持久化: 将Python对象保存到文件中,以便在程序关闭后仍然可以访问这些数据。
- 数据传输: 通过网络传输Python对象,例如在分布式系统中传递数据。
- 快速存储和加载: pickle模块可以高效地处理复杂的数据结构,适合需要快速存储和加载的场景。
# Pickle模块的基本用法
- 序列化对象
使用pickle.dump()方法可以将Python对象序列化并保存到文件中。
import pickle
# 创建一个 Python 对象
data = {
"name": "Alice",
"age": 25,
"hobbies": ["reading", "traveling"]
}
# 将对象序列化并保存到文件
with open("data.pkl", "wb") as file:
pickle.dump(data, file)
2
3
4
5
6
7
8
9
10
11
12
wb: 表示以二进制写模式打开文件pickle.dump(): 将data对象序列化并写入文件。
- 反序列化对象
使用pickle.load()方法可以从文件中加载并序列化Python对象
import pickle
# 从文件中加载并反序列化对象
with open("data.pkl", "rb") as file:
loaded_data = pickle.load(file)
print(loaded_data)
2
3
4
5
6
7
rb: 表示以二进制读模式打开文件pickle.load(): 从文件中读取字节流并反序列化为Python对象
# Pickle模块的注意事项
- 安全性: pickle模块在反序列化时会执行任意代码,因此不要加载来自不可信来源的pickle数据,以免遭受恶意攻击。
- 兼容性: pickle生成的字节流是Python特有的,不同版本的Python之间可能存在兼容性问题。
- 性能: 对于大型数据集,pickle的序列化和反序列化可能会比较慢,可以考虑实用更高效的序列化工具,如json或msgpack
# 高级用法: 自定义对象的序列化
pickle模块支持自定义类的序列化,默认,pickle会保存对象的属性和类名。如果需要更复杂的序列化逻辑,可以在类中实现__getstate__()和__setstate__()方法。
import pickle
class Person:
def __init__(self, name, age) -> None:
self.name = name
self.age = age
def __getstate__(self) -> object:
# 自定义序列化罗技
return {"name": self.name, "age": self.age}
def __setstate__(self, state):
# 自定义反序列化逻辑
self.name = state["name"]
self.age = state["age"]
# 创建对象并序列化
person = Person("Bob", 30)
with open("person.pkl", "wb") as file:
pickle.dump(person, file)
# 反序列化对象
with open("person.pkl", "rb") as file:
loaded_person = pickle.load(file)
print(loaded_person.name, loaded_person.age)
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
# pickle模块常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
pickle.dump(obj, file) | 将对象序列化并写入文件 | pickle.dump(data, open("data.pkl", "wb")) |
pickle.load(file) | 从文件读取并反序列化对象 | data = pickle.load(open("data.pkl", "rb")) |
pickle.dumps(obj) | 将对象序列化为字符串 | bytes_data = pickle.dumps([1, 2, 3]) |
pickle.loads(bytes) | 从字节串反序列化对象 | lst = pickle.loads(bytes_data) |
pickle.HIGHEST_PROTOCOL | 可用的最高协议版本(属性) | pickle.dump(..., protocol=pickle.HIGHEST_PROTOCOL) |
pickle.DEFAULT_PROTOCOL | 默认协议版本(属性,通常为4) | pickle.pickle.DEFAULT_PROTOCOL |
- 序列化对象到文件
import pickle
data = {'name': 'Alice', 'age': 25}
with open('data.pkl', 'wb') as f:
pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
2
3
4
- 从文件反序列化
with open('data.pkl', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data) # 输出: {'name': 'Alice', 'age': 25}
2
3
- 序列化为字节串(网络传输/缓存)
bytes_data = pickle.dumps([1, 2, 3], protocol=4)
restored_list = pickle.loads(bytes_data)
2
# pickle模块协议版本
| 协议版本 | 说明 |
|---|---|
| 0 | 人类可读的ASCII格式(兼容旧版) |
| 1 | 二进制格式(兼容旧版) |
| 2 | Python2.3+优化支持类对象 |
| 3 | Python3.0+默认协议(不支持Python2) |
| 4 | Python3.4+支持更大对象和更多数据类型 |
| 5 | Python3.8+支持内存优化和数据共享 |
# Python subprocess模块
subprocess是Python标准库中的一个模块,用于创建和管理子进程。其允许在Python程序中执行外部命令,并与这些命令进行交互。通过subprocess模块,可以执行系统命令、调用其他程序,并获取它们的输出或错误信息。
为什么使用subprocess模块?
在Python中,有时需要执行一些系统命令或调用其他程序来完成特定的任务,如,可能需要运行一个shell命令、启动一个外部应用程序,或者与一个命令行工具进行交互。subprocess模块提供了一种安全且灵活的方式来处理这些需求。与早起的os.system()或os.popen()相比,subprocess模块提供了更强大的功能和更好的控制能力,它允许更精细地管理子进程的输入、输出和错误流,并且可以处理更复杂的场景。
# subprocess模块的核心功能
- 执行外部命令:
subprocess.run()是subprocess模块中最常用的函数之一,它可以执行一个外部命令,并等待命令完成。
import subprocess
# 执行一个简单的 shell 命令
result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
# 打印命令的输出
print(result.stdout)
2
3
4
5
6
7
- 处理输入和输出: subprocess模块允许控制子进程的输入、输出和错误流,可以将数据传递给子进程的标准输入,或者从子进程的标准输出和标准错误中读取数据,如:
import subprocess
# 执行一个命令,并将输入传递给子进程
result = subprocess.run(["grep", "python"], input="hello\npython\nworld", capture_output=True, text=True)
# 打印命令的输出
print(result.stdout)
2
3
4
5
6
7
- 处理错误: subprocess模块还允许处理子进程的错误,如果子进程返回非零的退出状态码,
subprocess.run()会抛出一个CalledProcessError异常,可以通过检查result.returncode来获取子进程的退出状态码。
import subprocess
try:
result = subprocess.run(["ls", "nonexistent_file"], capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Command failed with return code {e.returncode}")
print(f"Error output: {e.stderr}")
2
3
4
5
6
7
# subprocess模块的高级用法
- 使用Popen类:
subprocess.Popen类提供了更底层的接口,允许灵活地控制子进程,可以使用Popen启动一个子进程,并在后台运行,或者与它交互。
import subprocess
# 启动一个子进程
process = subprocess.Popen(["ping", "baidu.com"], stdout=subprocess.PIPE, text=True)
# 读取子进程的输出
while True:
output = process.stdout.readline()
if output == "" and process.poll() is not None:
break
if output:
print(output.strip())
# 获取子进程的退出状态码
return_code = process.poll()
print(f"Process finished with return code {return_code}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 使用管道: subprocess模块允许使用管道将多个命令连接在一起,可以将一个命令的输出作为另一个命令的输入.
import subprocess
# 使用管道连接两个命令
p1 = subprocess.Popen(["ls", "-l"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "py"], stdin=p1.stdout, stdout=subprocess.PIPE, text=True)
# 获取最终输出
output = p2.communicate()[0]
print(output)
2
3
4
5
6
7
8
9
# subprocess模块的常用方法、类和参数
subprocess模块核心方法:
| 方法 | 说明 | 示例 |
|---|---|---|
subprocess.run() | 执行命令并等待完成(推荐) | subprocess.run(["ls", "-l"], capture_output=True, text=True) |
subprocess.Popen() | 创建子进程(底层控制) | proc = subprocess.Popen(["ping", "baidu.com"], stdout=subprocess.PIPE) |
subprocess.call() | 执行命令并返回退出码(旧版) | exit_code = subprocess.call(["python", "--version"]) |
subprocess.check_call() | 执行命令,失败时抛出异常 | subprocess.check_call(["git", "commit"]) |
subprocess.check_output() | 执行命令并返回输出(旧版) | output = subprocess.check_output(["date"], text=True) |
subprocess.CompletedProcess对象属性(run()方法的返回对象):
| 方法 | 说明 |
|---|---|
args | 执行的命令参数列表 |
returncode | 进程退出状态码 |
stdout | 标准输出内容(若设置了capture_output) |
stderr | 标准错误内容(若设置了capture_output) |
subprocess.Popen类常用方法/属性:
| 方法/属性 | 说明 | 示例 |
|---|---|---|
poll() | 检查进程是否终止(返回None表示运行中) | if proc.poll() is None: print("Running") |
wait() | 阻塞等待进程结束 | proc.wait() |
communicate() | 交互式输入/输出 | stdout, stderr = proc.communicate(input="data") |
terminate() | 发送终止信号(SIGTERM) | proc.terminate() |
kill() | 强制终止进程(SIGKILL) | proc.kill() |
stdin | 进程的标准输入流 | proc.stdin.write("input") |
stdout | 进程的标准输出流 | proc.stdout.read() |
stderr | 进程的标准错误流 | errors = proc.stderr.read() |
常用参数说明(适用于run()和Popen()):
| 参数 | 说明 | 示例值 |
|---|---|---|
args | 命令(列表或字符串) | ["ls", "-l"]或ls -l |
stdin | 标准输入配置 | subprocess.PIPE(管道),None(继承) |
stdout | 标准输出配置 | subprocess.PIPE, open("log.txt", "w") |
stderr | 标准错误配置 | subprocess.STDOUT(合并到stdout) |
shell | 是否通过shell执行 | True(支持字符串命令) |
cwd | 工作目录路径 | /tmp |
env | 自定义环境变量 | {"PATH": "/usr/bin"} |
timeout | 超时时间(秒) | 30 |
text | 输入/输出是否为字符串(非字节) | True |
# Python queue模块
在Python中,queue模块提供了一个线程安全的队列实现,用于在多线程编程中安全地传递数据。队列是一种先进先出(FIFO)的数据结构,queue模块提供了多种队列类型,包括Queue, LifoQueue和PriorityQueue。
# 队列类型
- Queue: 最常用的队列类型,实现了标准的先进先出(FIFO)队列,以下是Queue的基本用法:
import queue
# 创建一个队列
q = queue.Queue()
# 向队列中添加元素
q.put(1)
q.put(2)
q.put(3)
# 从队列中获取元素
print(q.get()) # 输出: 1
print(q.get()) # 输出: 2
print(q.get()) # 输出: 3
2
3
4
5
6
7
8
9
10
11
12
13
14
- LifoQueue: 一种后进先出(LIFO)的队列,类似栈,以下是LifoQueue的基本用法:
import queue
# 创建一个 LIFO 队列
q = queue.LifoQueue()
# 向队列中添加元素
q.put(1)
q.put(2)
q.put(3)
# 从队列中获取元素
print(q.get()) # 输出: 3
print(q.get()) # 输出: 2
print(q.get()) # 输出: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
- PriorityQueue: 一种优先级队列,元素按照优先级顺序被取出,如:
import queue
# 创建一个优先级队列
q = queue.PriorityQueue()
# 想队列中添加元素, 元素为元组(优先级, 数据)
q.put((3, 'Low priority'))
q.put((1, 'High priority'))
q.put((2, 'Medium priority'))
# 从队列中获取元素
print(q.get()) # 输出: (1, 'High priority')
print(q.get()) # 输出: (2, 'Medium priority')
print(q.get()) # 输出: (3, 'Low priority')
2
3
4
5
6
7
8
9
10
11
12
13
14
# 常用方法
put(item, block=True, timeout=None): 将item放入队列,如果block为True且队列已满,则等待timeout秒,直到队列有空闲空间。如果timeout为None,则无限等待。get(block=True, timeout=None): 从队列中获取并移除一个元素,如果block为True且队列为空,则等待timeout秒,直到队列中有元素。如果timeout为None,则无限等待。qsize(): 返回队列中元素数量empty(): 如果队列为空,返回True,否则返回Falsefull(): 如果队列已满,返回True,否则返回False
# 线程安全
queue模块的所有队列类型都是线程安全的,多个线程可以安全地同时操作同一个队列,不需要额外的同步机制。示例:
import queue
import threading
import time
# 创建一个队列
q = queue.Queue()
# 生产者线程
def producer():
for i in range(5):
print(f'生产 {i}')
q.put(i)
time.sleep(1)
# 消费者线程
def consumer():
while True:
item = q.get()
if item is None:
break
print(f'消费 {item}')
q.task_done()
# 启动生产者线程
producer_thread = threading.Thread(target=producer)
producer_thread.start()
# 启动消费者线程
consumer_thread = threading.Thread(target=consumer)
consumer_thread.start()
# 等待生产者线程完成
producer_thread.join()
# 等待队列中所有任务完成
q.join()
# 发送结束信号
q.put(None)
consumer_thread.join()
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
# 常用的属性和方法
- queue 模块核心类:
| 类 | 说明 | 适用场景 |
|---|---|---|
queue.Queue | 先进先出(FIFO)队列 | 通用任务队列 |
queue.LifoQueue | 后进先出(LIFO)队列(类似栈) | 需要后进先出的场景 |
queue.PriorityQueue | 优先级队列(最小堆实现) | 按优先级处理任务 |
queue.SimpleQueue | 更简单的FIFO队列(Python3.7+) | 不需要高级功能的场景 |
- 通用方法(所有队列类都支持):
| 方法 | 说明 | 示例 | 返回值 |
|---|---|---|---|
put(item) | 放入元素 | q.put("task1") | None |
get() | 取出并移除元素 | item = q.get() | 队列元素 |
empty() | 判断队列是否为空 | if q.empty(): | True/False |
full() | 判断队列是否已满 | if q.full(): | True/False |
qsize() | 返回队列当前大小 | size = q.qsize() | 整数 |
task_done() | 标记任务完成(用于join()) | q.task_done() | None |
join() | 阻塞直到所有任务完成 | q.join() | None |
- 阻塞控制参数:
| 参数 | 说明 | 默认值 | 示例 |
|---|---|---|---|
block | 当队列为空/满时阻塞 | True | q.get(block=False) |
timeout | 阻塞超时时间(秒) | None | q.put(x, timeout=5) |
PriorityQueue专用用法,元素格式:(priority, data),优先级越小越先出队。
import queue, threading
q = queue.Queue(maxsize=3) # 容量为3的队列
def producer():
for i in range(5):
q.put(f"Task-{i}")
print(f"Produced: Task-{i}")
def consumer():
while True:
item = q.get()
print(f"Consumed: {item}")
q.task_done()
threading.Thread(target=producer, daemon=True).start()
threading.Thread(target=consumer, daemon=True).start()
q.join() # 等待所有任务完成
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
非阻塞获取(避免死锁):
try:
item = q.get_nowait() # 等价于 q.get(block=False)
except queue.empty:
print("队列为空")
2
3
4
# Python StringIO模块
在Python中,SpringIO模块是一个非常有用的工具,允许在内存中处理字符串,就像处理文件一样,通常情况下,处理文件时需要打开、读取、写入和关闭文件,而StringIO模块则提供了一种更灵活的方式。
为什么适用SpringIO模块?
- 内存效率: StringIO模块在内存中操作字符串,避免了频繁的磁盘I/O操作,提高了程序的运行效率。
- 灵活性: 允许像操作文件一样操作字符串,非常适合需要临时存储和处理字符串的场景。
- 测试和调试: 在编写测试代码时,StringIO可以模拟文件对象,方便进行单元测试和调试
# 如何适用StringIO模块?
在Python3中,StringIO模块位于io模块,需要从io模块中导入它:
from io import StringIO
# 创建StringIO对象
# 创建一个 StringIO 对象
string_io = StringIO()
# 写入数据
# 适用write()方法向StringIO对象中写入字符串数据
string_io.write("Hello, World!")
# 读取数据
# 适用getvalue()方法可以获取StringIO对象中的所有数据
data = string_io.getvalue()
print(data) # 输出: Hello, World!
# 移动指针
# StringIO对象内部有一个指针,用于指示当前读写的位置,可以使用seek()方法移动指针
string_io.seek(0) # 将指针移动到开头
# 读取一行数据
# 适用readline()方法可以读取一行数据
line = string_io.readline()
print(line) # 输出: Hello, World!
# 关闭StringIO对象
# StringIO对象在内存中操作,但为了养成良好的编程习惯,仍然可以使用close()方法关闭它
string_io.close()
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 io import StringIO
# 创建StringIO对象
string_io = StringIO()
# 写入数据
string_io.write("Python is awesome!\n")
string_io.write("StringIO is useful!")
# 移动指针到开头
string_io.seek(0)
# 读取数据
print(string_io.read())
# 关闭StringIO对象
string_io.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 单元测试中的使用
from io import StringIO
import unittest
def process_input(input_data):
return input_data.upper()
class TestProcessInput(unittest.TestCase):
def test_process_input(self):
input_data = "hello"
expected_output = "HELLO"
# 适用 StringIO 模拟输入
input_stream = StringIO(input_data)
result = process_input(input_stream.read())
self.assertEqual(result, expected_output)
if __name__ == "__main__":
unittest.main()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 常用类、方法和函数
| 属性/方法 | 描述 |
|---|---|
StringIO() | 创建一个StringIO对象,可以传入初始字符串作为参数 |
write(s) | 将字符串s写入StringIO对象 |
read([size]) | 从StringIO对象中读取最多size个字符,如果未指定,则读取全部内容 |
readline([size]) | 从StringIO对象中读取一行,最多读取size个字符 |
readlines([sizehint]) | 从StringIO对象中读取所有航,并返回一个列表。sizehint用于限制读取的字符数 |
getvalue() | 返回StringIO对象中的所有内容,作为一个字符串 |
seek(offset[,whence]) | 移动文件指针到指定为止。offset是偏移量,whence是参考位置(0: 文件开头, 1: 当前位置, 2: 文件末尾) |
tell() | 返回当前文件指针的位置 |
truncate([size]) | 截断StringIO对象的内容到指定大小,如果未指定size,则截断到当前文件指针位置 |
close() | 关闭StringIO对象,释放资源 |
closed | 返回一个布尔值,表示StringIO对象是否已关闭 |
# Python logging模块
在编程中,日志记录(logging)是一种非常重要的工具,它可以帮助我们跟踪程序的运行状态、调试错误以及记录重要信息。Python提供了一个内置的logging模块,专门用于处理日志记录任务。与简单的print语句相比,logging模块更加灵活和强大,能够满足不同场景下的日志需求。
为什么适用logging模块?
- 灵活性: logging模块允许根据需要设置日志级别、格式和输出位置。
- 可扩展性: 可以轻松地将日志输出到文件、控制台、网络等不同的目标。
- 结构化日志: logging模块支持结构化日志记录,便于后续的分析和处理。
- 性能优化: 与print相比,logging模块在性能上进行了优化,适合在生产环境中使用。
# logging 模块的基本用法
- 导入 logging 模块
import logging
- 配置日志级别
日志级别用于控制日志的详细程度,logging模块提供了以下几种日志级别:
DEBUG: 详细的调试信息,通常用于开发阶段INFO: 程序正常运行时的信息WARNING: 表示潜在的问题,单程序仍能正常运行ERROR: 表示程序中的错误,导致某些功能无法正常工作CRITICAL: 表示严重的级别,可能导致程序崩溃
logging.basicConfig(level=logging.DEBUG)
- 记录日志
设置好日志级别后,可以使用:
logging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告信息")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")
2
3
4
5
- 日志输出格式: 可以通过basicConfig方法自定义日志的输出格式
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
2
3
4
5
- 将日志输出到文件: 默认日志会输出到控制台,可以这样配置将日志保存到文件中:
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
filename="app.log"
)
2
3
4
5
# logging 模块的高级用法
- 适用多个日志记录器: 在大型项目中,可能需要为不同的模块或组件创建独立的日志记录器,如:
import logging
logger = logging.getLogger("my_logger")
logger.setLevel(logging.DEBUG)
# 创建文件处理器
file_handler = logging.FileHandler("my_logger.log")
file_handler.setLevel(logging.DEBUG)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 设置日志格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到日志记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 记录日志
logger.debug("这是一条调试信息")
logger.info("这是一条普通信息")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 日志过滤器: 可以通过过滤器控制哪些日志需要被记录
class MyFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.ERROR
logger.addFilter(MyFilter())
2
3
4
5
- 日志轮转: 当日志文件过大时,可以使用RotatingFileHandler或TimedRotatingFileHandler实现日志轮转
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler("app.log", maxBytes=1024, backupCount=3)
logger.addHandler(handler)
2
3
4
# logging 模块常用的属性和方法
- 核心类
| 类 | 说明 | 示例 |
|---|---|---|
logging.Logger | 记录器,用于发出日志信息(通过logging.getLogger(name)获取) | logger = logging.getLogger("my_logger") |
logging.Handler | 处理器,决定日志输出位置(如文件、控制台等) | handler = logging.FileHandler("app.log") |
logging.Formatter | 格式化器,控制日志输出的格式 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') |
logging.Filter | 过滤器,用于更精细地控制日志记录 | filter = logging.Filter("module.name") |
- Logger对象常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
logger.setLevel(level) | 设置日志级别(如logging.DEBUG, logging.INFO) | logger.setLevel(logging.DEBUG) |
logger.debug(msg) | 记录DEBUG级别日志 | logger.debug("调试信息") |
logger.info(msg) | 记录INFO级别日志 | logger.info("程序启动") |
logger.warning(msg) | 记录WARNING级别日志 | logger.warning("磁盘空间不足") |
logger.error(msg) | 记录ERROR级别日志 | logger.error("操作失败") |
logger.critical(msg) | 记录CRITICAL级别日志 | logger.critical("系统崩溃") |
logger.addHandler(handler) | 添加处理器 | logger.addHandler(handler) |
logger.addFilter(filter) | 添加过滤器 | logger.addFilter(filter) |
- Handler常用类型
| Handler类型 | 说明 | 示例 |
|---|---|---|
StreamHandler | 输出到流(如控制台) | handler = logging.StreamHandler() |
FileHandler | 输出到文件 | handler = logging.FileHandler("app.log") |
RotatingFileHandler | 按文件大小分割日志 | handler = logging.RotatingFileHandler("app.log", maxBytes=1e6, backupCount=3) |
TimedRotatingFileHandler | 按时间分割日志 | handler = logging.TimedRotatingFileHandler("app.log", when="midnight") |
SMTPHandler | 通过邮件发送日志 | handler = logging.SMTPHandler("mail.example.com", "from@example.com", "to@example.com", "Error Log") |
- 日志级别(常量)
| 级别 | 数值 | 说明 |
|---|---|---|
CRITICAL | 50 | 严重错误,程序可能无法继续运行 |
ERROR | 40 | 错误,但程序仍可运行 |
WARNING | 30 | 警告信息(默认级别) |
INFO | 20 | 程序运行信息 |
DEBUG | 10 | 调试信息 |
NOTSET | 0 | 继承父记录器的级别 |
- Formatter常用格式字段
| 字段 | 说明 | 示例输出 |
|---|---|---|
%(asctime)s | 日志创建时间 | 2023-01-01 12:00:00,123 |
%(levelname)s | 日志级别名称 | INFO |
%(message)s | 日志消息内容 | 程序启动成功 |
%(name)s | 记录器名称 | my_logger |
%(filename)s | 生成日志的文件名 | app.py |
%(lineno)s | 生成日志的行号 | 42 |
%(funcName)s | 生成日志的函数名 | main |
- 快速配置方法
| 方法 | 说明 | 示例 |
|---|---|---|
logging.basicConfig() | 一键配置日志级别、处理器和格式(通常在程序入口调用) | logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s') |
常用参数:
level: 设置根记录器级别filename: 输出到文件filemode: 文件模式(如w覆盖)format: 格式字符串datefmt: 日期格式(如%Y-%m-%d %H:%M:%S)
基础配置示例:
import logging
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='app.log'
)
# 使用
logger = logging.getLogger("my_app")
logger.info("程序启动")
2
3
4
5
6
7
8
9
10
11
12
多处理器复杂配置示例:
import logging
# 创建记录器
logger = logging.getLogger("my_module")
logger.setLevel(logging.DEBUG)
# 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
# 文件处理器
file_handler = logging.FileHandler("debug.log")
file_handler.setLevel(logging.DEBUG)
# 格式化
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 使用
logger.debug("调试信息") # 仅写入文件
logger.warning("警告!") # 同时输出到控制台和文件
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
日志分割示例:
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"app.log", maxBytes=1e6, backupCount=3 # 每个文件1MB,保留3个备份
)
logger.addHandler(handler)
2
3
4
5
6
# Python datetime模块
Python的datetime模块是用于处理日期和时间的标准库模块。其提供了多种类和函数,可以轻松地处理日期、时间、时间差等操作。
datetime模块的核心类:
date: 用于表示日期,包含年、月、日三个属性time: 用于表示时间,包含时、分、秒、微秒等属性datetime: date和time的结合体,可以同时表示日期和时间timedelta: 用于表示时间差,可以用于日期和时间的加减操作
# 使用 datetime 模块
- 获取当前日期和时间
from datetime import datetime
# 获取当前日期和时间
now = datetime.now()
print("当前时间:", now)
2
3
4
5
- 创建特定的日期和时间
from datetime import datetime
# 创建特定的日期和时间
specific_time = datetime(2025, 4, 22, 15, 30, 0)
print("特定时间:", specific_time)
2
3
4
5
- 格式化日期和时间
from datetime import datetime
# 获取当前时间
now = datetime.now()
# 格式化输出
formatted_time = now.strftime("%Y-%m-%d %H:%M:%S")
print("格式化时间:", formatted_time)
2
3
4
5
6
7
8
- 计算时间差
from datetime import datetime, timedelta
# 获取当前时间
now = datetime.now()
# 计算 10 天后的时间
future_time = now + timedelta(days=10)
print("10 天后的时间:", future_time)
2
3
4
5
6
7
8
# 常见应用场景
- 计算两个日期之间的天数
from datetime import date
# 创建两个日期
date1 = date(2025, 4, 22)
date2 = date(2025, 5, 1)
# 计算天数差
delta = date2 - date1
print("两个日期之间的天数差: ", delta.days) # 9
2
3
4
5
6
7
8
9
- 处理时区: datetime模块本身不直接支持时区操作,但可以通过pytz库来处理时区
from datetime import datetime
import pytz
# 获取当前时间并设置时区
now = datetime.now(pytz.timezone('Asia/Shanghai'))
print("上海当前时间: ", now)
2
3
4
5
6
# 常用类、方法及属性
- 核心类
| 类 | 说明 | 示例 |
|---|---|---|
datetime.date | 日期类(年、月、日) | date(2023, 5, 15) |
datetime.time | 时间类(时、分、秒、微秒) | time(14, 30, 0) |
datetime.datetime | 日期时间类(包含日期和时间) | datetime(2023, 5, 15, 14, 30) |
datetime.timedelta | 时间间隔类(用于日期/时间计算) | timedelta(days=5) |
datetime.tzinfo | 时区信息基类(需子类化实现) | 自定义时区类 |
- date 对象常用方法/属性
| 方法/属性 | 说明 | 示例 |
|---|---|---|
date.today() | 返回当前本地日期 | date.today() -> date(2023, 5, 15) |
date.fromisoformat(str) | 从YYYY-MM-DD字符串解析日期 | date.fromisoformat("2023-05-15") |
date.year | 年份(只读) | d.year -> 2023 |
date.month | 月份(1-12, 只读) | d.month -> 5 |
date.day | 日(1-31, 只读) | d.day -> 15 |
date.weekday() | 返回星期几(0-周一, 6=周日) | d.weekday() -> 0 |
date.isoformat() | 返回YYYY-MM-DD格式字符串 | d.isoformat() -> "2023-05-15" |
date.strftime(format) | 自定义格式输出 | d.strftime(%Y/%m/%d) -> "2023/05/15" |
- time 对象常用方法/属性
| 方法/属性 | 说明 | 示例 |
|---|---|---|
time.hour | 小时(0-23, 只读) | t.hour -> 14 |
time.minute | 分钟(0-59, 只读) | t.minute -> 30 |
time.second | 秒(0-59, 只读) | t.second -> 0 |
time.microsecond | 微秒(0-999999, 只读) | t.microsecond -> 0 |
time.isoformat() | 返回HH:MM:SS.mmmmmm格式字符串 | t.isoformat() -> "14:30:00" |
time.strftime(format) | 自定义格式化输出 | t.strftime("%H:%M") -> "14:30" |
- datetime 对象常用方法/属性
| 方法/属性 | 说明 | 示例 |
|---|---|---|
datetime.now() | 返回当前本地日期时间 | datetime.now() -> datetime(2023, 5, 15, 14, 30, 0) |
datetime.utcnow() | 返回当前UTC日期时间 | datetime.utcnow() |
datetime.fromtimestamp(ts) | 从时间戳创建datetime对象 | datetime.fromtimestamp(1684146600) |
datetime.timestamp() | 返回时间戳(浮点数秒) | dt.timestamp() -> 1684146600.0 |
datetime.date() | 提取日期部分 | dt.date() -> date(2023, 5, 15) |
datetime.time() | 提取时间部分 | dt.time() -> time(14, 30) |
datetime.year | 年份(同date) | dt.year -> 2023 |
datetime.strftime(format) | 自定义格式化输出 | dt.strftime("%Y-%m-%d %H:%M") -> 2023-05-15 14:30 |
- timedelta对象常用属性
| 属性 | 说明 | 示例 |
|---|---|---|
days | 天数(可正可负) | delta.days -> 5 |
seconds | 秒数(0-86399) | delta.seconds -> 3600(1小时) |
microseconds | 微秒数(0-999999) | delta.microseconds -> 0 |
- 常用格式化符号(strftime)
| 符号 | 说明 | 示例输出 |
|---|---|---|
%Y | 四位年份 | 2023 |
%m | 两位月份(01-12) | 05 |
%d | 两位日(01-31) | 15 |
%H | 24小时制小时(00-23) | 14 |
%M | 分钟(00-59) | 30 |
%S | 秒(00-59) | 00 |
%A | 完整星期名 | Monday |
%a | 缩写星期名 | Mon |
%B | 完整月份名 | May |
%b | 缩写月份名 | May |
from datetime import date, timedelta, datetime, timedelta
import pytz
# 计算日期差
d1 = date(2023, 5, 15)
d2 = date(2023, 6, 1)
delta = d2 - d1 # 返回 timedelta 对象
print(delta.days) # 输出: 17
# 时间加减
now = datetime.now()
future = now + timedelta(days=3, hours=2)
print(future.strftime("%Y-%m-%d %H:%M")) # 2025-05-26 16:40
# 时区转换
utc_time = datetime.now(pytz.UTC)
beijing_time = utc_time.astimezone(pytz.timezone("Asia/Shanghai"))
print(beijing_time) # 2025-05-23 14:40:58.602405+08:00
# 解析字符串
dt = datetime.strptime("2023-05-15 14:30", "%Y-%m-%d %H:%M")
print(dt.year) # 输出: 2023
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
注意事项
- 不可变性: 所有 datetime 对象不可变,操作会返回新对象
- 时区处理: 原生 datetime 无时区支持,需用pytz或Python3.9+的zoneinfo
- 性能: 频繁创建对象可能影响性能,考虑重用或缓存
- 边界检查: 非法日期(如date(2023, 2, 30))会出发ValueError
# Python re模块
Python的re模块是用于处理正则表达式的标准模块。正则表达式(Regular Expression, 简称regex或regexp)是一种强大的工具,用于匹配、搜索和操作文本。
为什么适用re模块?: 在处理文本时,经常需要查找特定的模式或替换某些字符。例如,验证电子邮件地址、提取网页中的链接、或者格式化文本。手动编写代码来完成这些任务可能会非常繁琐,而正则表达式提供了一种简洁且高效的方式来解决这些问题。
# re 模块的基本用法
import re
pattern = r"hello"
text = "hello world"
# re.match()
# 从字符串的起始位置匹配正则表达式,如果匹配成功,返回一个匹配对象,否则返回None
match = re.match(pattern, text)
if match:
print("匹配成功: ", match.group())
else:
print("匹配失败")
# re.search()
# 用于在字符串中搜索正则表达式的第一个匹配项,与match()不同,search()不要求匹配从字符串的起始位置开始
pattern = r"world"
text = "hello world"
match = re.search(pattern, text)
if match:
print("匹配成功: ", match.group())
else:
print("匹配失败")
# re.findall()
# 用于查找字符串中所有与正则表达式匹配的子串,并返回一个列表。
pattern = r"\d+"
text = "There are 3 apples and 5 oranges."
matches = re.findall(pattern, text)
print("找到的数字: ", matches)
# re.sub()
# 用于替换字符串中与正则表达式匹配的部分
pattern = r"apple"
text = "I have an apple."
new_text = re.sub(pattern, "banana", text)
print("替换后的文本: ", new_text)
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
# 正则表达式的基本语法
- 普通字符: 如字母、数字直接匹配它们自身
import re
pattern = r"cat"
text = "The cat is on the mat."
match = re.search(pattern, text)
if match:
print("匹配成功:", match.group())
2
3
4
5
6
7
8
- 特殊字符: 具有特殊含义的字符,如
.: 匹配任意单个字符(除了换行符)*: 匹配前面的字符零次或多次+: 匹配前面的字符一次或多次?: 匹配前面的字符零次或一次\d: 匹配任意数字字符(等价于[0-9])\w: 匹配任意字母、数字或下划线字符(等价于[a-zA-Z0-9_])
import re
pattern = r"\d+"
text = "The price is 100 dollars."
match = re.search(pattern, text)
if match:
print("匹配成功:", match.group())
2
3
4
5
6
7
8
- 字符集: 用于匹配一组字符中的任意一个,如
[abc]匹配a, b或c
import re
pattern = r"[aeiou]"
text = "Hello World!"
matches = re.findall(pattern, text)
print("找到的元音字母:", matches)
2
3
4
5
6
7
- 分组: 允许将多个字符组合在一起,并对它们进行操作,如
(abc)匹配abc
import re
pattern = r"(ab)+"
text = "ababab"
match = re.search(pattern, text)
if match:
print("匹配成功:", match.group()) # ababab
2
3
4
5
6
7
8
# 实践练习
- 验证电子邮件地址
import re
pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
email = "example@example.com"
if re.match(pattern, email):
print("有效的电子邮件地址")
else:
print("无效的电子邮件地址")
2
3
4
5
6
7
8
9
- 提取电话号码
import re
pattern = r"\d{3}-\d{3}-\d{4}"
text = "My phone number is 123-456-7890."
match = re.search(pattern, text)
if match:
print("找到的电话号码:", match.group())
2
3
4
5
6
7
8
- 核心函数
| 方法 | 说明 | 示例 |
|---|---|---|
re.compile(pattern) | 预编译正则表达式(提升复用性能) | pat = re.compile(r'\d+') |
re.search(pattern, string) | 搜索字符串中第一个匹配项 | re.search(r'\d+', 'a1b2') -> 匹配1 |
re.match(pattern, string) | 从字符串起始位置匹配 | re.match(r'\d+', '123a') -> 匹配123 |
re.fullmatch(pattern, string) | 整个字符串完全匹配 | re.fullmatch(r'\d+', '123') -> 匹配123 |
re.findall(pattern, string) | 返回所有非重叠匹配的列表 | re.findall(r'\d+', 'a1b22c') -> ['1', '22'] |
re.finditer(pattern, string) | 返回所有匹配的迭代器(含位置信息) | for m in re.finditer(r'\d+', 'a1b2'): print(m.group()) |
re.sub(pattern, repl, string) | 替换匹配项 | re.sub(r'\d+', 'X', 'a1b2') -> aXbX |
re.split(pattern, string) | 按匹配项分割字符串 | re.split(r'\d+', 'a1b2c') -> ['a', 'b', 'c'] |
- 匹配对象(Match)方法/属性
| 方法/属性 | 说明 | 示例 |
|---|---|---|
group() | 返回整个匹配的字符串 | m.group() -> abc |
group(n) | 返回第n个捕获组的内容 | m = re.search(r'(\d)(\d)', '12'); m.group(1) -> 1 |
groups() | 返回所有捕获组的元组 | m.groups() -> ('1', '2') |
start()/end() | 匹配的起始/结束位置 | m.start() -> 0 |
span() | 返回匹配范围(start, end) | m.span() -> (0, 2) |
- 正则表达式元字符(部分)
| 元字符 | 说明 | 示例匹配 |
|---|---|---|
. | 匹配任意字符(除换行符) | a.c -> 'abc' |
\d | 匹配数字 | \d+ -> '123' |
\D | 匹配非数字 | \D+ -> 'abc' |
\w | 匹配单词字符(字母、数字、下划线) | \w+ -> 'Ab_1' |
\W | 匹配非单词字符 | \W+ -> '!@#' |
\s | 匹配空白字符(空格、制表符) | \s+ -> ' \t' |
\S | 匹配非空白字符 | \S+ -> 'abc' |
[] | 字符集合 | [A-Za-z] -> 任意字母 |
^ | 匹配字符串开头 | ^\d+ -> 开头的数字 |
$ | 匹配字符串结尾 | \d+$ -> 结尾的数字 |
* | 匹配前一个字符0次或多次 | a* -> '','aaa' |
+ | 匹配前一个字符1次或多次 | a+ -> 'a','aaa' |
? | 匹配前一个字符0次或1次 | a? -> ','a' |
{m, n} | 匹配前一个字符m到n次 | a{2, 3} -> aa','aaa |
\| | 或操作 | cat \| dog -> 'cat' 或 'dog' |
() | 捕获分组 | (\d+) -> 提取数字 |
- 编译标志(flags参数)
| 标志 | 说明 | 示例 |
|---|---|---|
re.IGNORECASE(re.I) | 忽略大小写 | re.search(r'abc', 'ABC', re.I) |
re.MULTILINE(re.M) | 多行模式(影响^和$) | re.findall(r'^\d+', '1\n2', re.M) -> ['1', '2'] |
re.DOTALL(re.S) | 让.匹配包括换行符的所有字符 | re.search(r'a.*b', 'a\nb', re.S) |
re.ASCII | 让\w, \W等仅匹配ASCII字符 | e.search(r'\w+', 'こん', re.ASCII) -> 无匹配 |
re.VERBOSE(re.X) | 允许正则中添加注释和空格 | re.compile(r'''\d+ # 匹配数字''', re.X) |
import re
# 提取邮箱地址
text = "Contact: admin@example.com, support@test.org"
emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
print(emails) # 输出: ['admin@example.com', 'support@test.org']
# 替换日期格式
date_str = "Today is 05-15-2023"
new_str = re.sub(r'(\d{2})-(\d{2})-(\d{4})', r'\3年\1月\2日', date_str)
print(new_str) # 输出: "Today is 2023年05月15日"
# 多条件匹配
pattern = re.compile(r'''
^(?P<username>\w+) # 用户名
:(?P<password>\S+) # 密码
@(?P<domain>\w+\.\w+) # 域名
$''', re.VERBOSE)
m = pattern.match("john:pass123@example.com")
if m:
print(m.groupdict()) # 输出: {'username': 'john', 'password': 'pass123', 'domain': 'example.com'}
# 分割复杂字符串
text = "Apple1Banana2Cherry3Date"
parts = re.split(r'\d+', text)
print(parts) # 输出: ['Apple', 'Banana', 'Cherry', 'Date']
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
注意事项
- 原始字符串: 建议正则表达式使用原始字符串
r'\d', 避免转义符冲突 - 贪婪匹配: 默认贪婪匹配(如
.*会匹配到最长可能),需用.*?实现非贪婪匹配 - 性能优化: 频繁使用的正则应优先用
re.compile()预编译 - 回溯问题: 复杂正则可能导致性能问题(如嵌套量词
(a+)+)
# Python csv模块
CSV(Comma-Separated Values)文件是一种常见的文件格式,用于存储表格数据,其由纯文本组成,每一行代表表格中的一行数据,而每一列则通过逗号(或其他分割符号)分隔,通常用于数据交换,因为它简单且易于处理。
Python提供了一个内置的csv模块,用于读取和写入CSV文件。这个模块简化了处理CSV文件的过程。
- 读取CSV文件
import csv
# 打开文件
with open('data.csv', mode='r', encoding='utf-8') as file: # 以只读模式打开名为data.csv的文件,并指定编码为UTF-8
# 创建csv.reader对象
csv_reader = csv.reader(file) # 创建一个csv.reader对象,用于读取文件内容
# 逐行读取数据
for row in csv_reader: # 逐行读取文件内容,每一行数据会被解析为一个列表
print(row)
2
3
4
5
6
7
8
9
- 写入CSV文件
import csv
# 要写入的数据
data = [
['Name', 'Age', 'City'],
['Alice', '30', 'New York'],
['Bob', '25', 'Los Angeles'],
]
# 打开CSV文件
with open('output.csv', mode='w', encoding='utf-8', newline='') as file: # 以写入模式打开output.csv文件,编码utf-8,newline='' 用于避免在Windows系统中出现空行
# 创建csv.writer对象
csv_writer = csv.writer(file) # 创建一个csv.writer对象,用于写入文件内容
# 写入数据
for row in data:
csv_writer.writerow(row) # 将每一行数据写入文件
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 使用字典读取和写入CSV文件
import csv
# 使用DictReader读取CSV文件
with open('data.csv', mode='r', encoding='utf-8') as file:
csv_dict_reader = csv.DictReader(file)
for row in csv_dict_reader:
print(row)
# 使用DictWriter写入CSV文件
data = [
{'Name': 'Alice', 'Age': '30', 'City': 'New York'},
{'Name': 'Bob', 'Age': '25', 'City': 'Los Angeles'},
]
with open('output.csv', mode='w', encoding='utf-8', newline='') as file:
fieldnames = ['Name', 'Age', 'City']
csv_dict_writer = csv.DictWriter(file, fieldnames=fieldnames)
# 写入表头
csv_dict_writer.writeheader()
# 写入数据
for row in data:
csv_dict_writer.writerow(row)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 常用的属性和方法
- csv模块核心方法
| 方法 | 说明 | 示例 |
|---|---|---|
csv.reader() | 从文件对象读取CSV数据 | reader = csv.reader(file) |
csv.writer() | 将数据写入CSV文件 | writer = csv.writer(file) |
csv.DictReader() | 将CSV行读取为字典(带表头) | dict_reader = csv.DictReader(file) |
csv.DictWriter() | 将字典写入CSV文件(需指定字段名) | dict_writer = csv.DictWriter(file, fieldnames) |
csv.register_dialect() | 注册自定义CSV格式(如分隔符) | csv.register_dialect('mydialect', delimiter=',') |
csv.unregister_dialect() | 删除已注册的方言 | csv.unregister_dialect('mydialect') |
csv.list_dialects() | 列出所有已注册的方言 | print(csv.list_dialects()) |
- csv.reader 和 csv.writer 对象常用方法
| 方法 | 说明 | 适用对象 |
|---|---|---|
__next__() | 迭代读取下一行(或使用for循环) | reader |
writerow(row) | 写入单行数据 | writer |
writerows(rows) | 写入多行数据(列表的列表) | writer |
- csv.DictReader 和 csv.DictWriter 对象特性
| 特性/方法 | 说明 | 示例 |
|---|---|---|
fieldnames | 字段名列表(DictReader自动从首行获取) | dict_reader.fieldnames |
writeheader() | 写入表头行(DictWriter专用) | dict_writer.writeheader() |
- 常用参数说明
| 参数 | 说明 | 示例值 | 适用方法 |
|---|---|---|---|
delimiter | 字段分隔符 | ','(默认),'\t' | reader/writer |
quotechar | 引用字符(包围特殊字段) | '"'(默认) | reader/writer |
quoting | 引用规则 | csv.QUOTE_ALL(全部引用) | reader/writer |
skipinitialspace | 忽略分隔符后的空格 | True/False | reader |
lineterminator | 行结束符 | \r\n(默认) | writer |
dialect | 预定义的方言名称 | 'excel'(默认) | 所有方法 |
# Python threading模块
Python的threading模块是用于实现多线程编程的标准库之一。多线程允许程序在同一时间内执行多个任务,从而提高程序的效率和相应速度。threading模块提供了创建和管理线程的工具。
在单线程程序中,任务是一个接一个地顺序执行,如果某个任务需要等待(例如等待网络相应或文件读取),整个程序会被阻塞,直到该任务完成。多线程可以让程序在等待某个任务的同时,继续执行其他任务,从而提高程序的整体性能。
# 如何使用threading模块
# 创建线程
- 继承threading.Thread类
import threading
class MyThread(threading.Thread):
def run(self):
print("线程开始执行")
# 在这里编写线程要执行的代码
print("现成执行结束")
# 创建线程实例
thread = MyThread()
# 启动线程
thread.start()
# 等待线程执行完毕
thread.join()
print("主线程结束")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 使用threading.Thread构造函数
import threading
def my_function():
print("线程开始执行")
# 在这里编写线程要执行的代码
print("线程执行结束")
# 创建线程实例
thread = threading.Thread(target=my_function)
# 启动线程
thread.start()
# 等待线程执行完毕
thread.join()
print("主线程结束")
2
3
4
5
6
7
8
9
10
11
12
13
14
# 线程同步
在多线程编程中,多个线程可能会同时访问共享资源,可能导致数据不一致的问题,为了避免这种情况,可以使用线程同步机制,如锁(Lock):
import threading
# 创建一个锁对象
lock = threading.Lock()
def my_function():
with lock:
print("线程开始执行")
# 在这里编写线程要执行的代码
print("线程执行结束")
# 创建线程实例
thread1 = threading.Thread(target=my_function)
thread2 = threading.Thread(target=my_function)
# 启动线程
thread1.start()
thread2.start()
# 等待线程执行完毕
thread1.join()
thread2.join()
print("主线程结束")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 线程间通信
线程间通信可以通过队列(Queue)实现,Queue是线程安全的,可以在多个线程之间安全地传递数据。
import threading
import queue
def worker(q):
while not q.empty():
item=q.get()
print(f"处理项目: {item}")
q.task_done()
# 创建一个队列并填充数据
q = queue.Queue()
for i in range(10):
q.put(i)
# 创建线程实例
thread1 = threading.Thread(target=worker, args=(q,))
thread2 = threading.Thread(target=worker, args=(q,))
# 启动线程
thread1.start()
thread2.start()
# 等待队列中的所有项目被处理完毕
q.join()
print("所有项目处理完毕")
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
# 常用类、方法及属性
- 核心类
| 类/方法/属性 | 说明 | 示例 |
|---|---|---|
threading.Thread | 线程类,用于创建和管理线程 | t = Thread(target=func, args=(1,)) |
threading.Lock | 互斥锁(原始锁) | lock = Lock() |
threading.RLock | 可重入锁(同一线程可多次获取) | rlock = RLock() |
threading.Event | 事件对象,用于线程同步 | event = Event() |
threading.Condition | 条件变量,用于复杂线程协调 | cond = Condition() |
threading.Semaphore | 信号量,控制并发线程数 | sem = Semaphore(3) |
threading.BoundedSemaphore | 有界信号量(防止计数超过初始值) | b_sem = BoundedSemaphore(2) |
threading.Timer | 定时器线程 | timer = Timer(5.0, func) |
threading.local | 线程局部数据(各线程独立存储) | local_data = threading.local() |
- Thread对象常用方法/属性
| 方法/属性 | 说明 | 示例 |
|---|---|---|
start() | 启动线程 | t.start() |
run() | 线程执行的方法(可重写) | 自定义类时覆盖此方法 |
join(timeout=None) | 阻塞当前线程,直到目标线程结束 | t.join() |
is_alive() | 检查线程是否在运行 | if t.is_alive(): |
name | 线程名称(可修改) | t.name = "Worker-1" |
daemon | 守护线程标志(主线程退出时自动结束) | t.daemon = True |
ident | 线程标识符(未启动时为None) | print(t.ident) |
- Lock/RLock常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
acquire(blocking=True, timeout=-1) | 获取锁(阻塞或非阻塞) | lock.acquire() |
release() | 释放锁 | lock.release() |
locked() | 检查锁是否被占用 | if not lock.locked(): |
- Event常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
set() | 设置事件为真,唤醒所有等待线程 | event.set() |
clear() | 重置事件为假 | event.clear() |
wait(timeout=None) | 阻塞直到事件为真或超时 | event.wait(2.0) |
is_set() | 检查事件状态 | if event.is_set(): |
- Condition常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
wait(timeout=None) | 释放锁并阻塞,直到被通知或超时 | cond.wait() |
notify(n=1) | 唤醒最多n个等待线程 | cond.notify(2) |
notify_all() | 唤醒所有等待线程 | cond.notify_all() |
- 模块级函数/属性
| 函数/属性 | 说明 | 示例 |
|---|---|---|
threading.active_count() | 返回当前活跃线程数 | print(threading.active_count()) |
threading.current_thread() | 返回当前线程对象 | print(threading.current_thread().name) |
threading.enumerate() | 返回所有活跃线程的列表 | for t in threading.enumerate(): |
threading.main_thread() | 返回主线程对象 | if threading.current_thread() is threading.main_thread(): |
threading.get_ident() | 返回当前线程的标识符(Python 3.3+) | print(threading.get_ident()) |
# 注意事项
- 全局解释器锁(GIL): Python的GIL会限制同一时间只有一个线程执行Python字节码。因此,在CPU密集型任务中,多线程可能不会带来性能提升,对于I/O密集型任务,多线程仍然是有益的。
- 线程安全: 在多线程环境中,确保对共享资源的访问是线程安全的,避免数据竞争和死锁。
- 线程数量: 创建过多的线程可能会导致系统资源耗尽,影响程序性能。合理控制线程数量,或使用线程池(ThreadPoolExecutor)来管理线程。
# Python asyncio模块
asyncio是Python标准库中的一个模块,用于编写一步I/O操作的代码。asyncio提供了一种高效的方式来处理并发任务,特别适用于I/O密集型操作,如网络请求、文件读写等。通过使用asyncio,可以在单线程重同时处理多个任务,而无需使用多线程或多进程。
在传统的同步编程中,当一个任务需要等待I/O操作(如网络请求)完成时,程序会阻塞,直到操作完成。这会导致程序的效率低下,尤其在需要处理大量I/O操作时。asyncio通过引入异步编程模型,允许程序在等待I/O操作时继续执行其他任务,从而提高程序的并发性和效率。
# asyncio的核心概念
- 协程(Coroutine): 协程是asyncio的核心概念之一,它是一个特殊的函数,可以在执行过程中暂停,并在稍后恢复执行。协程通过
async def关键字定义,并通过await关键字暂停执行,等待异步操作完成。
import asyncio
async def say_hello():
print("Hello, World!")
await asyncio.sleep(1) # Simulate an asynchronous operation
print("Goodbye, World!")
2
3
4
5
6
- 事件循环(Event Loop): 事件循环是asyncio的核心组件,负责调度和执行协程。它不断地检查是否有任务需要执行,并在任务完成后调用相应的回调函数。
async def main():
await say_hello()
asyncio.run(main())
2
3
4
- 任务(Task): 任务是对协程的封装,表示一个正在执行或将要执行的协程,可以通过
asyncio.create_task()函数创建任务,并将其添加到事件循环重。
async def main():
task = asyncio.create_task(say_hello())
await task
2
3
- Future: 是一个表示异步操作结果的对象,它通常用于底层API,表示一个尚未完成的操作。可以通过
await关键字等待Future完成。
async def main():
future = asyncio.Future()
await future
2
3
# asyncio的基本用法
- 运行协程: 使用
asyncio.run()函数,它会创建一个事件循环,并运行指定的协程。
import asyncio
async def main():
print("Start")
await asyncio.sleep(1)
print("End")
asyncio.run(main())
2
3
4
5
6
7
8
- 并发执行多个任务: 使用
asyncio.gather()函数并发执行多个协程,并等待它们全部完成。
import asyncio
async def task1():
print("Task 1 started")
await asyncio.sleep(1)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(2)
print("Task 2 finished")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 超时控制: 使用
asyncio.wait_for()函数为协程设置超时时间,如果协程在指定时间内未完成,将引发asyncio.TimeoutError异常。
import asyncio
async def long_task():
await asyncio.sleep(10)
print("Task finished")
async def main():
try:
await asyncio.wait_for(long_task(), timeout=5)
except asyncio.TimeoutError:
print("Task timeed out")
asyncio.run(main())
2
3
4
5
6
7
8
9
10
11
12
13
# asyncio的应用场景
asyncio特别适用于以下场景:
- 网络请求: 如HTTP请求、WebSocket通信等
- 文件I/O: 如异步读写文件
- 数据库操作: 如异步访问数据库
- 实时数据处理: 如实时消息队列处理
# 常用类、方法和函数
- 核心函数
| 方法/函数 | 说明 | 示例 |
|---|---|---|
asyncio.run(coro) | 运行异步主函数(Python3.7+) | asyncio.run(main()) |
asyncio.create_task(coro) | 创建任务并加入事件循环 | task = asyncio.create_task(fetch_data()) |
asyncio.gather(*coros) | 并发运行多个协程 | await asyncio.gather(task1, task2) |
asyncio.sleep(delay) | 异步等待(非阻塞) | await asyncio.sleep(1) |
asyncio.wait(coros) | 控制任务完成方式 | done, pending = await asyncio.wait([task1, task2]) |
- 事件循环(Event Loop)
| 方法 | 说明 | 示例 |
|---|---|---|
loop.run_until_complete(future) | 运行直到任务完成 | loop.run_until_complete(main()) |
loop.run_forever() | 永久运行事件循环 | loop.run_forever() |
loop.stop() | 停止事件循环 | loop.stop() |
loop.close() | 关闭事件循环 | loop.close() |
loop.call_soon(callback) | 安排回调函数立即执行 | loop.call_soon(print, "Hello") |
loop.call_later(delay, callback) | 延迟执行回调 | loop.call_later(5, callback) |
- 协程(Coroutine)与任务(Task)
| 方法/装饰器 | 说明 | 示例 |
|---|---|---|
@asyncio.coroutine | 协程装饰器(旧版,Python3.4-3.7) | @asyncio.coroutinedef old_coro(): |
async def | 定义协程(Python3.5+) | async def fetch(): |
task.cancel() | 取消任务 | task.cancel() |
task.done() | 检查任务是否完成 | if task.done(): |
task.result() | 获取任务结果(需任务完成) | data = task.result() |
- 同步原语(类似threading)
| 类 | 说明 | 示例 |
|---|---|---|
asyncio.Lock() | 异步互斥锁 | lock = asyncio.Lock()async with lock: |
asyncio.Event() | 事件通知 | event = asyncio.Event()await event.wait() |
asyncio.Queue() | 异步队列 | queue = asyncio.Queue()await queue.put(item) |
asyncio.Semaphore() | 信号量 | sem = asyncio.Semaphore(5)async with sem: |
- 网络与子进程
| 方法/类 | 说明 | 示例 |
|---|---|---|
asyncio.open_connection() | 建立TCP连接 | reader, writer = await asyncio.open_connection('host', 80) |
asyncio.start_server() | 创建TCP服务器 | server = await asyncio.start_server(handle, '0.0.0.0', 8888) |
asyncio.create_subprocess_exec() | 创建子进程 | proc = await asyncio.create_subprocess_exec('ls') |
- 实用工具
| 方法 | 说明 | 示例 |
|---|---|---|
asyncio.current_task() | 获取当前任务 | task = asyncio.current_task() |
asyncio.all_tasks() | 获取所有任务 | tasks = asyncio.all_tasks() |
asyncio.shield(coro) | 保护任务不被取消 | await asyncio.shield(critical_task) |
asyncio.wait_for(coro, timeout) | 带超时的等待 | try: await asyncio.wait_for(task, 5) |
import asyncio
async def fetch(url):
print(f"Fetching {url}")
await asyncio.sleep(2)
return f"Data from {url}"
async def main():
results = await asyncio.gather(
fetch("url1.com"),
fetch("url2.com")
)
print(results)
asyncio.run(main())
# Fetching url1.com
# Fetching url2.com
# ['Data from url1.com', 'Data from url2.com']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import asyncio
async def producer(queue):
for i in range(5):
await queue.put(i)
await asyncio.sleep(0.1)
async def consumer(queue):
while True:
item = await queue.get()
print(f"Consumed {item}")
queue.task_done()
async def main():
queue = asyncio.Queue()
await asyncio.gather(
producer(queue),
consumer(queue)
)
asyncio.run(main())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 注意事项
- Python版本: 部分功能需Python3.7+(如asyncio.run())
- 阻塞操作: 避免在协程中使用同步阻塞代码(如time.sleep())
- 调试: 设置
PYTHONASYNCIODEBUG=1环境变量可启用调试模式 - 取消任务: 被取消的任务会引发
CancelledError,需妥善处理
# Python PyQt
PyQt是一个强大的Python库,用于创建图形用户界面(GUI),可以用来代替Python内置的Tkinter。 PyQt是Qt框架的Python绑定,广泛应用于桌面应用程序开发。Qt是一个跨平台的C++应用程序开发框架。PyQt允许Python开发者利用Qt库创建功能强大的GUI应用程序。
PyQt有以下几个主要版本:
- PyQt4: 基于Qt4的绑定
- PyQt5: 基于Qt5的绑定
- PyQt6: 基于Qt6的绑定(最新版本)
安装PyQt:
# 安装PyQt5
pip install PyQt5
# 安装Qt设计师和其他工具(可选)
pip install PyQt5-tools
2
3
4
# 第一个PyQt程序
from PyQt5.QtWidgets import QApplication, QWidget
# 创建应用实例
app = QApplication([])
# 创建主窗口
window = QWidget()
window.setWindowTitle("我的第一个PyQt程序")
window.setGeometry(100, 100, 400, 300) # (x, y, width, height)
# 显示窗口
window.show()
# 运行应用
app.exec_()
# 1. QAapplication: 管理GUI应用程序的控制流和主设置
# 2. QWidget: 最基本的窗口类,所有UI组件都继承它
# 3. setWindowTitle(): 设置窗口标题
# 4. setGeometry(): 设置窗口位置和大小
# 5. show(): 显示窗口
# 6. app.exec_(): 启动事件循环,等待用户交互
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
一个典型的PyQt应用程序包含以下部分:
- QApplication对象: 每个PyQt应用程序都需要有一个QApplication实例
- 窗口和控件: 用户界面组件
- 事件循环: 处理用户输入和系统事件的循环
- 事件处理器: 相应事件的函数或方法
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 设置窗口标题和大小
self.setWindowTitle("我的第一个PyQt应用")
self.setGeometry(100, 100, 400, 300) # x, y, width, height
# 创建按钮
self.button = QPushButton("点击我", self)
self.button.setGeometry(150, 150, 100, 30)
self.button.clicked.connect(self.button_clicked)
def button_clicked(self):
print("按钮被点击了!")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 常用PyQt组件
- 按钮(QPushButton)
from PyQt5.QtWidgets import QPushButton
button = QPushButton("点击我", window)
button.move(150, 150) # 设置按钮位置
2
3
4
- 标签(QLabel)
from PyQt5.QtWidgets import QLabel
label = QLabel("Hello PyQt!", window)
label.move(100, 100)
2
3
4
- 文本框(QLineEdit)
from PyQt5.QtWidgets import QLineEdit
textbox = QLineEdit(window)
textbox.move(100, 50)
2
3
4
# 布局管理(QVBoxLayout)
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QLineEdit
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout()
layout.addWidget(QLabel("用户名"))
layout.addWidget(QLineEdit())
layout.addWidget(QPushButton("登录"))
window.setLayout(layout)
window.show()
sys.exit(app.exec_())
2
3
4
5
6
7
8
9
10
11
12
13
14
# 信号与槽机制
PyQt使用**信号(Signal)和槽(Slot)**机制处理事件。PyQt的信号和槽机制是用于对象之间通信的核心机制。
- 信号(Signal): 当特定事件发生时发出的通知
- 槽(Slot): 响应信号的函数或方法
from PyQt5.QtWidgets import QPushButton
def on_button_click():
print("按钮被点击了!")
button = QPushButton("点击我", window)
button.clicked.connect(on_button_click) # 连接信号和槽
2
3
4
5
6
7
自定义信号:
from PyQt5.QtCore import pyqtSignal, QObject
class MyEmitter(QObject):
my_signal = pyqtSignal(str) # 定义一个信号
emitter = MyEmitter()
emitter.my_signal.connect(lambda x: print(f"收到信号: {x}"))
emitter.my_signal.emit("Hello") # 触发信号
2
3
4
5
6
7
8
# 使用Qt Designer
Qt Designer是一个可视化设计工具,可以拖放组件来设计界面:
- 启动Designer(通常在Python安装目录的
Lib\site-packages\qt5-application\Qt\bin下) - 设计界面并保存为
.ui文件 - 将
.ui文件转换为Python代码:pyuic5 input.ui -o output.py
在代码中使用生成的界面:
from PyQt5 import uic
# 加载UI文件
Form, Window = uic.loadUiType("output.ui")
# 使用UI
app = QApplication(sys.argv)
window = Window()
form = Form()
form.setupUi(window)
window.show()
sys.exit(app.exec_())
2
3
4
5
6
7
8
9
10
11
12
# 实战: 简单的记事本引用
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit,
QAction, QFileDialog, QMessageBox)
class Notepad(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.text_edit = QTextEdit(self)
self.setCentralWidget(self.text_edit)
self.create_actions()
self.create_menus()
self.setWindowTitle('简易记事本')
self.setGeometry(100, 100, 800, 600)
def create_actions(self):
# 文件菜单动作
self.new_action = QAction('新建', self)
self.new_action.setShortcut('Ctrl+N')
self.new_action.triggered.connect(self.new_file)
self.open_action = QAction('打开', self)
self.open_action.setShortcut('Ctrl+O')
self.open_action.triggered.connect(self.open_file)
self.save_action = QAction('保存', self)
self.save_action.setShortcut('Ctrl+S')
self.save_action.triggered.connect(self.save_file)
self.exit_action = QAction('退出', self)
self.exit_action.setShortcut('Ctrl+Q')
self.exit_action.triggered.connect(self.close)
# 编辑菜单动作
self.copy_action = QAction('复制', self)
self.copy_action.setShortcut('Ctrl+C')
self.copy_action.triggered.connect(self.text_edit.copy)
self.paste_action = QAction('粘贴', self)
self.paste_action.setShortcut('Ctrl+V')
self.paste_action.triggered.connect(self.text_edit.paste)
self.cut_action = QAction('剪切', self)
self.cut_action.setShortcut('Ctrl+X')
self.cut_action.triggered.connect(self.text_edit.cut)
def create_menus(self):
menubar = self.menuBar()
# 文件菜单
file_menu = menubar.addMenu('文件')
file_menu.addAction(self.new_action)
file_menu.addAction(self.open_action)
file_menu.addAction(self.save_action)
file_menu.addSeparator()
file_menu.addAction(self.exit_action)
# 编辑菜单
edit_menu = menubar.addMenu('编辑')
edit_menu.addAction(self.copy_action)
edit_menu.addAction(self.paste_action)
edit_menu.addAction(self.cut_action)
def new_file(self):
self.text_edit.clear()
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(self, '打开文件')
if filename:
try:
with open(filename, 'r') as f:
self.text_edit.setText(f.read())
except Exception as e:
QMessageBox.warning(self, '错误', f'无法打开文件: {e}')
def save_file(self):
filename, _ = QFileDialog.getSaveFileName(self, '保存文件')
if filename:
try:
with open(filename, 'w') as f:
f.write(self.text_edit.toPlainText())
except Exception as e:
QMessageBox.warning(self, '错误', f'无法保存文件: {e}')
if __name__ == '__main__':
app = QApplication(sys.argv)
notepad = Notepad()
notepad.show()
sys.exit(app.exec_())
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
# PyQt5核心组件
大多数组件位于PyQt5.QtWidgets,高级功能(如多媒体、网络)可能需要其他模块(如QtCore, QtGui, QtNetwork等)。QListView/QTableView/QTreeView需要搭配数据模型(如QStandardItemModel)使用,灵活性更高。其他安装扩展:
pip install PyQtWebEngine # 网页支持
pip install PyQtChart # 图表支持
2
| 组件类别 | 组件名称 | 所在模块 | 说明 |
|---|---|---|---|
| 基础窗口组件 | QWidget | QtWidgets | 所有用户界面对象的基类,可作为空白窗口或容器 |
| **** | QMainWindow | QtWidgets | 主窗口框架,包含菜单栏、工具栏、状态栏 |
| **** | QDialog | QtWidgets | 对话框基类,用于弹出窗口 |
| 布局管理 | QVBoxLayout | QtWidgets | 垂直布局管理器 |
| **** | QHBoxLayout | QtWidgets | 水平布局管理器 |
| **** | QGridLayout | QtWidgets | 网格布局管理器 |
| **** | QFormLayout | QtWidgets | 表单布局管理器(标签+输入框对) |
| 按钮类 | QPushButton | QtWidgets | 普通按钮 |
| **** | QRadioButton | QtWidgets | 单选按钮 |
| **** | QCheckBox | QtWidgets | 复选框 |
| **** | QToolButton | QtWidgets | 工具栏按钮(可带图标) |
| 输入控件 | QLineEdit | QtWidgets | 单行文本输入框 |
| **** | QTextEdit | QtWidgets | 多行富文本编辑器(支持HTML) |
| **** | QPlainTextEdit | QtWidgets | 多行纯文本编辑器 |
| **** | QSpinBox | QtWidgets | 数字调节框(整数) |
| **** | QDoubleSpinBox | QtWidgets | 数字调节狂(浮点数) |
| **** | QComboBox | QtWidgets | 下拉选择框 |
| **** | QDateEdit | QtWidgets | 日期选择框 |
| **** | QTimeEdit | QtWidgets | 时间选择框 |
| **** | QDateTimeEdit | QtWidgets | 日期时间选择框 |
| **** | QSlider | QtWidgets | 滑动条(水平/垂直) |
| **** | QDial | QtWidgets | 圆形旋钮控件 |
| 显示控件 | QLabel | QtWidgets | 文本/图片标签 |
| **** | QLCDNumber | QtWidgets | LCD数字显示屏 |
| **** | QProgressBar | QtWidgets | 进度条 |
| **** | QStatusBar | QtWidgets | 状态栏(通常用于QMainWindow) |
| 容器类 | QGroupBox | QtWidgets | 分组框(带标题的容器) |
| **** | QTabWidget | QtWidgets | 标签页容器 |
| **** | QStackedWidget | QtWidgets | 堆叠容器(每次显示一个子控件) |
| **** | QScrollArea | QtWidgets | 滚动区域容器 |
| **** | AMdiArea | QtWidgets | MDI(多文档界面)区域 |
| 列表/表格/树 | QListWidget | QtWidgets | 列表控件(含项管理) |
| **** | QTreeWidget | QtWidgets | 树形控件 |
| **** | QTableWidget | QtWidgets | 表格控件 |
| **** | QListView | QtWidgets | 列表视图(需搭配数据模型) |
| **** | QTableView | QtWidgets | 表格视图(需搭配数据模型) |
| **** | QTreeView | QtWidgets | 树形视图(需搭配数据模型) |
| 菜单/工具栏 | QMenuBar | QtWidgets | 菜单栏 |
| **** | QMenu | QtWidgets | 菜单(可包含子菜单和动作) |
| **** | QToolBar | QtWidgets | 工具栏 |
| **** | QAction | QtWidgets | 动作(用于菜单项、工具栏按钮等) |
| 对话框 | QFileDialog | QtWidgets | 文件选择对话框 |
| **** | QColorDialog | QtWidgets | 颜色选择对话框 |
| **** | QFontDialog | QtWidgets | 字体选择对话框 |
| **** | QInputDialog | QtWidgets | 输入对话框(文本、数字等) |
| **** | QMessageBox | QtWidgets | 消息提示框(警告、错误、询问等) |
| 图形视图 | QGraphicsView | QtWidgets | 图形视图框架(用于2D图形) |
| **** | QGraphicsScene | QtWidgets | 图形场景(配合QGraphicsView使用) |
| 其他功能组件 | QCalendarWidget | QtWidgets | 日历控件 |
| **** | QSplashScreen | QtWidgets | 启动画面 |
| **** | QSystemTrayIcon | QtWidgets | 系统托盘图标 |
| **** | QWebEngineView | QtWebEngineWidgets | 网页浏览器组件(需单独安装PyQtWebEngine) |
# 学习资源
- Python3 正则表达式
- re.match 函数
- re.search 方法
- re.match 与 re.search 的区别
- 检索和替换
- 正则表达式对象
- 正则表达式修饰符 - 可选标志
- 正则表达式模式
- 正则表达式实例
- Python3 CGI编程
- 什么是CGI
- 网页浏览
- CGI架构图
- Web服务器支持及配置
- 第一个CGI程序
- HTTP头部
- CGI环境变量
- GET和POST方法
- CGI中使用Cookie
- Cookie设置
- 检索Cookie信息
- 文件下载对话框
- Python3 MySQL(mysql-connector)
- 创建数据库连接
- 创建数据表
- 插入数据
- 查询数据
- 删除记录
- 更新表数据
- 删除表
- Python3 MySQL(PyMySQL)
- PyMySQL 安装
- 数据库连接
- 创建数据库表
- 数据库插入操作
- 数据库查询操作
- 数据库更新操作
- 删除操作
- 执行事务
- 错误处理
- Python3 PostgreSQL
- psycopg2
- SQLAlchemy
- Python3 网络编程
- 什么是Socket
- 简单实例
- Python Internet模块
- Python3 SMTP发送邮件
- 使用Python发送HTML格式的邮件
- Python发送带附件的邮件
- 在HTML文本中添加图片
- 使用第三方SMTP服务发送
- Python3 多线程
- Python 线程
- 线程模块
- 使用 threading 模块创建线程
- 线程同步
- 线程优先级队列(Queue)
- Python3 XML解析
- 什么是XML?
- Python对XML的解析
- Python使用ElementTree解析xml
- Python使用SAX解析XML
- Python解析XML实例
- 使用xml.dom解析xml
- Python3 JSON
- Python3 日期和时间
- 什么是时间元组
- 获取当前时间
- 获取格式化时间
- 格式化日期
- 获取某月日历
- Time模块
- 日历模块(Calendar)
- 其他相关模块和函数
- Python3 内置函数
- abs()
- dict()
- help()
- min()
- setattr()
- all()
- dir()
- hex()
- next()
- slice()
- any()
- divmod()
- id()
- object()
- sorted()
- ascii()
- enumerate()
- input()
- oct()
- staticmethod()
- bin()
- eval()
- int()
- open()
- str()
- bool()
- exec()
- isinstance()
- ord()
- sum()
- bytearray()
- filter()
- issubclass()
- pow()
- super()
- bytes()
- float()
- iter()
- print()
- tuple()
- callable()
- format()
- len()
- property()
- type()
- chr()
- frozenset()
- list()
- range()
- vars()
- classmethod()
- getattr()
- locals()
- repr()
- zip()
- compile()
- globals()
- map()
- reversed()
- __import__()
- complex()
- hasattr()
- max()
- round()
- reload()
- delattr()
- hash()
- memoryview()
- set()
- Python3 MongoDB
- PyMongo
- 创建数据库
- 创建集合
- 增、删、改、查等操作
- Python3 urllib
- urllib.request
- urllib.error
- urllib.parse
- urllib.robotparser
- Python uWSGI安装配置
- 第一个WSGI应用
- 结合Web服务器使用
- Python3 pip
- 扩展内容: Anaconda
- Python3 operator
- 运算符函数
- Python math
- math模块常量
- math模块方法
- Python requests
- request方法
- 附加请求参数
- Python random
- random 模块方法
- Python 有用的资源
- Python AI 绘画
- Windows 环境安装
- Civitai 介绍
- Python statistics
- 常用的统计函数
- 其他常用函数
- Python hashlib
- 常用方法
- 哈希对象方法
- 常见哈希算法
- Python 量化
- 获取历史股票数据
- 简单的数据分析和可视化
- 移动平均交叉策略回测
- Python pyecharts
- pyecharts安装
- pyecharts图表类型
- 创建第一个图表
- 设置全局配置项
- Python selenium库
- 安装Selenium和WebDriver
- 基本用法
- 简单的网页自动化
- selenium常用方法
- Python 爬虫
- BeautifulSoup
- 高级用法
- BeautifulSoup属性与方法
- Python Scrapy库
- 编写一个简单的Scrapy爬虫
- 常用方法
- 方法使用举例
- Python Markdown
- 将Markdown转换为HTML的步骤
- 扩展功能
- 参考资源
- Python sys模块
- sys 库的常用功能
- sys 模块常用属性
- sys 模块常用方法
- Python pikle模块
- Pickle模块的基本用法
- Pickle模块的注意事项
- 高级用法: 自定义对象的序列化
- pickle模块常用方法
- pickle模块协议版本
- Python subprocess模块
- subprocess模块的核心功能
- subprocess模块的高级用法
- subprocess模块的常用方法、类和参数
- Python queue模块
- 队列类型
- 常用方法
- 线程安全
- 常用的属性和方法
- Python StringIO模块
- 如何适用StringIO模块?
- 实际应用示例
- 常用类、方法和函数
- Python logging模块
- logging 模块的基本用法
- logging 模块的高级用法
- logging 模块常用的属性和方法
- Python datetime模块
- 使用 datetime 模块
- 常见应用场景
- 常用类、方法及属性
- Python re模块
- re 模块的基本用法
- 正则表达式的基本语法
- 实践练习
- Python csv模块
- 常用的属性和方法
- Python threading模块
- 如何使用threading模块
- 常用类、方法及属性
- 注意事项
- Python asyncio模块
- asyncio的核心概念
- asyncio的基本用法
- asyncio的应用场景
- 常用类、方法和函数
- 注意事项
- Python PyQt
- 第一个PyQt程序
- 常用PyQt组件
- 布局管理(QVBoxLayout)
- 信号与槽机制
- 使用Qt Designer
- 实战: 简单的记事本引用
- PyQt5核心组件
- 学习资源