Files
python-book/01.基础语法/10.常用模块.md
2025-09-12 14:46:36 +08:00

1469 lines
50 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 常用模块
## 序列化模块
将原本的字典、列表等内容转换成一个字符串的过程就叫做**序列化**
**序列化的目的**
1. 以某种存储形式使自定义对象持久化;
2. 将对象从一个地方传递到另一个地方。
3. 使程序更具维护性。
![img](10.常用模块/3856406007.png)
python可序列化的数据类型序列化出来之后的结果如下
| Python | JSON |
| ---------- | ------ |
| dict | object |
| list,tuple | array |
| str | string |
| int,float | number |
| True | true |
| False | false |
| None | null |
### json模块
Json模块提供了四个功能dumps、dump、loads、load
```python
import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)
# 序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)
dic2 = json.loads(str_dic)
print(type(dic2),dic2)
# 反序列化:将一个字符串格式的字典转换成一个字典
list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic)
print(type(str_dic),str_dic)
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2)
```
| 属性 | 说明 |
| :----------- | :----------------------------------------------------------- |
| Skipkeys | 1,默认值是False如果dict的keys内的数据不是python的基本类型,2,设置为False时就会报TypeError的错误。此时设置成True则会跳过这类key3,当它为True的时候所有非ASCII码字符显示为\uXXXX序列只需在dump时将ensure_ascii设置为False即可此时存入json的中文即可正常显示。 |
| indent | 是一个非负的整型如果是0就是顶格分行显示如果为空就是一行最紧凑显示否则会换行且按照indent的数值显示前面的空白分行显示这样打印出来的json数据也叫pretty-printed json |
| ensure_ascii | 当它为True的时候所有非ASCII码字符显示为\uXXXX序列只需在dump时将ensure_ascii设置为False即可此时存入json的中文即可正常显示。 |
| separators | 分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(,,:)这表示dictionary内keys之间用“,”隔开而KEY和value之间用“”隔开。 |
| sort_keys | 将数据根据keys的值进行排序 |
```python
import json
data = {'name':'陈松','sex':'female','age':88}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)
```
json.dump和json.load不常用主要是针对文件操作进行序列化和反序列化
```python
序列化
import json
v = {'k1':'yh','k2':'小马过河'}
f = open('xiaoma.txt',mode='w+',encoding='utf-8') #文件不存在就会生成
val = json.dump(v,f)
print(val)
f.close()
----------------结果
None
#dump将内容序列化并写入打开的文件中。
反序列化
import json
f = open('xiaoma.txt',mode='r',encoding='utf-8')
data = json.load(f)
f.close()
print(data,type(data))
---------------结果:
{'k1': 'yh', 'k2': '小马过河'} <class 'dict'>
```
### pickle模块
| 模块 | 描述 |
| ------ | :------------------------------------------------- |
| json | 用于字符串 和 python数据类型间进行转换 |
| pickle | 用于python特有的类型 和 python的数据类型间进行转换 |
pickle模块提供了四个功能dumps、dump(序列化、loads反序列化、load
**不仅可以序列化字典,列表...可以把python中任意的数据类型序列化**
json模块和picle模块都有 dumps、dump、loads、load四种方法而且用法一样。
不同的是json模块序列化出来的是通用格式其它编程语言都认识就是普通的字符串
而picle模块序列化出来的只有python可以认识其他编程语言不认识的保存下来的是二进制文件
不过picle可以序列化函数但是其他文件想用该函数在该文件中需要有该文件的定义定义和参数必须相同内容可以不同
```python
import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)
dic2 = pickle.loads(str_dic)
print(dic2)
import time
struct_time = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()
f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
```
### shelve模块
shelve也是python提供给我们的序列化工具比pickle用起来更简单一些。
shelve只提供给我们一个open方法是用key来访问的使用起来和字典类似。
参考博客
https://www.cnblogs.com/sui776265233/p/9225164.html
```python
import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10,'str':'hello','float':0.123}
f.close()
f1 = shelve.open('shelve_file')
ret = f1['key']
f1.close()
print(ret)
```
这个模块有个限制它不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道我们的应用如果只进行读操作我们可以让shelve通过只读方式打开DB
```python
import shelve
f1 = shelve.open('shelve_file',flag='r')
ret = f1['key']
f1.close()
print(ret)
```
由于shelve在默认情况下是不会记录待持久化对象的任何修改的所以我们在shelve.open()时候需要修改默认参数,否则对象的修改不会保存。
```python
import shelve
f1 = shelve.open('shelve_file')
print(f1['key'])
f1['key']['k1'] = 'v1'
f1.close()
f2 = shelve.open('shelve_file',writeback=True)
print(f2['key'])
f2['key']['k1'] = 'hello'
f2.close()
```
**使用shelve模块实现简单的数据库**
```python
# 简单的数据库
import sys,shelve
def print_help():
'存储(增加)、查找、更新(修改)、循环打印、删除、退出、帮助'
print('The available commons are: ')
print('store : Stores information about a person')
print('lookup : Looks up a person from ID numbers')
print("update : Update a person's information from ID number")
print('print_all: Print all informations')
print("delete : Delete a person's information from ID number")
print('quit : Save changes and exit')
print('? : Print this message')
def store_people(db):
pid = input('Please enter a unique ID number: ')
person = {}
person['name'] = input('Please enter the name: ')
person['age'] = input('Please enter the age: ')
person['phone'] = input('Please enter the phone: ')
db[pid] = person
print("Store information: pid is %s, information is %s" % (pid, person))
def lookup_people(db):
pid = input('Please enter the number: ')
field = input('What would you like to know? (name, age, phone) ')
if pid in db.keys():
value = db[pid][field]
print("Pid %s's %s is %s" % (pid, field, value))
else:
print('Not found this number')
def update_people(db):
pid = input('Please enter the number: ')
field = input('What would you like to update? (name, age, phone) ')
newvalue = input('Enter the new information: ')
if pid in db.keys():
value = db[pid]
value[field] = newvalue
print("Pid %s's %s update information is %s" % (pid, field, newvalue))
else:
print("Not found this number, can't update")
def delete_people(db):
pid = input('Please enter the number: ')
if pid in db.keys():
del db[pid]
print("pid %s's information delete done" % pid)
else:
print( "Not found this number, can't delete")
def print_all_people(db):
print( 'All information are: ')
for key, value in db.items():
print(key, value)
def enter_cmd():
cmd = input('Please enter the cmd(? for help): ')
cmd = cmd.strip().lower()
return cmd
def main():
database = shelve.open('database201803.dat', writeback=True)
try:
while True:
cmd = enter_cmd()
if cmd == 'store':
store_people(database)
elif cmd == 'lookup':
lookup_people(database)
elif cmd == 'update':
update_people(database)
elif cmd == 'print_all':
print_all_people(database)
elif cmd == 'delete':
delete_people(database)
elif cmd == '?':
print_help()
elif cmd == 'quit':
return
finally:
database.close()
if __name__ == '__main__':
main()
```
## hashlib模块
Python的hashlib提供了常见的摘要算法如MD5SHA1等等。
什么是摘要算法呢摘要算法又称哈希算法、散列算法。它通过一个函数把任意长度的数据转换为一个长度固定的数据串通常用16进制的字符串表示
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过就是因为摘要函数是一个单向函数计算f(data)很容易但通过digest反推data却非常困难。而且对原始数据做一个bit的修改都会导致计算出的摘要完全不同。
```python
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
```
如果数据量很大可以分块多次调用update(),最后计算的结果是一样的
```python
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 '.encode('utf-8'))
md5.update('in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
```
MD5是最常见的摘要算法速度很快生成结果是固定的128 bit字节通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1调用SHA1和调用MD5完全类似
```python
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use md5 '.encode('utf-8'))
sha1.update('in python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
```
### 摘要算法应用
任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中
```sql
name | password
--------+----------
michael | 123456
bob | abc999
alice | alice2008
```
如果使用md5来将保护密码那么就是这样
```sql
username | password
---------+---------------------------------
michael | e10adc3949ba59abbe56e057f20f883e
bob | 878ef96e86145580c38c87f0410ad153
alice | 99b1c2188db85afee403b1536010c2c9
```
有很多md5撞库工具可以轻松的将简单密码给碰撞出来
所以要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5这一方法通过对原始口令加一个复杂字符串来实现俗称“加盐”
经过Salt处理的MD5口令只要Salt不被黑客知道即使用户输入简单口令也很难通过MD5反推明文口令。
但是如果有两个用户都使用了相同的简单口令比如123456在数据库中将存储两条相同的MD5值这说明这两个用户的口令是一样的。
如果假定用户无法修改登录名就可以通过把登录名作为Salt的一部分来计算MD5从而实现相同口令的用户也存储不同的MD5。
显示进度条
```python
import time
for i in range(0,101,2):
time.sleep(0.1)
char_num = i//2
per_str = '\r%s%% : %s\n' % (i, '*' * char_num) \
if i == 100 else '\r%s%% : %s' % (i,'*'*char_num)
print(per_str,end='', flush=True)
```
## configparser模块
该模块适用于配置文件的格式与windows ini文件类似可以包含一个或多个节section每个节可以有多个参数键=值)。
常见的文档格式
```
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
```
使用python生成一个这样的文件
```python
import configparser
conf = configparser.ConfigParser()
conf['DEFAULT'] = {'ServerAliveInterval':'45',
'Compression':'yes',
'CompressionLevel':'9',
'ForwardX11':'yes'
}
conf['bitbucket.org'] = {'User':'hg'}
conf['topsecret.server.com'] = {'Port':'50022',
'ForwardX11':'no'
}
with open('config','w') as config:
conf.write(config)
```
查找
```python
import configparser
conf = configparser.ConfigParser()
conf['DEFAULT'] = {'ServerAliveInterval':'45',
'Compression':'yes',
'CompressionLevel':'9',
'ForwardX11':'yes'
}
conf['bitbucket.org'] = {'User':'hg'}
conf['topsecret.server.com'] = {'Port':'50022',
'ForwardX11':'no'
}
print('bitbucket.org' in conf)
print('bitbucket.com' in conf)
print(conf['bitbucket.org']['user'])
print(conf['DEFAULT']['Compression'])
for key in conf['bitbucket.org']:
print(key) # DEFAULT的键也会出现
print(conf.options('bitbucket.org'))
# 同for循环,找到'bitbucket.org'下所有键
print(conf.items('bitbucket.org'))
# 找到'bitbucket.org'下所有键值对
print(conf.get('bitbucket.org','compression'))
```
增删改操作
```python
import configparser
conf = configparser.ConfigParser()
conf.read('config')
conf.add_section('yuan') # 添加键
conf.remove_section('bitbucket.org') # 删除键
conf.remove_option('topsecret.server.com','forwardx11') # 移除条目
conf.set('topsecret.server.com','k1','11111') # 在对应键下加上条目
conf.set('yuan','k2','22222')
conf.write(open('config.new','w')) # 写入文件
```
## logging模块
参考博客:
https://blog.csdn.net/pansaky/article/details/90710751
### 函数式简单配置
```python
import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
```
默认情况下Python的logging模块将日志打印到了标准输出中且只显示了大于等于WARNING级别的日志这说明默认的日志级别设置为WARNING日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG默认的日志格式为日志级别Logger名称用户输出消息。
```python
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='test.log',
filemode='w')
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
```
参数解释
- logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为可用参数有
- filename用指定的文件名创建FiledHandler这样日志会被存储在指定的文件中。
- filemode文件打开方式在指定了filename时使用这个参数默认值为“a”还可指定为“w”。
- format指定handler使用的日志显示格式。
- datefmt指定日期时间格式。
- level设置rootlogger后边会讲解具体概念的日志级别
- stream用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open- (test.log,w))默认为sys.stderr。若同时列出了filename和stream两个参数则stream参数会被忽略。
- format参数中可能用到的格式化串
- %(name)s Logger的名字
- %(levelno)s 数字形式的日志级别
- %(levelname)s 文本形式的日志级别
- %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
- %(filename)s 调用日志输出函数的模块的文件名
- %(module)s 调用日志输出函数的模块名
- %(funcName)s 调用日志输出函数的函数名
- %(lineno)d 调用日志输出函数的语句所在的代码行
- %(created)f 当前时间用UNIX标准的表示时间的浮 点数表示
- %(relativeCreated)d 输出日志信息时的自Logger创建以 来的毫秒数
- %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
- %(thread)d 线程ID。可能没有
- %(threadName)s 线程名。可能没有
- %(process)d 进程ID。可能没有
- %(message)s用户输出的消息
### logger对象配置
```python
import logging
logger = logging.getLogger()
# 创建一个handler用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8')
# 再创建一个handler用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
```
logging库提供了多个组件Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口Handler发送日志到适当的目的地Filter提供了过滤日志信息的方法Formatter指定日志显示格式。另外可以通过logger.setLevel(logging.Debug)设置级别,当然也可以通过fh.setLevel(logging.Debug)单对文件流设置某个级别。
### logger的配置文件
```python
"""
logging配置
"""
import os
import logging.config
# 定义三种日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
logfile_name = 'all2.log' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码再也不用担心中文log乱码了
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上更高level的logger传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态
if __name__ == '__main__':
load_my_logging_cfg()
```
```
注意:
#1、有了上述方式我们的好处是所有与logging模块有关的配置都写到字典中就可以了更加清晰方便管理
#2、我们需要解决的问题是
1、从字典加载配置logging.config.dictConfig(settings.LOGGING_DIC)
2、拿到logger对象来产生日志
logger对象都是配置到字典的loggers 键对应的子字典中的
按照我们对logging模块的理解要想获取某个东西都是通过名字也就是key来获取的
于是我们要获取不同的logger对象就是
logger=logging.getLogger('loggers子字典的key名')
但问题是如果我们想要不同logger名的logger对象都共用一段配置那么肯定不能在loggers子字典中定义n个key
'loggers': {
'l1': {
'handlers': ['default', 'console'], #
'level': 'DEBUG',
'propagate': True, # 向上更高level的logger传递
},
'l2: {
'handlers': ['default', 'console' ],
'level': 'DEBUG',
'propagate': False, # 向上更高level的logger传递
},
'l3': {
'handlers': ['default', 'console'], #
'level': 'DEBUG',
'propagate': True, # 向上更高level的logger传递
},
}
#我们的解决方式是定义一个空的key
'loggers': {
'': {
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': True,
},
}
这样我们再取logger对象时
logging.getLogger(__name__)不同的文件__name__不同这保证了打印日志时标识信息不同但是拿着该名字去loggers里找key名时却发现找不到于是默认使用key=''的配置
```
## collections模块
在内置数据类型dict、list、set、tuple的基础上collections模块还提供了几个额外的数据类型Counter、deque、defaultdict、namedtuple和OrderedDict等。
1. namedtuple: 生成可以使用名字来访问元素内容的tuple
2. deque: 双端队列,可以快速的从另外一侧追加和推出对象
3. Counter: 计数器,主要用来计数
4. OrderedDict: 有序字典
5. defaultdict: 带有默认值的字典
### namedtuple
```python
from collections import namedtuple
point = namedtuple('point',['x','y'])
p = point(1,2)
print(p.x)
```
一个点的二维坐标就可以表示成,但是,看到(1, 2)很难看出这个tuple是用来表示一个坐标的。
这时namedtuple就派上了用场
### deque
使用list存储数据时按索引访问元素很快但是插入和删除元素就很慢了因为list是线性存储数据量大的时候插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表适合用于队列和栈
```python
from collections import deque
q = deque(['a','b','c'])
q.append('x')
q.appendleft('y')
print(q)
```
deque除了实现list的append()和pop()外还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。
### OrderedDict
```python
from collections import OrderedDict
d = dict([('a',1),('b',2),('c',3)])
print(d)
od = OrderedDict([('a',1),('b',2),('c',3)])
print(od)
```
注意OrderedDict的Key会按照插入的顺序排列不是Key本身排序
### defaultdict
有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中将小于 66 的值保存至第二个key的值中。
即: {'k1': 大于66 , 'k2': 小于66}
```python
li = [11,22,33,44,55,77,88,99,90]
result = {}
for row in li:
if row < 66:
if 'key1' not in result:
result['key1']=[]
result['key1'].append(row)
else:
if 'key2' not in result:
result['key2']=[]
result['key2'].append(row)
print(result)
```
```python
from collections import defaultdict
li = [11,22,33,44,55,77,88,99,90]
result=defaultdict(list)
for row in li:
if row > 66:
result['key1'].append(row)
else:
result['key2'].append(row)
print(result)
```
### counter
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型以字典的键值对形式存储其中元素作为key其计数作为value。
```python
from collections import Counter
c = Counter('qazxswqazxswqazxswsxaqwsxaqws')
print(c)
```
## 时间有关的模块
常用方法
- time.sleep(secs)
- (线程)推迟指定的时间运行。单位为秒。
- time.time()
- 获取当前时间戳
表示时间的三种方式
在Python中通常有这三种方式来表示时间时间戳、元组(struct_time)、格式化的时间字符串:
1. **时间戳**(timestamp) 通常来说时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”返回的是float类型。
2. 格式化的时间字符串(Format String) 1999-12-06
| 格式符 | 描述 |
| ------ | :---------------------------------------- |
| %y | 两位数的年份表示00-99 |
| %Y | 四位数的年份表示000-9999 |
| %m | 月份01-12 |
| %d | 月内中的一天0-31 |
| %H | 24小时制小时数0-23 |
| %I | 12小时制小时数01-12 |
| %M | 分钟数00=59 |
| %S | 秒00-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 | 当前时区的名称 |
| %% | %号本身 |
3. 元组(struct_time) struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)
| 索引Index | 属性Attribute | 值Values |
| :------------ | :------------------------ | :----------------- |
| 0 | tm_year | 比如2011 |
| 1 | tm_mon | 1月12日 |
| 2 | tm_mday | 1月31日 |
| 3 | tm_hour | 0 - 23 |
| 4 | tm_min | 0 - 59 |
| 5 | tm_sec | 0 - 60 |
| 6 | tm_wdayweekday | 0 - 60表示周一 |
| 7 | tm_yday一年中的第几天 | 1 - 366 |
| 8 | tm_isdst是否是夏令时 | 默认为0 |
```python
import time
# 第一种时间格式,时间戳的形式
print(time.time())
# 第二种时间格式,格式化的时间
print(time.strftime('%Y-%m-%d %X'))
print(time.strftime('%Y-%m-%d %H-%M-%S'))
# 第三种时间格式,结构化的时间,是一个元组
print(time.localtime())
```
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
**几种格式之间的转换**
![img](10.常用模块/987936105.png)
```python
import time
# 格式化时间 ----> 结构化时间
ft = time.strftime('%Y/%m/%d %H:%M:%S')
st = time.strptime(ft,'%Y/%m/%d %H:%M:%S')
print(st)
# 结构化时间 ---> 时间戳
t = time.mktime(st)
print(t)
# 时间戳 ----> 结构化时间
t = time.time()
st = time.localtime(t)
print(st)
# 结构化时间 ---> 格式化时间
ft = time.strftime('%Y/%m/%d %H:%M:%S',st)
print(ft)
```
![img](10.常用模块/838049513.png)
```python
import time
#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
print(time.asctime(time.localtime(1550312090.4021888)))
#时间戳 --> %a %d %d %H:%M:%S %Y串
#time.ctime(时间戳) 如果不传参数,直接返回当前时间的格式化串
print(time.ctime(1550312090.4021888))
```
计算时间差
```python
import time
start_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
end_time=time.mktime(time.strptime('2019-09-12 11:00:50','%Y-%m-%d %H:%M:%S'))
dif_time=end_time-start_time
struct_time=time.gmtime(dif_time)
print('过去了%d%d%d%d小时%d分钟%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
struct_time.tm_mday-1,struct_time.tm_hour,
struct_time.tm_min,struct_time.tm_sec))
```
### datatime模块
```python
# datatime模块
import datetime
now_time = datetime.datetime.now() # 现在的时间
# 只能调整的字段weeks days hours minutes seconds
print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后
print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三周前
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后
print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后
print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前
print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前
print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后
print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前
print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后
current_time = datetime.datetime.now()
# 可直接调整到指定的 年 月 日 时 分 秒 等
print(current_time.replace(year=1977)) # 直接调整到1977年
print(current_time.replace(month=1)) # 直接调整到1月份
print(current_time.replace(year=1989,month=4,day=25)) # 1989-04-25 18:49:05.898601
# 将时间戳转化成时间
print(datetime.date.fromtimestamp(1232132131)) # 2009-01-17
```
## random模块
用来生成随机数模块
```python
import random
print(random.random()) # 大于0且小于1之间的小数
print(random.uniform(1,3)) # 大于1小于3的小数
print(random.randint(1,5)) # 大于等于1且小于等于5之间的整数
print(random.randrange(1,10,2)) # 大于等于1且小于10之间的奇数
ret = random.choice([1,'23',[4,5]]) # 1或者23或者[4,5]
print(ret)
a,b = random.sample([1,'23',[4,5]],2) # 列表元素任意2个组合
print(a,b)
item = [1,3,5,7,9]
random.shuffle(item) # 打乱次序
print(item)
```
生成随机验证码
```python
import random
def v_code():
code = ''
for i in range(5):
num=random.randint(0,9)
alf=chr(random.randint(65,90))
add=random.choice([num,alf])
code="".join([code,str(add)])
return code
print(v_code())
```
## OS模块
os模块是与操作系统交互的一个接口
当前执行这个python文件的工作目录相关的
**工作路径**
| 方法 | 描述 |
| :------------------ | :----------------------------------------------- |
| os.getcwd() | 获取当前工作目录即当前python脚本工作的目录路径 |
| os.chdir("dirname") | 改变当前脚本工作目录相当于shell下cd |
| os.curdir | 返回当前目录: ('.') |
| os.pardir | 获取当前目录的父目录字符串名:('..') |
**文件夹相关**
| 方法 | 描述 |
| :------------------------------- | :----------------------------------------------------------- |
| os.makedirs('dirname1/dirname2') | 可生成多层递归目录 |
| os.removedirs('dirname1') | 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 |
| os.mkdir('dirname') | 生成单级目录相当于shell中mkdir dirname |
| os.rmdir('dirname') | 删除单级空目录若目录不为空则无法删除报错相当于shell中rmdir dirname |
| os.listdir('dirname') | 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 |
**文件相关**
| 方法 | 描述 |
| :----------------------------- | :---------------- |
| os.remove() | 删除一个文件 |
| os.rename("oldname","newname") | 重命名文件/目录 |
| os.stat('path/filename') | 获取文件/目录信息 |
操作系统差异相关
| 方法 | 描述 |
| :--------- | :------------------------------------------------------ |
| os.sep | 输出操作系统特定的路径分隔符win下为"\\\",Linux下为"/" |
| os.linesep | 输出当前平台使用的行终止符win下为"\t\n",Linux下为"\n" |
| os.pathsep | 输出用于分割文件路径的字符串 win下为;,Linux下为: |
| os.name | 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' |
执**行系统命令相关**
| 方法 | 描述 |
| :----------------------------- | :-------------------------- |
| os.system("bash command") | 运行shell命令直接显示 |
| os.popen("bash command).read() | 运行shell命令获取执行结果 |
| os.environ | 获取系统环境变量 |
**path系列和路径相关**
| 方法 | 描述 |
| :---------------------------------- | :----------------------------------------------------------- |
| os.path.abspath(path) | 返回path规范化的绝对路径 |
| os.path.split(path) | 将path分割成目录和文件名二元组返回 |
| os.path.dirname(path) | 返回path的目录。其实就是os.path.split(path)的第一个元素 |
| os.path.basename(path) | 返回path最后的文件名。如何path以或\结尾那么就会返回空值即os.path.split(path)的第二个元素。 |
| os.path.exists(path) | 如果path存在返回True如果path不存在返回False |
| os.path.isabs(path) | 如果path是绝对路径返回True |
| os.path.isfile(path) | 如果path是一个存在的文件返回True。否则返回False |
| os.path.isdir(path) | 如果path是一个存在的目录则返回True。否则返回False |
| os.path.join(path1[, path2[, ...]]) | 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 |
| os.path.getatime(path) | 返回path所指向的文件或者目录的最后访问时间 |
| os.path.getmtime(path) | 返回path所指向的文件或者目录的最后修改时间 |
| os.path.getsize(path) | 返回path的大小 |
```python
import os
print(os.stat('.\config')) # 当前目录下的config文件的信息
# 运行结果
# os.stat_result(st_mode=33206, st_ino=2814749767208887, st_dev=1788857329, st_nlink=1, st_uid=0, st_gid=0, st_size=185, st_atime=1550285376, st_mtime=1550285376, st_ctime=1550285376)
```
| 模式 | 描述 |
| :------- | :----------------------------------------------------------- |
| st_mode | inode 保护模式 |
| st_ino | inode 节点号 |
| st_dev | inode 驻留的设备 |
| st_nlink | inode 的链接数 |
| st_uid | 所有者的用户ID |
| st_gid | 所有者的组ID |
| st_size | 普通文件以字节为单位的大小;包含等待某些特殊文件的数据 |
| st_atime | 上次访问的时间 |
| st_mtime | 最后一次修改的时间 |
| st_ctime | 由操作系统报告的"ctime"。在某些系统上如Unix是最新的元数据更改的时间在其它系统上如Windows是创建时间详细信息参见平台的文档 |
## sys模块
sys模块是与python解释器交互的一个接口
| 方法 | 描述 |
| :----------- | :----------------------------------------------------- |
| sys.argv | 命令行参数List第一个元素是程序本身路径 |
| sys.exit(n) | 退出程序正常退出时exit(0),错误退出sys.exit(1) |
| sys.version | 获取Python解释程序的版本信息 |
| sys.path | 返回模块的搜索路径初始化时使用PYTHONPATH环境变量的值 |
| sys.platform | 返回操作系统平台名称 |
## re模块
### 正则表达式
正则就是用一些具有特殊含义的符号组合到一起称为正则表达式来描述字符或者字符串的方法。或者说正则就是用来描述一类事物的规则。在Python中它内嵌在Python中并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
| 元字符 | 匹配内容 |
| :-------- | :----------------------------------------------------------- |
| \w | 匹配字母(包含中文)或数字或下划线 |
| \W | 匹配非字母(包含中文)或数字或下划线 |
| \s | 匹配任意的空白符 |
| \S | 匹配任意非空白符 |
| \d | 匹配数字 |
| \D | 匹配非数字 |
| \A | 从字符串开头匹配 |
| \z | 匹配字符串的结束,如果是换行,只匹配到换行前的结果 |
| \n | 匹配一个换行符 |
| \t | 匹配一个制表符 |
| ^ | 匹配字符串的开始 |
| $ | 匹配字符串的结尾 |
| . | 匹配任意字符除了换行符当re.DOTALL标记被指定时则可以匹配包括换行符的任意字符。 |
| [...] | 匹配字符组中的字符 |
| [^...] | 匹配除了字符组中的字符的所有字符 |
| * | 匹配0个或者多个左边的字符。 |
| + | 匹配一个或者多个左边的字符。 |
| | 匹配0个或者1个左边的字符非贪婪方式。 |
| {n} | 精准匹配n个前面的表达式。 |
| {n,m} | 匹配n到m次由前面的正则表达式定义的片段贪婪方式 |
| 出现次数? | 非贪婪模式,默认的贪婪模式,会尽可能多的匹配字符,非贪婪模式只会尽少匹配字符 |
| () | 匹配括号内的表达式,也表示一个组 |
#### 单字符匹配
```python
import re
print(re.findall('\w','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\W','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\s','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\S','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\d','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\D','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\A上大','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('^上大','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('666\z','上大人123asdfg%^&*(_ \t \n)666'))
print(re.findall('666\Z','上大人123asdfg%^&*(_ \t \n)666'))
print(re.findall('666$','上大人123asdfg%^&*(_ \t \n)666'))
print(re.findall('\n','上大人123asdfg%^&*(_ \t \n)'))
print(re.findall('\t','上大人123asdfg%^&*(_ \t \n)'))
```
#### 重复匹配
```python
import re
print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb'))
print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb',re.DOTALL))
print(re.findall('a?b', 'ab aab abb aaaab a牛b aba**b'))
print(re.findall('a*b', 'ab aab aaab abbb'))
print(re.findall('ab*', 'ab aab aaab abbbbb'))
print(re.findall('a+b', 'ab aab aaab abbb'))
print(re.findall('a{2,4}b', 'ab aab aaab aaaaabb'))
print(re.findall('a.*b', 'ab aab a*()b'))
print(re.findall('a.*?b', 'ab a1b a*()b, aaaaaab'))
# .*? 此时的?不是对左边的字符进行0次或者1次的匹配,
# 而只是针对.*这种贪婪匹配的模式进行一种限定:告知他要遵从非贪婪匹配 推荐使用!
# []: 括号中可以放任意一个字符,一个中括号代表一个字符
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
# ^ 在[]中表示取反的意思.
print(re.findall('a.b', 'a1b a3b aeb a*b arb a_b'))
print(re.findall('a[abc]b', 'aab abb acb adb afb a_b'))
print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b'))
print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b'))
print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b'))
print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b'))
print(re.findall('a[*-+]b','a-b a*b a+b a/b a6b'))
print(re.findall('a[-*+]b','a-b a*b a+b a/b a6b'))
print(re.findall('a[^a-z]b', 'acb adb a3b a*b'))
# 分组:() 制定一个规则,将满足规则的结果匹配出来
print(re.findall('(.*?)_sb', 'cs_sb zhao_sb 日天_sb'))
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))
print(re.findall('compan(y|ies)','Too many companies have gone bankrupt, and the next one is my company'))
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))
# 分组() 中加入?: 表示将整体匹配出来而不只是()里面的内容
```
#### 常用方法举例
```python
import re
# findall 全部找到返回一个列表
print(re.findall('a','aghjmnbghagjmnbafgv'))
# search 只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配则返回None
print(re.search('sb|chensong', 'chensong sb sb demon 日天'))
print(re.search('chensong', 'chensong sb sb barry 日天').group())
# matchNone,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
print(re.match('sb|chensong', 'chensong sb sb demon 日天'))
print(re.match('chensong', 'chensong sb sb barry 日天').group())
# split 分割 可按照任意分割符进行分割
print(re.split('[:,;]','1;3,c,a3'))
# sub 替换
print(re.sub('帅哥','sb','陈松是一个帅哥'))
# complie 根据包含的正则表达式的字符串创建模式对象。可以实现更有效率的匹配。
obj = re.compile('\d{2}')
print(obj.search('abc123eeee').group())
print(obj.findall('1231232aasd'))
ret = re.finditer('\d','asd123affess32432') # finditer返回一个存放匹配结果的迭代器
print(ret)
print(next(ret).group())
print(next(ret).group())
print([i.group() for i in ret])
```
#### 命名分组举例
命名分组匹配
```python
import re
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
print(ret.group('tag_name'))
print(ret.group())
ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
# 如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# 获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())
```
### 图片爬虫
- 该网站主页是缩略图,点进去才是高清图,所以先解析高清图的地址,然后再下载高清图
```python
from urllib.request import urlopen, Request, build_opener, install_opener, HTTPSHandler
import re, os
def get_img(url):
# 下载点开地址后的高清图
req = Request(url, headers={'User-Agent': user_agent})
try:
content = urlopen(req).read().decode('utf-8')
except :
return
link = re.findall('<img class="img-lightbox img-zoom-in" src="(.*?)" alt="(.*?)" width="\d+?" height="\d+?">',
content)
tmp.extend(link)
print(f"{link[-1]}地址解析成功")
def download_img(img_url, img_name):
img_name = img_name.replace(' ', '_')
# 创建下载图片的文件夹
path = os.getcwd() + os.sep + 'images'
if not os.path.isdir(path):
os.makedirs(path)
req = Request(img_url, headers={'User-Agent': user_agent})
try:
data = urlopen(req).read()
except:
return
if re.search('.jpg', img_url):
file_ext = ".jpg"
elif re.search('.png', img_url):
file_ext = ".png"
elif re.search('.gif', img_url):
file_ext = ".gif"
else:
return "当前图片格式不正确"
with open(f'{path}{os.sep}{img_name}{file_ext}', 'wb') as f:
f.write(data)
print(img_name, "已下载")
img_page_url = []
tmp = []
user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
pages = input("你要下载多少页:")
if pages.isdigit():
pages = int(pages)
else:
raise "你输入不是数字!"
for i in range(1, pages + 1):
url = f'https://www.bizhihui.com/dongman/{i}/?order=time'
req = Request(url, headers={'User-Agent': user_agent})
try:
content = urlopen(req).read().decode('utf-8')
except:
continue
link = re.findall('<a class="item-img" href="(.*?)" title=".*" target="_blank">', content)
img_page_url.extend(link)
print(img_page_url)
for i in img_page_url:
get_img(i)
print(tmp)
for j in tmp:
if j:
download_img(j[0], j[-1])
else:
continue
input("下载完成,按回车结束....")
```
## shutil模块
高级的 文件、文件夹、压缩包 处理模块
### shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
```python
import shutil
shutil.copyfileobj(open('config','r'),open('config.new','w'))
```
### shutil.copyfile(src, dst)
拷贝文件
```python
import shutil
shutil.copyfile('config','config1') # 目标文件无需存在
```
### shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变
```python
import shutil
shutil.copymode('config','config1') # 目标文件必须存在
```
### shutil.copystat(src, dst)
仅拷贝状态的信息包括mode bits, atime, mtime, flags
```python
import shutil
shutil.copystat('config','config1') # 目标文件必须存在
```
### shutil.copy(src, dst)
拷贝文件和权限
```python
import shutil
shutil.copy('config','config1') # 目标文件必须存在
```
### shutil.ignore_patterns(*patterns)
### shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹
```python
import shutil
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 目标目录不能存在注意对folder2目录父级目录要有可写权限ignore的意思是排除
# 硬链接
shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
# 软链接
```
### shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件
```python
import shutil
shutil.rmtree('folder1')
```
### shutil.move(src, dst)
递归的去移动文件它类似mv命令其实就是重命名。
```python
import shutil
shutil.move('folder1', 'folder3')
```
### shutil.make_archive(base_name, format,...)
- 创建压缩包并返回文件路径例如zip、tar
- base_name 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
- 如 data_bak =>保存至当前路径
- 如:/tmp/data_bak =>保存至/tmp/
- format 压缩包种类“zip”, “tar”, “bztar”“gztar”
- root_dir 要压缩的文件夹路径(默认当前目录)
- owner 用户,默认当前用户
- group 组,默认当前组
- logger 用于记录日志通常是logging.Logger对象
```python
# 将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
# 将 /data下的文件打包放置 /tmp/目录
import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
```
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的
```python
import zipfile
# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()
# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()
```
```python
import tarfile
# 压缩文件
t = tarfile.open('/tmp/egon.tar','w')
t.add('/test1/a.py',arcname='a.bak')
t.add('/test1/b.py',arcname='b.bak')
t.close()
# 解压缩文件
t = tarfile.open('/tmp/egon.tar','r')
t.extractall('/egon')
t.close()
```