python 之 常用模块(subprocess模块、logging模块、re模块)
subprocess模块
subprocess模块是什么
在学习这个模块前,我们先用Python的help()函数查看一下subprocess模块是干嘛的:
DESCRIPTION This module allows you to spawn processes, connect to their input/output/error pipes, and obtain their return codes.
即允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。
通过python和系统交互,执行系统的命令
三种执行命令的方法
-
subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐
-
subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法
-
subprocess.Popen() #上面各种方法的底层封装
subprocess.run()
标准写法
subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True) # PIPE:管道 check:检查错误
涉及到管道|的命令需要这样写
subprocess.run('df -h|grep disk1',shell=True) #shell = True的意思是这条命令直接交给系统去执行,不需要python负责解析
subprocess.call()
#执行命令,返回命令执行状态 , 0 or 非0 >>> retcode = subprocess.call(["ls", "-l"]) #执行命令,如果命令结果为0,就正常返回,否则抛异常 >>> subprocess.check_call(["ls", "-l"]) 0 #接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 >>> subprocess.getstatusoutput('ls /bin/ls') (0, '/bin/ls') #接收字符串格式命令,并返回结果 >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' #执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res >>> res=subprocess.check_output(['ls','-l']) >>> res b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'
subprocess.Popen()
常用参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
下面这2条语句执行会有什么区别?
a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE) a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
区别是Popen会在发起命令后立刻返回,而不等命令执行结果。这样的好处是什么呢?
如果你调用的命令或脚本 需要执行10分钟,你的主程序不需卡在这里等10分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。
Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法
poll()
Check if child process has terminated. Returns returncode
wait()
Wait for child process to terminate. Returns returncode attribute.
terminate()
终止所启动的进程Terminate the process with SIGTERM
kill()
杀死所启动的进程 Kill the process with SIGKILL
communicate()
与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束
>>> a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True) >>> a.communicate(b'22') (b'your guess:try bigger\n', b'')
send_signal(signal.xxx)
发送系统信号
pid
拿到所启动进程的进程号
logging 模块
logging模块是什么
记录程序运行日志的模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,
logging的日志可以分为 debug(), info(), warning(), error() and critical()
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),默认的日志格式为日志级别: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')
配置参数:
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对象配置
import logging # 生成logger对象 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') # 设置hander 级别 fh.setLevel(logging.DEBUG) # handler绑定formatter fh.setFormatter(formatter) ch.setFormatter(formatter) # logger对象绑定handler 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')
每个组件的主要功能
logger组件
每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名
LOG=logging.getLogger(”chat.gui”)
还可以绑定handler和filters
Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加 或 删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加 或 删除指定的handler
handler组件
handler对象负责发送相关的信息到指定目的地。
Python的日志系统有多种Handler可以使用。
有些Handler可以把信息输出到控制台,有些Handler可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。
如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
- logging.StreamHandler 使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。
- logging.FileHandler 和StreamHandler 类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件
logging.handlers.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
- maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
- backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval
是时间间隔。
when
参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
- S 秒
- M 分
- H 小时
- D 天
- W 每星期(interval==0时代表星期一)
- midnight 每天凌晨
formatter 组件
日志的formatter是个独立的组件,可以跟handler组合
fh = logging.FileHandler("access.log") formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) #把formmater绑定到fh上
filter 组件
如果你想对日志内容进行过滤,就可自定义一个filter
class IgnoreBackupLogFilter(logging.Filter): """忽略带db backup 的日志""" def filter(self, record): #固定写法 return "db backup" not in record.getMessage()
注意filter函数会返加True or False,logger根据此值决定是否输出此日志
然后把这个filter添加到logger中
logger.addFilter(IgnoreBackupLogFilter())
下面的日志就会把符合filter条件的过滤掉
logger.debug("test ....") logger.info("test info ....") logger.warning("start to run db backup job ....") logger.error("test error ....")
一个同时输出到屏幕、文件、带filter的完成例子
import logging class IgnoreBackupLogFilter(logging.Filter): """忽略带db backup 的日志""" def filter(self, record): #固定写法 return "db backup" not in record.getMessage() #console handler ch = logging.StreamHandler() ch.setLevel(logging.INFO) #file handler fh = logging.FileHandler('mysql.log') #fh.setLevel(logging.WARNING) #formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #bind formatter to ch ch.setFormatter(formatter) fh.setFormatter(formatter) logger = logging.getLogger("Mysql") logger.setLevel(logging.DEBUG) #logger 优先级高于其它输出途径的 #add handler to logger instance logger.addHandler(ch) logger.addHandler(fh) #add filter logger.addFilter(IgnoreBackupLogFilter()) logger.debug("test ....") logger.info("test info ....") logger.warning("start to run db backup job ....") logger.error("test error ....")
文件自动截断例子
import logging from logging import handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")
re模块
re模块是什么
re模块里放置了和正则表达式相关的方法,正则表达式就是字符串的匹配规则
在线测试工具 http://tool.chinaz.com/regex/
常用的表达式规则
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) '$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1 '*' 匹配*号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa' '+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] '?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次 '{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb' '{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] '|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' '(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'
[^\] 匹配任何不在指定范围内的任意字符 '\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^ '\Z' 匹配字符结尾,同$ '\d' 匹配数字0-9 '\D' 匹配非数字 '\w' 匹配[A-Za-z0-9] '\W' 匹配非[A-Za-z0-9] 's' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' '(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
贪婪匹配和惰性匹配
*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配,尽可能少的匹配
>>> str = '李杰和李莲英和李二棍子' >>> import re >>> re.findall('李.', str) ['李杰', '李莲', '李二'] >>> re.findall('李.*', str) ['李杰和李莲英和李二棍子'] >>> re.findall('李.+', str) ['李杰和李莲英和李二棍子'] >>> re.findall('李.?', str) ['李杰', '李莲', '李二'] # 后面加问号 >>> re.findall('李.*?', str) ['李', '李', '李'] >>> re.findall('李.+?', str) ['李杰', '李莲', '李二'] >>> re.findall('李.??', str) ['李', '李', '李']
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 合在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现
import re str_1 = '李杰和李莲英和李二棍子kdaxfkjahkx' res = re.match('.*', str_1) res1 = re.match('.*?', str_1) res3 = re.match('.*?x', str_1) print(res) print(res1) print(res3)
输出
<_sre.SRE_Match object; span=(0, 22), match=’李杰和李莲英和李二棍子kdaxfkjahkx’>
<_sre.SRE_Match object; span=(0, 0), match=”>
<_sre.SRE_Match object; span=(0, 15), match=’李杰和李莲英和李二棍子kdax’>
re的匹配语法有以下几种
函数参数说明:
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
maxsplit | 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 – 可选标志 |
- re.match 从头开始匹配
- re.match(pattern, string, flags=0)
- 只匹配第一个,返回对象
- 先判断赋值的变量,加.group()返回值
- re.search 匹配包含
- re.search(pattern, string, flags=0)
- 只匹配一个,返回对象
- re.findall 把所有匹配到的字符放到以列表中的元素返回
- re.findall(pattern, string, flags=0)
- match and search均用于匹配单值,即:只能匹配字符串中的一个,如果想要匹配到字符串中所有符合条件的元素,则需要使用 findall。
- re.split 以匹配到的字符当做列表分隔符
- re.split(pattern, string, maxsplit=0, flags=0)
- re.sub 匹配字符并替换
- re.sub(pattern, repl, string, count=0, flags=0)
- 比str.replace 更强大
- re.fullmatch 全部匹配
- re.fullmatch(pattern, string, flags=0)
- 需要完全的对上,才可以匹配上
- 整个字符串匹配成功就返回re object, 否则返回None
- re.fullmatch(‘\w+@\w+\.(com|cn|edu)’,alex@oldboyedu.cn)
- re.compile 根据包含正则表达式的字符串创建模式对象
- re.compile(pattern, flags=0)
- prog = re.compile(pattern)
result = prog.match(string)
两步,等同于
result = re.match(pattern, string) - 为什么还要做这个东西呢?
- 因为系统在收到语法后,先把语法转化为逻辑条件语句,然后再那后面的字符串进去进行对比,把他们分开就省掉了一半系统工作量,提高效率
import re text = "JGood is a handsome boy, he is cool, clever, and so on..." res = re.findall(r'\w*oo\w*', text) #查找所有包含'oo'的单词 print(res) regex = re.compile(r'\w*oo\w*') res2 = regex.findall(text) #查找所有包含'oo'的单词" print(res2)
输出
[‘JGood’, ‘cool’]
[‘JGood’, ‘cool’]
flags标志符
flags是匹配模式,可以使用按位或’|’表示同时生效,也可以在正则表达式字符串中指定。Pattern对象是不能直接实例化的,只能通过compile方法得到。匹配模式有:
1).re.I (re.IGNORECASE): 忽略大小写
2).re.M (MULTILINE): 多行模式,改变’^’和’$’的行为
3).re.S (DOTALL): 点任意匹配模式,改变’.’的行为
4).re.L (LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
5).re.U (UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
6).re.X (VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释
紧凑的例子,不易阅读和维护
import re address = re.compile('[\w\d.+-]+@([\w\d.]+\.)+(com|org|edu)') candidates = [ u'first.last@example.com', u'first.last+category@gmail.com', u'valid-address@mail.example.com', u'not-valid@example.foo', ] for candidate in candidates: match = address.search(candidate) print('{:<30} {}'.format( candidate, 'Matches' if match else 'No match') )
详细模式的修改之后:
import re address = re.compile( ''''' [\w\d.+-]+ # username @ ([\w\d.]+\.)+ # domain name prefix (com|org|edu) # TODO: support more top-level domains ''', re.VERBOSE) candidates = [ u'first.last@example.com', u'first.last+category@gmail.com', u'valid-address@mail.example.com', u'not-valid@example.foo', ] for candidate in candidates: match = address.search(candidate) print('{:<30} {}'.format( candidate, 'Matches' if match else 'No match'), )
re模块下的常用方法
import re ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里 print(ret) #结果 : ['a', 'a'] ret = re.search('a', 'eva egon yuan').group() print(ret) #结果 : 'a' # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 ret = re.match('a', 'abc').group() # 同search,不过尽在字符串开始处进行匹配 print(ret) #结果 : 'a' ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 print(ret) # ['', '', 'cd'] ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个 print(ret) #evaHegon4yuan4 ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次) print(ret) obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字 ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串 print(ret.group()) #结果 : 123 import re ret = re.
finditer
('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器 print(ret) # <callable_iterator object at 0x10195f940> print(next(ret).group()) #查看第一个结果 print(next(ret).group()) #查看第二个结果 print([i.group() for i in ret]) #查看剩余的左右结果
注意:
1 findall的优先级查询:
import re ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['www.oldboy.com']
2 split的优先级查询
ret=re.split("\d+","eva3egon4yuan") print(ret) #结果 : ['eva', 'egon', 'yuan'] ret=re.split("(\d+)","eva3egon4yuan") print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan'] #在匹配部分加上()之后所切出的结果是不同的, #没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项, #这个在某些需要保留匹配部分的使用过程是非常重要的。
练习和扩展
1、匹配标签
import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>") #还可以在分组中利用?<name>的形式给分组起名字 #获取的匹配结果可以直接用group('名字')拿到对应的值 print(ret.group('tag_name')) #结果 :h1 print(ret.group()) #结果 :<h1>hello</h1> ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>") #如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致 #获取的匹配结果可以直接用group(序号)拿到对应的值 print(ret.group(1)) print(ret.group()) #结果 :<h1>hello</h1>
2、匹配整数
import re ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))") print(ret) #['1', '2', '60', '40', '35', '5', '4', '3'] ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
# findall 首先匹配()里面的,所以
print(ret) #['1', '-2', '60', '', '5', '-4', '3'] ret.remove("") print(ret) #['1', '-2', '60', '5', '-4', '3']
联系列表:[上海浦东发展银行白金专线]主叫1次共5.48分钟,被叫0次共0.0分钟;
[工商银行]主叫4次共28.36666666分钟,被叫0次共0.0分钟;[建设银行信用卡服务热线]主叫3次共9.26666666分钟,被叫0次共0.0分钟;
[浦发银行信用卡热线]主叫0次共0.0分钟,被叫1次共0.58分钟
re.compile('[^\.\d](\d+)[^\.\d]')
3、数字匹配
1、 匹配一段文本中的每行的邮箱 http://blog.csdn.net/make164492212/article/details/51656638 2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’; 分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、 一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 3、 匹配qq号。(腾讯QQ号从10000开始) [1,9][0,9]{4,} 4、 匹配一个浮点数。 ^(-?\d+)(\.\d+)?$ 或者 -?\d+\.?\d* 5、 匹配汉字。 ^[\u4e00-\u9fa5]{0,}$ 6、 匹配出所有整数
4、爬虫练习
import requests import re import json def getPage(url): response=requests.get(url) return response.text def parsePage(s): com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',re.S) ret=com.finditer(s) for i in ret: yield { "id":i.group("id"), "title":i.group("title"), "rating_num":i.group("rating_num"), "comment_num":i.group("comment_num"), } def main(num): url='https://movie.douban.com/top250?start=%s&filter='%num response_html=getPage(url) ret=parsePage(response_html) print(ret) f=open("move_info7","a",encoding="utf8") for obj in ret: print(obj) data=json.dumps(obj,ensure_ascii=False) f.write(data+"\n") if __name__ == '__main__': count=0 for i in range(10): main(count) count+=25
简化版
import re import json from urllib.request import urlopen def getPage(url): response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): com = re.compile( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S) ret = com.finditer(s) for i in ret: yield { "id": i.group("id"), "title": i.group("title"), "rating_num": i.group("rating_num"), "comment_num": i.group("comment_num"), } def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num response_html = getPage(url) ret = parsePage(response_html) print(ret) f = open("move_info7", "a", encoding="utf8") for obj in ret: print(obj) data = str(obj) f.write(data + "\n") count = 0 for i in range(10): main(count) count += 25
计算器
实现能计算类似
1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式的计算器程序
简单版
# _*_ coding:utf-8 _*_ import re s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' s = ''.join(s.split()) #去除空格 print(eval(s)) #验证效果 def count(s): #计算没有括号的算数 for i in s: if i == '*': cheng = re.search('-?\d+\.?\d*\*-?\d+\.?\d*',s) s = s.replace(cheng.group(),'+%s'%(str(float(cheng.group().split('*')[0])*float(cheng.group().split('*')[1])))) elif i == '/': chu = re.search('-?\d+\.?\d*\/-?\d+\.?\d*',s) s = s.replace(chu.group(),'+%s'%(str(float(chu.group().split('/')[0])/float(chu.group().split('/')[1])))) if re.search('\+\++',s):s = s.replace(re.search('\+\++',s).group(),'+') if s[0] == '+':s = s.strip('+') while '-'in s or '+' in s: jia=re.search('-?\d+\.?\d*\+-?\d+\.?\d*',s) if not jia: if s[0] != '-':s = str(float(re.split('-',s)[0])-float(re.split('-',s)[1])) elif s.count('-') == 2:s = '-%s'%(str(float(re.findall('\d+\.?\d*',s)[0])+float(re.findall('\d+\.?\d*',s)[1]))) break s = s.replace(jia.group(), '+%s' %(str(float(jia.group().split('+')[0])+float(jia.group().split('+')[1])))) if s[0] == '+':s = s.strip('+') return s while '('in s: #有括号时 取括号内的式子计算 res = re.findall('\(([^()]+)\)', s) #去括号 for i in range(len(res)): s = s.replace('(%s)' % res[i], count(res[i])) if '--' in s: s = s.replace('--', '+') print(count(s)) #没有括号,进行最后一步计算