(第六集)python包与常用模块

本文详细介绍了Python中的模块概念,包括模块的定义、分类及其在实际开发中的应用。文章深入探讨了import语句的工作原理,模块的搜索路径,以及如何使用from ... import ... 语句来导入特定的功能。此外,还讲解了包的概念及其组织结构,以及如何在Python项目中有效地管理和使用模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模块介绍


一个模块就是一个包含了一组功能的python文件,比如spam.py,模块名为spam,可以通过import spam使用。
模块分4个通用类别:
  1 使用python编写的.py文件
  2 已被编译为共享库或DLL的C或C++扩展
  3 把一系列模块组织到一起的文件夹(注:文件夹下有一个init.py文件,该文件夹称之为包)
  4 使用C编写并链接到python解释器的内置模块

模块的意义

从文件级别组织程序,更方便管理。将程序分成一个个的文件,可以把这些文件当做脚本去执行,还可把他们当模块来导入到其他的模块中,实现了功能的重复利用。
提升开发效率。

使用模块之import


import的使用

同一个模块import很多次,仅是对已经加载到内存中的模块对象增加一次引用,不会重新执行模块内的语句,重复导入会直接引用内存中已经加载好的结果:
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',
当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam
'''
执行结果:
from the spam.py
'''

# 从sys.module中可找到当前已经加载的模块

导入模块时执行的内容(以导入spam模块为例)

1.为源文件(spam模块)创建新的名称空间.

2.在新创建的命名空间中执行模块中包含的代码,见初始导入import spam

3.创建名字spam来引用该命名空间.

被导入模块有独立的名称空间

每个模块都是一个独立的名称空间,在编写自定义的模块时,无需担心定义在自己模块中的全局变量会在被导入时,与使用者的全局变量冲突.

为模块名起别名(编写可扩展的代码)

import spam as sm
print(sm.money)

根据用户的输入,选择不同的sql功能.

#定义模块mysql.py
def sqlparse():
    print('from mysql sqlparse')
#定义模块oracle.py
def sqlparse():
    print('from oracle sqlparse')

#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db

db.sqlparse() 

在一行导入多个模块

import sys,os,re

使用模块之from … import…


from...import...的使用:
from spam import read1,read2

from…import 与import的对比

将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字,无需加前缀:spam.

好处:使用方便
坏处:容易与当前执行文件中的名字冲突

验证

  • 验证一
当前位置直接使用read1和read2执行时,仍以spam.py文件全局名称空间:
#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=1000
read1() # 执行spam模块中read1函数中的money变量


#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()  # 执行spam模块的read2函数
  • 验证二:
#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1() #执行本模块中的read1()函数
'''
执行结果:
from the spam.py
  • 验证三:
from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000

from…import *(不推荐)

所有的不是以下划线(_)开头的名字都导入到当前位置,会覆盖掉之前已定义的名字,且可读性差

模块的重载


性能原因,每个模块只被导入一次,放入字典sys.module中.python不支持重新加载或卸载之前导入的模块.改变了模块的内容,必须重启程序.


# aa.py文件:
def func1():
    print('func1')
import time,importlib
import aa
time.sleep(20)
importlib.reload(aa)
aa.func1()
# 在20秒的等待时间里,修改aa.py中func1的内容,结果不变

py文件区分两种用途:模块与脚本


#编写好的一个python文件可以有两种用途:
    一:脚本,一个文件就是整个程序,用来被执行
    二:模块,文件中存放着一堆功能,用来被导入使用
#python为我们内置了全局变量__name__,
    当文件被当做脚本执行时:__name__ 等于'__main__'
    当文件被当做模块导入时:__name__等于模块名
#作用:用来控制.py文件在不同的应用场景下执行不同的逻辑
    if __name__ == '__main__':
        print()

模块搜索路径


模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块.

1、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用。
    ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看。
2、如果没有,解释器则会查找同名的内建模块。
3、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。

包介绍


包是一种通过使用‘.模块名’来组织python模块名称空间的方式,包就是一个包含有init.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

包本质是文件夹,将文件组织起来,提高程序的结构性和可维护性。

包的导入语句

import #一般用于导入内置模块
from ... import ... #一般用于导入自定义的模块

遵循原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法;
但导入后,使用时没限制,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)

包的使用


import

单独导入包名称时不会导入包中所有包含的所有子模块。

from … import …

fromimport导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法

from glance.api import *

此处是想从包api中导入所有,实际上该语句只会导入包api下init.py文件中定义的名字

1 #在__init__.py中定义
2 x=10
3 
4 def func():
5     print('from api.__init.py')
6 
7 __all__=['x','func','policy']

绝对导入和相对导入

  • 示范文件
glance/                   #Top-level package

├── __init__.py      #Initialize the glance package

├── api                  #Subpackage for api

│   ├── __init__.py

│   ├── policy.py

│   └── versions.py

├── cmd                #Subpackage for cmd

│   ├── __init__.py

│   └── manage.py

└── db                  #Subpackage for db

    ├── __init__.py

    └── models.py

绝对导入:以glance作为起始 。
相对导入:用.或者..的方式最为起始(只能在同一个包中使用,不能用于不同目录内)

例:在glance/api/version.py中想要导入glance/cmd/manage.py

#绝对导入
from glance.cmd import manage
manage.main()

#相对导入
from ..cmd import manage
manage.main()

测试结果:注意一定要在于glance同级的文件中测试

包的执行范围

包以及包所包含的模块都是用来被导入的,而不是被直接执行的。而环境变量都是以执行文件为准的。

  • 例如:在glance/api/versions.py中导入glance/api/policy.py
#在version.py中

import policy
policy.get()

#单独运行version.py可以执行,运行version.py的路径搜索是从当前路径开始的,
于是在导入policy时能在当前目录下找到
# 在glance同级下的一个test.py文件中导入version.py
from glance.api import versions
'''
执行结果:
ImportError: No module named 'policy'
'''
'''
分析:
此时导入versions在versions.py中执行
import policy需要先从sys.path也就是从当前目录找policy.py,
这是找不到的
'''

软件开发规范


soft
    bin #可执行文件
        start.py
    conf #配置文件
        settions.py
    core #程序源文件
        src.py
    db #数据文件
        db.json
    lib # 库文件
        common.py
    log #日志文件
        access.lob
    Readme #程序说明文件

time与datetime模块


时间类型

print(time.time()) # 时间戳:1487130156.419527
print(time.strftime("%Y-%m-%d %X")) #格式化的时间字符串:'2017-02-15 11:40:53'
# 结构化的时间
print(time.localtime()) #本地时区的struct_time
print(time.gmtime())    #UTC时区的struct_time

时间类型转换关系

  • 时间戳与结构化时间的转换
# 将时间戳转换为当前时区的struct_time(结构化时间):
time.localtime()
time.localtime(1473525444.037215)

# 将时间戳转换为UTC时区(0时区)的struct_time:
time.gmtime()

将struct_time转化为时间戳:
 print(time.mktime(time.localtime()))
  • 结构化时间与格式化字符串时间的转换
 # 把struct_time格式化时间(如time.localtime()和 time.gmtime())转化为格式化的时间字符串:
print(time.strftime("%Y-%m-%d %X", time.localtime()))#2016-09-11 00:49:56

# 把格式化时间字符串转化为struct_time:
print(time.strptime('2011-05-05 16:37:06', '%Y-%m-%d %X'))
  • 时间格式的其它形式
print(time.asctime())  # 把一个表示时间的元组或者struct_time表示为这种形式:Sun Sep 11 00:43:43 2016
print(time.ctime())  # Sun Sep 11 00:46:38 2016
print(time.ctime(time.time()))  # Sun Sep 11 00:46:38 2016

datetime模块

import datetime
# print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
#print(datetime.date.fromtimestamp(time.time()) )  # 时间戳直接转成日期格式 2016-08-19
# print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天
# print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天
# print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时
# print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分

# c_time  = datetime.datetime.now()
# print(c_time.replace(minute=3,hour=2)) #时间替换

random模块


import random

print(random.random())#(0,1)----float    大于0且小于1之间的小数

print(random.randint(1,3))  #[1,3]    大于等于1且小于等于3之间的整数

print(random.randrange(1,3)) #[1,3)    大于等于1且小于3之间的整数

print(random.choice([1,'23',[4,5]]))#随机选择1或者23或者[4,5]

print(random.sample([1,'23',[4,5]],2))#对列表元素任意2个组合

print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716 


item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
print(item)
  • 应用示例:
生成随机验证码:
import random
def make_code(n):
    res = ''
    for i in rang(n):
        s1=chr(random.randint(65,90))   # 生成字母
        s2 = str(random.randint(0,9))   # 生成数字
        res += random.choice([s1, s2])
    return res
print(make_code(9))

os模块


os模块是与操作系统交互的一个接口

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.environ  获取系统环境变量
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的大小

系统中的path问题

在Linux和Mac平台上,该函数会原样返回path,
在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为反斜杠(\)
>>> os.path.normcase('c:/windows\\system32\\')   
'c:\\windows\\system32\\'      

规范化路径,如..和/
 os.path.normpath('c://windows\\System32\\../Temp/')   
'c:\\windows\\Temp'   #在windows中的显示样式

>>> a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..'
>>> print(os.path.normpath(a))
/Users/jieli/test1

os路径处理

方式一:推荐使用
import os,sys
possible_topdir = os.path.normpath(os.path.join(
    os.path.abspath(__file__), #文件绝对路径
    os.pardir, #上一级
    os.pardir,
    os.pardir
))
sys.path.insert(0,possible_topdir) #添加到sys模块搜索路径列表中的第一位中
方式二:不推荐使用
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

sys模块


 sys.argv           命令行参数List,第一个元素是程序本身路径
 sys.exit(n)        退出程序,正常退出时exit(0)
 sys.version        获取Python解释程序的版本信息
 sys.maxint         最大的Int值
 sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
 sys.platform       返回操作系统平台名称

实现打印进度条函数

import sys
import time

def progress(percent,width=50):
    if percent >= 1:
        percent=1
    show_str=('[%%-%ds]' %width) %(int(width*percent)*'#')
    print('\r%s %d%%' %(show_str,int(100*percent)),file=sys.stdout,flush=True,end='')

data_size=1025
recv_size=0
while recv_size < data_size:
    time.sleep(0.1) #模拟数据的传输延迟
    recv_size+=1024 #每次收1024

    percent=recv_size/data_size #接收的比例
    progress(percent,width=70) #进度条的宽度70

shutil文件处理模块


高级的 文件、文件夹、压缩包 处理模块..

将文件内容拷贝到另一个文件中.

import shutil
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

拷贝文件

shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在,会自己创建新文件

仅拷贝权限,内容、组、用户均不变

shutil.copymode('f1.log', 'f2.log') #目标文件必须存在

仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

shutil.copystat('f1.log', 'f2.log') #目标文件必须存在

拷贝文件和权限

shutil.copy('f1.log', 'f2.log')

递归的去拷贝文件夹

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('folder1')

递归去移动文件,类似mv命令(重命名)

shutil.move('folder1', 'folder3')

打包文件

import shutil
#将 /data 下的文件打包放置当前程序目录
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

#将 /data下的文件打包放置 /tmp/目录
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
shutil.make_archive(base_name, format,...)
base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak  =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir:   要压缩的文件夹路径(默认当前目录)
owner:  用户,默认当前用户
group:  组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象

对压缩包的处理(调用 ZipFile 和 TarFile 两个模块完成)

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()
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()

json&pickle模块


eval:将一个字符串转成python对象,执行一个字符串表达式,并返回表达式的值.
import json
 x="[null,true,false,1]"
 print(eval(x)) #报错,无法解析null类型,而json就可以
 print(json.loads(x)) 

序列化(pickling)

把对象(变量)从内存中变成可存储或传输的过程称之为序列化。

 - 序列化意义:
持久保存状态:内存无法永久保存数据,在断电/重启程序之前将内存中数据都保存下来(保存到文件中),便于下次载入之前的数据,然后继续执行,这是序列化。

跨平台数据交互:将序列化后的数据通过网络传输到别的机器上,实现了跨平台数据交互。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling

json


序列化格式的一种,不仅是标准格式,且比XML更快,
可以直接在Web页面中读取,非常方便。

# 转换过程:
内存中结构化数据---格式JSON---字符串---保存文件中/基于网络传输

# 序列化:
dic={'name':'alvin','age':23,'sex':'male'} #序列化可以单引号,但是反序列化要双引号,json.dumps(dic)

反序列化:(反序列化要双引号)
f=open('序列化对象')
data=json.loads(f.read())   #  等价于data=json.load(f)

# 注意点:
import json
dct="{'1':111}"           #json 不认单引号
dct=str({"1":111})       #报错,因为生成的数据还是单引号:{'one': 1}

dct='{"1":"111"}'  #反序列化时,需是双引号才能转换成功
print(json.loads(dct))
#  无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads

pickle(转换成字节类型)

# 转换过程:
内在中结构化数据---格式pickle---bytes类型---保存到文件中/基于网络传输

import pickle 
dic={'name':'alvin','age':23,'sex':'male'}  # 字典格式数据
j=pickle.dumps(dic)  #序列化成字节格式数据
print(type(j))           #<class 'bytes'>

f=open('序列化对象_pickle','wb')    #注意是w是写入str,wb是写入bytes,j是'bytes'
f.write(j)                                         #等价于pickle.dump(dic,f) 
f.close()

#-------------------------反序列化
f=open('序列化对象_pickle','rb') 
data=pickle.loads(f.read())            #  等价于data=pickle.load(f) 
print(data['age'])

shelve模块


shelve模块比pickle模块简单,只有一个open函数,
返回类似字典的对象,可读可写;
key必须为字符串,而值可以是python所支持的数据类型。

import shelve
f=shelve.open(r'sheve.txt')
# f['stu1_info']={'name':'egon','age':18,'hobby':['piao','smoking','drinking']}
# f['stu2_info']={'name':'gangdan','age':53}
# f['school_info']={'website':'http://www.pypy.org','city':'beijing'}

print(f['stu1_info']['hobby'])
f.close()

xml模块


xml是实现不同语言或程序之间进行数据交换的协议,跟json一样,传统公司如金融行业的很多系统的接口还主要是xml。

格式数据范例

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

python操作常用模块

root.iter('year') #全文搜索
root.find('country') #在root的子节点找,只找一个
root.findall('country') #在root的子节点找,找所有

操作xml文档

import xml.etree.ElementTree as ET

tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)

#遍历xml文档
for child in root:
    print('========>',child.tag,child.attrib,child.attrib['name'])
    for i in child:
        print(i.tag,i.attrib,i.text)

#只遍历year 节点
for node in root.iter('year'):
    print(node.tag,node.text)
#---------------------------------------

import xml.etree.ElementTree as ET

tree = ET.parse("xmltest.xml")
root = tree.getroot()

#修改
for node in root.iter('year'):
    new_year=int(node.text)+1
    node.text=str(new_year)
    node.set('updated','yes')
    node.set('version','1.0')
tree.write('test.xml')


#删除node
for country in root.findall('country'):
   rank = int(country.find('rank').text)
   if rank > 50:
     root.remove(country)

tree.write('output.xml')

#在country内添加(append)节点year2
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml")
root=tree.getroot()
for country in root.findall('country'):
    for year in country.findall('year'):
        if int(year.text) > 2000:
            year2=ET.Element('year2')
            year2.text='新年'
            year2.attrib={'update':'yes'}
            country.append(year2) #往country节点下添加子节点

tree.write('a.xml.swap')

创建xml文档

import xml.etree.ElementTree as ET

new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
age = ET.SubElement(name,"age",attrib={"checked":"no"})
sex = ET.SubElement(name,"sex")
sex.text = '33'
name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age = ET.SubElement(name2,"age")
age.text = '19'

et = ET.ElementTree(new_xml) #生成文档对象
et.write("test.xml", encoding="utf-8",xml_declaration=True)

ET.dump(new_xml) #打印生成的格式

hashlib模块


hash:一种算法 ,3.x里代替了md5模块和sha模块,
主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法。

特点

  • 内容相同则hash运算结果相同,内容稍微改变则hash值则变。
  • 不可逆推。
  • 相同算法:无论校验多长的数据,得到的哈希值长度固定。
import hashlib 
m=hashlib.md5()      # 创建md5对象,m=hashlib.sha256() 
m.update('hello'.encode('utf8'))   # 加密数据
print(m.hexdigest())  # 取出数据:5d41402abc4b2a76b9719d911017c592

m.update('alvin'.encode('utf8'))

print(m.hexdigest())  #92a7e713c30abbb0319fa07da2a5c4af

m2=hashlib.md5()
m2.update('helloalvin'.encode('utf8'))
print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af

'''
注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样
但是update多次为校验大文件提供了可能
'''

模拟撞库破解密码

# 加密算法时候存在缺陷,即:通过撞库可以反解。
所以,有必要对加密算法中添加自定义key再来做加密。

import hashlib
passwds=[
    'tom3714',
    'tom1313',
    'tom94139413',
    'tom123456',
    '123456tom',
    'a123tom',
    ]
def make_passwd_dic(passwds):  # 创建库文件
    dic={}
    for passwd in passwds:
        m=hashlib.md5()
        m.update(passwd.encode('utf-8'))
        dic[passwd]=m.hexdigest()
    return dic

def break_code(cryptograph,passwd_dic):  # 库文件的md5值与传入值进行匹配,属于暴力破解
    for k,v in passwd_dic.items():
        if v == cryptograph:
            print('密码是===>\033[46m%s\033[0m' %k)  # 获得密码

cryptograph='aee949757a2e698417463d47acac93df'
break_code(cryptograph,make_passwd_dic(passwds))

hmac模块

 # 内部对我们创建 key 和 内容 进行进一步的处理然后再加密:
import hmac
 h = hmac.new('alvin'.encode('utf8'))
 h.update('hello'.encode('utf8'))
 print (h.hexdigest())#320df9832eab4c038b6c1d7ed73a5940
#要想保证hmac最终结果一致,必须保证:
#1:hmac.new括号内指定的初始key一样时,无论update多少次,校验的内容累加到一起是一样的内容
import hmac

h1=hmac.new(b'egon')  # 初始key
h1.update(b'hello')
h1.update(b'world')
print(h1.hexdigest())   # f1bf38d054691688f89dcd34ac3c27f2

h2=hmac.new(b'egon')   # 初始key
h2.update(b'helloworld')
print(h2.hexdigest())   # f1bf38d054691688f89dcd34ac3c27f2

h3=hmac.new(b'egonhelloworld')
print(h3.hexdigest())   # bcca84edd9eeb86f30539922b28f3981

logging模块


日志的级别

CRITICAL = 50 #FATAL = CRITICAL 危险
ERROR = 40 # 错误
WARNING = 30 #WARN = WARNING 警告
INFO = 20  #信息
DEBUG = 10  # 调试级别
NOTSET = 0 #不设置

默认级别为warning,默认打印到终端

import logging

logging.debug('调试debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('错误error')
logging.critical('严重critical')

'''
WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
'''

为logging模块指定全局配置,针对所有logger有效,控制打印到文件中

# 可向配置方法中传入的参数类型
可在logging.basicConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。 
datefmt:指定日期时间格式。 
level:设置rootlogger的日志级别 
stream:用指定的stream创建StreamHandler。
可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。
若同时列出了filename和stream两个参数,则stream参数会被忽略。
# 传入参数的值格式:
%(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:用户输出的消息
  • 应用示例:
#========使用
import logging
logging.basicConfig(filename='access.log',
                    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',
                    level=10)

logging.debug('调试debug')
logging.info('消息info')
logging.warning('警告warn')
logging.error('错误error')
logging.critical('严重critical')

#========结果
access.log内容:
2017-07-28 20:32:17 PM - root - DEBUG -test:  调试debug
2017-07-28 20:32:17 PM - root - INFO -test:  消息info
2017-07-28 20:32:17 PM - root - WARNING -test:  警告warn
2017-07-28 20:32:17 PM - root - ERROR -test:  错误error
2017-07-28 20:32:17 PM - root - CRITICAL -test:  严重critical

logging模块的Formatter,Handler,Logger,Filter对象

#logger:产生日志的对象。
#Filter:过滤日志的对象。
#Handler:接收日志然后控制打印到不同的地方,FileHandler用来打印到文件中,StreamHandler用来打印到终端。
#Formatter对象:可以定制不同的日志格式对象,然后绑定给不同的Handler对象使用,以此来控制不同的Handler的日志格式
  • 应用示例:
'''
critical=50
error =40
warning =30
info = 20
debug =10
'''
import logging

#1、logger对象:负责产生日志,然后交给Filter过滤,然后交给不同的Handler输出
logger=logging.getLogger(__file__)

#2、Filter对象:不常用,略

#3、Handler对象:接收logger传来的日志,然后控制输出
h1=logging.FileHandler('t1.log') #打印到文件
h2=logging.FileHandler('t2.log') #打印到文件
h3=logging.StreamHandler() #打印到终端

#4、Formatter对象:日志格式
formmater1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

formmater2=logging.Formatter('%(asctime)s :  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

formmater3=logging.Formatter('%(name)s %(message)s',)


#5、为Handler对象绑定格式
h1.setFormatter(formmater1)
h2.setFormatter(formmater2)
h3.setFormatter(formmater3)

#6、将Handler添加给logger并设置日志级别
logger.addHandler(h1)
logger.addHandler(h2)
logger.addHandler(h3)
logger.setLevel(10)

#7、测试
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')

Logger与Handler的级别

# 验证
import logging

form=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

ch=logging.StreamHandler() # 打印到终端 
ch.setFormatter(form)
# ch.setLevel(10)
ch.setLevel(20)

l1=logging.getLogger('root')
# l1.setLevel(20)
l1.setLevel(10)

l1.addHandler(ch)

l1.debug('l1 debug')

Logger的继承

import logging

formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

ch=logging.StreamHandler()   #设置为打印到终端
ch.setFormatter(formatter)   #设置日志打印格式


logger1=logging.getLogger('root')  #设置Logger的名字
logger2=logging.getLogger('root.child1')
logger3=logging.getLogger('root.child1.child2')


logger1.addHandler(ch)  #为logger1对象添加Handler来控制输出
logger2.addHandler(ch)
logger3.addHandler(ch)

logger1.setLevel(10)  # 日志级别
logger2.setLevel(10)
logger3.setLevel(10)

logger1.debug('log1 debug')
logger2.debug('log2 debug')
logger3.debug('log3 debug')
'''
2017-07-28 22:22:05 PM - root - DEBUG -test:  log1 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug
2017-07-28 22:22:05 PM - root.child1 - DEBUG -test:  log2 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
2017-07-28 22:22:05 PM - root.child1.child2 - DEBUG -test:  log3 debug
'''
  • 应用
# 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()
# logging使用测试:
import time
import logging
import my_logging  # 导入自定义的logging配置

logger = logging.getLogger(__name__)  # 生成logger实例
def demo():
    logger.debug("start range... time:{}".format(time.time()))
    logger.info("中文测试开始。。。")
    for i in range(10):
        logger.debug("i:{}".format(i))
        time.sleep(0.2)
    else:
        logger.debug("over range... time:{}".format(time.time()))
    logger.info("中文测试结束。。。")
if __name__ == "__main__":
    my_logging.load_my_logging_cfg()  # 在你程序文件的入口加载自定义的logging配置
    demo()
# 如何拿到logger对象

有了上述方式我们的好处是:所有与logging模块有关的配置都写到字典中就可以了,更加清晰,方便管理
需要解决的问题是:
    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=''的配置

re正则表达式


正则就是用来描述一类事物的规则,内嵌在Python中,通过 re 模块实现,被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

常用匹配模式(元字符)

\w  匹配字母数字及下划线
\W 匹配非字母数字及下划线
\s   匹配任意空白字符,等价于[\t\n\r\f]
\S   匹配任意非空字符
\d   匹配任意数字,等价于[0-9]
\D   匹配任意非数字
\A   匹配字符串开始
\Z   匹配字符串结束,如存在换行,只匹配到换行前的结束字符串
\z   匹配字符串结束
\G   匹配最后匹配完成的位置
\n   匹配一个换行符
\t   匹配一个制表符
^   匹配字符串的开关
    匹配字符串的结尾
.   匹配任意字符。除了换行符,当re.DOTALL标记被指定时,则可匹配包含换行符的任意字符
[……]   用来表示一组字符,单独列出:[amk]  匹配‘a’,'m'或'k'
[^……]   取反,不在[]中的字符:[^abc]匹配除了a,b,c之外的字符
*   匹配0个或多个的表达式
+   匹配1个或多个的表达式
?   匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
{n}   精确匹配n个前面表达式
{n,m}   匹配n到m次由前面的正则表达式定义的片段,贪婪方式
a|b   匹配a或b
()   匹配括号内的表达式,也表示一个组

re提供的方法

import re
# print(re.findall('e','alex make love') )   #['e', 'e', 'e'],返回所有满足匹配条件的结果,放在列表里
# print(re.search('e','alex make love').group())
#e,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
# print(re.match('e','alex make love'))    #None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
#为何同样的表达式search与findall却有不同结果:
print(re.search('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))").group()) #(-40.35/5)
print(re.findall('\(([\+\-\*\/]*\d+\.?\d*)+\)',"1-12*(60+(-40.35/5)-(-4*3))")) #['/5', '*3']

#看这个例子:(\d)+相当于(\d)(\d)(\d)(\d)...,是一系列分组
print(re.search('(\d)+','123').group()) # 123   group的作用是将所有组拼接到一起显示出来,
print(re.findall('(\d)+','123')) # ['3']  findall结果是组内的结果,且是最后一个组的结果
-----------------------------------------------------------------------------------------------------
# 分割
# print(re.split('[ab]','abcd'))     #['', '', 'cd'],先按'a'分割得到'''bcd',再对'''bcd'分别按'b'分割
# 替换
# print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默认替换所有
# print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love 替换1个
# print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love 替换2个
# print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex
# # alex位于第1位,空格符位于第2位,make位于第三位,再一个空字符位于第4位,love位于第5位
# print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),结果带有总共替换的个数

# 生成正则表达式对象
# obj=re.compile('\d{2}')
# print(obj.search('abc123eeee').group()) #12
# print(obj.findall('abc123eeee')) #['12'],重用了obj

匹配模式

import  re
#\w与\W
# print(re.findall('\w','hello egon 123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
# print(re.findall('\W','hello egon 123')) #[' ', ' ']

#\s与\S
# print(re.findall('\s','hello  egon  123')) #[' ', ' ', ' ', ' ']
# print(re.findall('\S','hello  egon  123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

#\n \t都是空,都可以被\s匹配
# print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']

#\n与\t
# print(re.findall(r'\n','hello egon \n123')) #['\n']
# print(re.findall(r'\t','hello egon\t123')) #['\n']

#\d与\D
# print(re.findall('\d','hello egon 123')) #['1', '2', '3']
# print(re.findall('\D','hello egon 123')) #['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']

#\A与\Z
# print(re.findall('\Ahe','hello egon 123')) #['he'],\A==>^
# print(re.findall('123\Z','hello egon 123')) #['he'],\Z==>$

#^与$
# print(re.findall('^h','hello egon 123')) #['h']
# print(re.findall('3$','hello egon 123')) #['3']

# 重复匹配:| . | * | ? | .* | .*? | + | {n,m} |
#.
# print(re.findall('a.b','a1b')) #['a1b']
# print(re.findall('a.b','a1b a*b a b aaab')) #['a1b', 'a*b', 'a b', 'aab']
# print(re.findall('a.b','a\nb')) #[]
# print(re.findall('a.b','a\nb',re.S)) #['a\nb']
# print(re.findall('a.b','a\nb',re.DOTALL)) #['a\nb']同上一条意思一样

#*
# print(re.findall('ab*','bbbbbbb')) #[]
# print(re.findall('ab*','a')) #['a']
# print(re.findall('ab*','abbbb')) #['abbbb']

#?
# print(re.findall('ab?','a')) #['a']
# print(re.findall('ab?','abbb')) #['ab']
#匹配所有包含小数在内的数字
# print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3']


# .*默认为贪婪匹配,匹配多个任意字符
# print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']

#.*?为非贪婪匹配:推荐使用,要么匹配一个任意字符,要么匹配多个任意字符
# print(re.findall('a.*?b','a1b22222222b')) #['a1b']

#+
# print(re.findall('ab+','a')) #[]
# print(re.findall('ab+','abbb')) #['abbb']

#{n,m}  匹配n到m次由前面的正则表达式定义的片段,贪婪方式
# print(re.findall('ab{2}','abbb')) #['abb']
# print(re.findall('ab{2,4}','abbb')) #['abb']
# print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'
# print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

#[] 用来表示一组字符,单独列出:[amk]  匹配‘a’,'m''k'
# print(re.findall('a[1*-]b','a1b a*b a-b')) #[]内的都为普通字符了,且如果-没有被转意的话,应该放到[]的开头或结尾
# print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,所以结果为['a=b']
# print(re.findall('a[0-9]b','a1b a*b a-b a=b')) #[]内代表数字取值范围,所以结果为['a1b']
# print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) #[]内代表小定字母取值范围,所以结果为['aeb']
# print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #[]内代表小写大写的取值范围,所以结果为['a=b']

#\# print(re.findall('a\\c','a\c')) #对于正则来说a\\c确实可以匹配到a\c,但是在python解释器读取a\\c时,会发生转义,然后再交给re去执行,发生2次转义所以抛出异常
# print(re.findall(r'a\\c','a\c')) #r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
# print(re.findall('a\\\\c','a\c')) #同上面的意思一样,和上面的结果一样都是['a\\c']

#():分组 匹配成功后,只保留括号里的结果,括号以外的内容不保留
# print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']
# print(re.findall('(ab)+123','ababab123')) #['ab'],匹配到末尾的ab123中的ab
# print(re.findall('(?:ab)+123','ababab123')) # ['ababab123'],findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容
# print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']
# print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">点击</a>'))#['href="http://www.baidu.com"']

补充


import re
print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #{'tag_name': 'h1'} 获得字典

print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())   # <h1>hello</h1>
print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())      # <h1>hello</h1>
# 使用|(或),先匹配的先生效,|左边是匹配小数,而findall最终结果是查看分组,所有即使匹配成功小数也不会存入结果
#而不是小数时,就去匹配(-?\d+),匹配到的自然就是非小数的数,在此处即整数
print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整数['1', '-2', '60', '', '5', '-4', '3']

匹配形式(贪婪……)


import re

s='''
http://www.baidu.com
egon@oldboyedu.com
你好
010-3141
'''
#常规匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)
# print(res)
# print(res.group())
# print(res.span())

#泛匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello.*Demo',content)
# print(res.group())

#匹配目标,获得指定数据
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
# print(res.group()) #取所有匹配的内容
# print(res.group(1)) #取匹配的第一个括号内的内容
# print(res.group(2)) #去陪陪的第二个括号内的内容

#贪婪匹配:  .* 代表匹配0到无穷个任意字符
# content='Hello 123 456 World_This is a Regex Demo'#
# res=re.match('^He.*(\d+).*Demo$',content)
# print(res)
# print(res.group(1)) #只打印所有数字中的6,因为.*会尽可能多的匹配,然后后面跟至少一个数字,其余数字被其它匹配了
#  print(res.group()) # Hello 123 456 World_This is a Regex Demo

#非贪婪匹配:?匹配尽可能少的字符
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^He.*?(\d+).*Demo$',content)
# print(res.group(1)) #打印123

#匹配模式: .不能匹配换行符
# content='''Hello 123456 World_This
# is a Regex Demo
# '''
# res=re.match('He.*?(\d+).*?Demo$',content)
# print(res) #输出None
#
# res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S让.可以匹配换行符
# print(res)
# print(res.group(1)) # 123456

#转义:\
# content='price is $5.00'
# res=re.match('price is $5.00',content)
# print(res)
# res=re.match('price is \$5\.00',content)
# print(res.group())

#总结:尽量精简,详细的如下
    # 尽量使用泛匹配模式.*
    # 尽量使用非贪婪模式: .*?
    # 使用括号得到匹配目标:用group(n)去取得结果
    # 有换行符就用re.S:修改模式

#re.search:会扫描整个字符串,不会从头开始,找到第一个匹配的结果就会返回
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
# res=re.search('Hello.*?(\d+).*?Demo',content) #
# print(res.group(1)) # 输出结果为123

# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
# res=re.match('Hello.*?(\d+).*?Demo',content)
# print(res) #输出结果为Noneb

匹配演练


import re
content='''
<tbody>
<tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk "><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320"><img class="rpic" src="http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&amp;quality=100"></a><span data-res-id="476630320" "
# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# print(res.group(1))


#re.findall:找到符合条件的所有结果
# res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# for i in res:
#     print(i)



#re.sub:字符串替换
import re
content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'

# content=re.sub('\d+','',content)
# print(content)


#用\1取得第一个括号的内容
#用法:将123与456换位置
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)
# content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)
# print(content)




# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Extra.*?(\d+).*strings',content)
# print(res.group(1))


# import requests,re
# respone=requests.get('https://book.douban.com/').text

# print(respone)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)
# # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)
#
#
# for i in res:
#     print('%s    %s    %s   %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值