本节目录:
1.shelve模块
2.xml模块
3.re模块
4.subprocess模块
5.logging模块
6.pymysql
1.shelve 模块
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式
import shelve d = shelve.open('shelve_test') #打开一个文件 class Test(object): def __init__(self,n): self.n = n t = Test(123) t2 = Test(123334) name = ["alex","rain","test"] d["test"] = name #持久化列表 d["t1"] = t #持久化类 d["t2"] = t2 d.close()
2.xml模块
3.re模块
正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串,在文本处理方面功能非常强大,也经常用作爬虫,来爬取特定内容,Python本身不支持正则,但是通过导入re模块,Python也能用正则表达式,下面就来讲一下python正则表达式的用法。
一、匹配规则
二、findall
findall(),可以将匹配到的结果以列表的形式返回,如果匹配不到则返回一个空列表,下面来看一下代码中的使用
import re l=re.findall(r'\d','4g6gggg9,9') # \d代表数字,将匹配到的元素放到一个列表里 print(l) # ['4', '6', '9', '9'] print(re.findall(r'\w','ds.._ 4')) # ['d', 's', '_', '4'],匹配字母数字下划线 print(re.findall(r'^sk','skggj,fd,7')) # 以sk开头的,['sk'] print(re.findall(r'^sk','kggj,fd,7')) # [] print(re.findall(r'k{3,5}','ffkkkkk')) # 取前面一个字符‘k'的3到5次,['kkkkk'] print(re.findall(r'a{2}','aasdaaaaaf')) # 匹配前一个字符a两次,['aa', 'aa', 'aa'] print(re.findall(r'a*x','aaaaaax')) # ['aaaaaax'] 匹配前面一个字符0次或多次,贪婪匹配 print(re.findall(r'\d*', 'www33333')) # ['', '', '', '33333', ''] print(re.findall(r'a+c','aaaacccc')) # ['aaaac'] 匹配前面一个字符的一次或多次,贪婪匹配 print(re.findall(r'a?c','aaaacccc')) # ['ac', 'c', 'c', 'c'] 匹配前面一个字符的0次或1次 print(re.findall(r'a[.]d','acdggg abd')) # .在[]里面失去了意义,所以结果为[] print(re.findall(r'[a-z]','h43.hb -gg')) # ['h', 'h', 'b', 'g', 'g'] print(re.findall(r'[^a-z]','h43.hb -gg')) # 取反,['4', '3', '.', ' ', '-'] print(re.findall(r'ax$','dsadax')) # 以'ax'结尾 ['ax'] print(re.findall(r'a(\d+)b','a23666b')) # ['23666'] print(re.findall(r'a(\d+?)b','a23666b')) # ['23666']前后均有限定条件,则非贪婪模式失效 print(re.findall(r'a(\d+)','a23b')) # ['23'] print(re.findall(r'a(\d+?)','a23b')) # [2] 加上一个?变成非贪婪模式
总结:findall 默认是匹配所有的,返回所以匹配到的一个列表,但是注意的是当有分组的时候,匹配还是按照匹配规则去,返回内容的时候,只包含了分组里面的内容。
三、match和search
match从要匹配的字符串的开头开始,尝试匹配,如果字符串开始不符合正则表达式,则匹配失败,函数返回None,匹配成功的话用group取出匹配的结果,search和mach很像,search是匹配整个字符串直道匹配到一个就返回。下面看下代码。
mport re print(re.match(r'a\d','a333333a4').group()) # a3,匹配到第一个返回 print(re.match(r'a\d','ta333333a4')) # 字符串开头不满足要求,返回一个None print(re.search(r'a\d','ta333333a4').group()) # a3 整个字符串匹配不需要从开头匹配 print(re.search(r'a(\d+)','a23b').group()) # a23 这里需要注意的是group()返回的是整个匹配到的字符串,如果是group(1)的话就只返回 23 了 print(re.search(r'a(\d+?)','a2366666666666b').group()) # a2 非贪婪模式 print(re.search(r'a(\d+)b','a23666b').group(1)) # 23666 group(1)返回第一个组
四、split、sub和subn
split能够将匹配的子串分割后返回列表,sub能将匹配到的字段用另一个字符串替换返回替换后的字符串,subn还返回替换的次数,下面再代码上看一下他们的用法
import re print(re.split(r'\d','sd.4,r5')) # 以数字分割,注意第二次以'5'分割的时候,后面有一个空格 ['sd.',',r',''] print(re.sub(r'\d','OK','3,4sfds.6hhh')) # OK,OKsfds.OKhhh print(re.sub(r'\d','OK','3,4sfds.6hhh',2)) # 2表示指定替换两次 OK,OKsfds.6hhh print(re.subn(r'\d','OK','3,4sfds.6hhh')) # ('OK,OKsfds.OKhhh', 3) 将替换的次数也返回了
五、原生字符串、编译、分组
1、原生字符串
细心的人会发现,我每一次在写匹配规则的话,都在前面加了一个r,为什么要这样写,下面从代码上来说明,
import re #“\b”在ASCII 字符中代表退格键,\b”在正则表达式中代表“匹配一个单词边界” print(re.findall("\bblow","jason blow cat")) #这里\b代表退格键,所以没有匹配到 print(re.findall("\\bblow","jason blow cat")) #用\转义后这里就匹配到了 ['blow'] print(re.findall(r"\bblow","jason blow cat")) #用原生字符串后就不需要转义了 ['blow']
2、编译
如果一个匹配规则,我们要使用多次,我们就可以先将其编译,以后就不用每次都在去写匹配规则,下面来看一下用法
import re c=re.compile(r'\d') #以后要在次使用的话,只需直接调用即可 print(c.findall('as3..56,')) #['3', '5', '6']
3、分组
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()
表示的就是要提取的分组,可以有多个组,分组的用法很多,这里只是简单的介绍一下
import re print(re.findall(r'(\d+)-([a-z])','34324-dfsdfs777-hhh')) # [('34324', 'd'), ('777', 'h')] print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(0)) # 34324-d 返回整体 print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(1)) # 34324 获取第一个组 print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(2)) # d 获取第二个组 print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(3)) # IndexError: no such group print(re.search(r"(jason)kk\1","xjasonkkjason").group()) #\1表示应用编号为1的组 jasonkkjason print(re.search(r'(\d)gg\1','2j333gg3jjj8').group()) # 3gg3 \1表示使用第一个组\d # 下面的返回None 为什么是空?而匹配不到3gg7,因为\1的不仅表示第一组,而且匹配到的内容也要和第一组匹配到的内容相同,第一组匹配到3,第二组匹配到7 不相同所以返回空 print(re.search(r'(\d)gg\1','2j333gg7jjj8')) print(re.search(r'(?P<first>\d)abc(?P=first)','1abc1')) # 1abc1 声明一个组名,使用祖名引用一个组 r=re.match('(?P<n1>h)(?P<n2>\w+)','hello,hi,help') # 组名的另外一种用法 print(r.group()) # hello 返回匹配到的值 print(r.groups()) # ('h', 'ello')返回匹配到的分组 print(r.groupdict()) # {'n2': 'ello', 'n1': 'h'} 返回分组的结果,并且和相应的组名组成一个字典 # 分组是从已经匹配到的里面去取值 origin ="hello alex,acd,alex" print(re.findall(r'(a)(\w+)(x)',origin)) # [('a', 'le', 'x'), ('a', 'le', 'x')] print(re.findall(r'a\w+',origin)) # ['alex', 'acd', 'alex'] print(re.findall(r'a(\w+)',origin)) # ['lex', 'cd', 'lex'] print(re.findall(r'(a\w+)',origin)) # ['alex', 'acd', 'alex'] print(re.findall(r'(a)(\w+(e))(x)',origin)) # [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')] r=re.finditer(r'(a)(\w+(e))(?P<name>x)',origin) for i in r : print(i,i.group(),i.groupdict()) ''' [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')] <_sre.SRE_Match object; span=(6, 10), match='alex'> alex {'name': 'x'} <_sre.SRE_Match object; span=(15, 19), match='alex'> alex {'name': 'x'} ''' print(re.findall('(\w)*','alex')) # 匹配到了alex、但是4次只取最后一次即 x 真实括号只有1个 print(re.findall(r'(\w)(\w)(\w)(\w)','alex')) # [('a', 'l', 'e', 'x')] 括号出现了4次,所以4个值都取到了 origin='hello alex sss hhh kkk' print(re.split(r'a(\w+)',origin)) # ['hello ', 'lex', ' sss hhh kkk'] print(re.split(r'a\w+',origin)) # ['hello ', ' sss hhh kkk']
五、综合练习
检测一个IP地址,比如说192.168.1.1,下面看下代码怎么实现的
c=re.compile(r'((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){3}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)') print(c.search('245.255.256.25asdsa10.11.244.10').group()) # 10.11.244.10 245.255.256.25不符合要求所以就没有匹配出来
这里来解释下上面的匹配规则,先看 (1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.),其中1\d\d表示匹配100-199的数字 | 代表或的意思,2[0-4]\d代表匹配100-249,25[0-5]代表匹配250-255,[1-9]\d|\d)代表匹配10-99和0-9,\.代表匹配一个点,{3}代表将前面的分组匹配3次,后面的一部分类似就不说明了。要匹配一个ip重要的是先理解ip每个字段的形式,然后再来写匹配规则。此外有兴趣的话,还可以去看我Python实战目录里面计算器的实现,里面用到了正则。
4.subprocess模块
那么subprocess模块是做什么的?他可以指定shell 去执行命令
输出到屏幕:
import subprocess subprocess.Popen('dir',shell=True) #指定shell 去解释'dir'这个命令
##注意subprocess 是一个程序 而 屏幕又是另一个的程序,可以看到的执行结果是subprocess的执行结果直接交给了屏幕输出
输出到管道:
>>> res = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE) 指定了subprocess的执行结果,输出到管道中去,而你要是想要数据就直接通过subprocess的对象到管道中去取 subprocess 可以有三个管道: stderr = subprocess.PIPE ##标准输出 stdin = subprocess.PIPE ##标准输入 stdout = subprocess.PIPE ##错误输出
去管道取数据:
>>> res.stdout.read() ##去管道取得数据 默认是对应系统编码的字节码,记住:他取完就没有了。
5.logging模块
一 (简单应用)
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
输出:
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message
可见,默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认的日志格式为日志级别:Logger名称:用户输出消息。
二 灵活配置日志级别,日志格式,输出位置
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='/tmp/test.log', filemode='w') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
输出:
Thu, 10 May 2018 20:30:21 a1.py[line:9] DEBUG debug message Thu, 10 May 2018 20:30:21 a1.py[line:10] INFO info message Thu, 10 May 2018 20:30:21 a1.py[line:11] WARNING warning message Thu, 10 May 2018 20:30:21 a1.py[line:12] ERROR error message Thu, 10 May 2018 20:30:21 a1.py[line:13] CRITICAL critical message
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为-可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。 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对象
我们看过金庸的很多小说,其中呢 最厉害的武功应该就是吸星大法了!会吸星大法的人呢,可以吸收别人武功为己用。
import logging ##这样我们就创建了一个会吸星大法的人 logger = logging.getLogger() # 创建一种能够向文件写入日志的武功 fh = logging.FileHandler('test.log') # 创建一种能够向屏幕输出日志的武功 ch = logging.StreamHandler() # 创建一种输入日志的格式是怎么样的 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ##两种武功分别吃掉格式 fh.setFormatter(formatter) ch.setFormatter(formatter) ##这个会吸星的再吃掉两种武功,就具有了两个的功力了 logger.addHandler(fh) #logger对象可以添加多个fh和ch对象 logger.addHandler(ch) ##剩下的就logger这个人存活着,使用logger做各种动作 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')
这里有两个注意点:
(1):Logger是一个树形层级结构我们是可以给Logger起名字的,默认就是祖先root。
logger = logging.getLogger() ##祖先 logger2 = logging.getLogger("mylogger") ##儿子 logger3 = logging.getLogger("mylogger.next") ## 孙子
名字是唯一的,要是你创建了如下两个对象:但是呢 其实都是同一个对象,且输出的级别为ERROR
logger2 = logging.getLogger("mylogger") ##创建了logger2对象,级别为INFO logger2.setLevel(logging.INFO) logger3 = logging.getLogger("mylogger") ##创建了logger3对象,级别为ERROR logger3.setLevel(logging.ERROR)
(2):还需要注意的是,要是当给的名字,存在父辈的时候,上面有几个父辈就没个级别的日志就打印几次。
如下代码:请输入你的猜想是什么?
import logging logger = logging.getLogger() logger.setLevel(logging.DEBUG) logger1 = logging.getLogger('mylogger') logger1.setLevel(logging.INFO) fh = logging.FileHandler("FILE.LOG") ch = logging.StreamHandler() logger.addHandler(fh) logger.addHandler(ch) logger1.addHandler(fh) logger1.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') logger1.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message')
是不是可以说,logger打印五条?logger1打印5条信息?其实并不是。请看结果:
logger debug message
logger info message
logger warning message
logger error message
logger critical message
logger1 info message
logger1 info message
logger1 warning message
logger1 warning message
logger1 error message
logger1 error message
logger1 critical message
logger1 critical message
会发现logger1会多打印出一次相同的信息,这是因为logger1上面存在了一个父辈root,要记得存在几个父辈就多打印几次。
6.pymysql
python关于mysql的API--pymysql模块
pymsql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同。
模块安装
pip install pymysql
执行sql语句
import pymysql #添加数据 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='yyy') cursor = conn.cursor() ####创建游标,游标可以理解成在cmd中一闪一闪的光标 # sql = """CREATE TABLE EMPLOYEE ( # FIRST_NAME CHAR(20) NOT NULL, # LAST_NAME CHAR(20), # AGE INT, # SEX CHAR(1), # INCOME FLOAT )""" # # cursor.execute(sql) ###他是有返回值的 row_affected 就是这次操作受到影响的行数 #row_affected = cursor.execute("create table t1(id INT ,name VARCHAR(20))") #row_affected=cursor.execute("INSERT INTO t1(id,name) values (1,'alvin'),(2,'xialv')") #cursor.execute("update t1 set name = 'silv2' where id=2") #查询数据 cursor和文件的光标是一致的,取完数据就取完了 row_affected=cursor.execute("select * from t1") one=cursor.fetchone() ##取到查询到的一条数据 # many=cursor.fetchmany(2) ##取到2条数据。 # all=cursor.fetchall() ##取到查询到的所有数据 #scroll 移动光标 #print(cursor.fetchone()) #(1,) #cursor.scroll(-1,'relative') #相对当前位置移动 -1 向上移动一个 #print(cursor.fetchone()) #(1,) #print(cursor.fetchone()) #(1,) #cursor.scroll(1,'relative') #相对当前位置移动 跳过下一下 #print(cursor.fetchone()) #(3,) #cursor.scroll(2,mode='absolute') # 相对绝对位置移动 #更改获取数据结果的数据类型,默认是元组,可以改为字典等:conn.cursor(cursor=pymysql.cursors.DictCursor) conn.commit() cursor.close() conn.close()