subprocess、re、logging模块
一、subprocess模块
subprocess模块
- 可以通过python代码给操作系统终端发送命令,并且可以返回结果。
1 import subprocess 2 while True: 3 # 让用户输入终端命令 4 cmd_str = input('请输入终端命令:').strip() 5 # Popen(cmd命令, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 6 # 调用Popen就会将用户的终端命令发送给本地操作系统的终端 7 # 得到一个对象,对象中包含着正确或错误的结果。 8 popen_obj = subprocess.Popen( 9 cmd_str, shell=True, 10 stdout=subprocess.PIPE, stderr=subprocess.PIPE 11 ) 12 success = popen_obj.stdout.read().decode('gbk') 13 if success: 14 print('正确命令!\n', success) 15 error = popen_obj.stderr.read().decode('gbk') 16 if error: 17 print('错误命令!\n', error)
二、re模块
re模块:
在python中,若想使用正则表达式,必须通过re模块来实现。
1、什么是正则表达式
- 正则表达式:
正则表达式是一门独立的技术, 任何语言都可以使用正则表达式,
正则表达式是由一堆特殊的字符组合而来的。
- 字符组
- 元字符
- 组合使用
2、为什么要使用正则?
比如要获取“一堆字符串”中的“某些字符”,
正则表达式可以帮我们过滤,并提取出想要的字符数据。
# 比如过滤并获取 一段字符串中指定的字符串
- 应用场景:
- 爬虫: re, BeautifulSoup4, Xpath, selector
- 数据分析过滤数据: re, pandas, numpy...
- 用户名与密码、手机认证:检测输入内容的合法性
3、如何使用
检测手机号码的合法性, 需求: 11位、开头13/14/15/18
1 # 纯python校验 2 # 需求: 11位、开头13/14/15/18 3 while True: 4 phone_number = input('输入手机号:').strip() 5 if phone_number.isdigit() and len(phone_number) == 11 and ( 6 phone_number.startswith('13') or 7 phone_number.startswith('14') or 8 phone_number.startswith('15') or 9 phone_number.startswith('18') 10 ): 11 print('手机号码合格') 12 else: 13 print('手机号码不合格')
1 # re效验 2 import re 3 while True: 4 phone_number = input('输入手机号:').strip() 5 # 需求: 11位、开头13/14/15/18 6 # 参数1: 正则表达式 '' 7 # 参数2: 需要过滤的字符串 8 # ^: 代表“开头” 9 # $: 代表“结束” 10 # |: 代表“或” 11 # (13|14): 可以获取一个值,判断是否是13或14. 12 # {1}: 需要获取1个值 限制数量 13 # []: 分组限制取值范围 14 # [0-9]: 限制只能获取0——9的某一个字符。 15 re_obj = re.match('^(13|14|15|18)[0-9]{9}', phone_number) 16 # print(re_obj) 17 if re_obj: 18 print('手机号码合格') 19 else: 20 print('手机号码不合格')
1 """ 2 - 字符组: 3 - [0-9] 可以匹配到一个0-9的字符 4 - [9-0]: 报错, 必须从小到大 5 - [a-z]: 从小写的a-z 6 - [A-Z]: 从大写A-Z 7 - [z-A]: 错误, 只能从小到大,根据ascii表来匹配大小。 8 - [A-z]: 总大写的A到小写的z。 9 10 注意: 顺序必须要按照ASCII码数值的顺序编写。 11 """ 12 import re 13 res = re.match('[A-Za-z0-9]{8}', 'yang1234') 14 print(res.group())
1 """ 2 - 元字符: 3 *******根据博客的表格来记 (看一眼) 4 https://images2015.cnblogs.com/blog/1036857/201705/1036857-20170529203214461-666088398.png 5 6 - 组合使用 7 - \w\W: 匹配字母数字下划线与非字母数字下划线,匹配所有。 8 - \d\D: 无论是数字或者非数字都可以匹配。 9 - \t: table 10 - \n: 换行 11 - \b: 匹配单词结尾,tank jasonk 12 - ^: startswith 13 - '^'在外面使用: 表示开头。 14 - [^]: 表示取反的意思。 15 16 - $: endswith 17 18 - ^$: 配合使用叫做精准匹配,如何限制一个字符串的长度或者内容。 19 - |: 或。ab|abc如果第一个条件成立,则abc不会执行,怎么解决,针对这种情况把长的写在前面就好了,一定要将长的放在前面。 20 - [^...]: 表示取反的意思。 21 - [^ab]: 代表只去ab以外的字符。 22 - [^a-z]: 取a-z以外的字符。 23 """
""" re模块三种比较重要的方法: - findall(): ----> [] 可以匹配 "所有字符" ,拿到返回的结果,返回的结果是一个列表。 'awfwaghowiahioawhio' # a ['a', 'a', 'a', 'a'] - search():----> obj ----> obj.group() 'awfwaghowiahioawhio' # a 在匹配一个字符成功后,拿到结果后结束,不往后匹配。 'a' - match():----> obj ----> obj.group() 'awfwaghowiahioawhio' # a 'a' 'wfwaghowiahioawhio' # a None 从匹配字符的开头匹配,若开头不是想要的内容,则返回None。 """
1 import re 2 str1 = '1234 abcd 5678 efgh ==++' 3 # findall 4 # res = re.findall('[0-9]{4}', str1) 5 # print(res) # ['1234', '5678'] 6 7 # search 8 # res = re.search('[0-9]{4}', str1) 9 # print(res.group()) # 1234 10 11 # match 12 res = re.match('[0-9]{4}', str1) 13 # print(res.group()) 14 if res: 15 print(res.group())
4、爬虫应用
1 """ 2 爬蟲四部原理: 3 1.发送请求: requests 4 2.获取响应数据: 对方机器直接返回的 5 3.解析并提取想要的数据: re 6 4.保存提取后的数据: with open() 7 8 爬蟲三部曲: 9 1.发送请求 10 2.解析数据 11 3.保存数据 12 """ 13 14 import requests 15 import re 16 17 # 爬蟲三部曲: 18 # 1.发送请求 19 def get_page(url): 20 response = requests.get(url) 21 # response.content # 获取二进制流数据,比如图片、视频、音频 22 # response.text # 获取响应文本,比如html代码 23 return response 24 25 # 2.解析数据 26 # 伪代码: 27 # response = get_page('url地址') 28 # parser_page(response.text) 29 def parser_page(text): # response.text 30 # re.findall('正则表达式', '过滤的文本') 31 res_list = re.findall( 32 '<table width="100%" border="0".*?<a href="(.*?)" class="ulink">(.*?)</a>', 33 text, 34 re.S # 全局匹配 35 ) 36 for moive_tuple in res_list: 37 yield moive_tuple 38 39 # 3.保存数据 40 # 伪代码: 41 # res_list = parser_page(text) 42 # save_data(res_list) 43 def save_data(res_list_iter): 44 with open('电影天堂.txt', 'a', encoding='utf-8')as f: 45 for movie_tuple in res_list_iter: 46 movie_url, name = movie_tuple 47 str1 = f""" 48 电影地址:{movie_url} 49 电影名字:{name} 50 """ 51 f.write(str1) 52 53 # 获取10个链接 54 n = 1 55 for line in range(10): 56 url = f'https://www.ygdy8.net/html/gndy/dyzz/list_23_{n}.html' 57 n += 1 58 print(url) 59 60 response = get_page(url) 61 res_list_iter = parser_page(response.text) 62 save_data(res_list_iter)
三、logging模块
""" logging模块的使用 - 是用来记录日志的模块,一般记录用户在软件中的操作。 def get_logger(user_type): # 1.加载log配置字典到logging模块的配置中 logging.config.dictConfig(LOGGING_DIC) # 2.获取日志对象 logger = logging.getLogger(user_type) return logger logger = get_logger('user') logger.info('日志消息') """
1 import os 2 import logging.config 3 4 # 定义三种日志输出格式 开始 5 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ 6 '[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字 7 8 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' 9 10 id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' 11 12 # 定义日志输出格式 结束 13 # 注意1: log文件的目录 14 BASE_PATH = os.path.dirname(os.path.dirname(__file__)) 15 logfile_dir = os.path.join(BASE_PATH, 'log_dir') 16 17 # 注意2: log文件名 18 logfile_name = 'user.log' 19 20 # 如果不存在定义的日志目录就创建一个 21 if not os.path.isdir(logfile_dir): 22 os.mkdir(logfile_dir) 23 24 # log文件的全路径 25 logfile_path = os.path.join(logfile_dir, logfile_name) 26 27 # 注意3: log配置字典,直接拿来用就可以了 28 LOGGING_DIC = { 29 'version': 1, 30 'disable_existing_loggers': False, 31 'formatters': { 32 'standard': { 33 'format': standard_format 34 }, 35 'simple': { 36 'format': simple_format 37 }, 38 }, 39 'filters': {}, 40 'handlers': { 41 #打印到终端的日志 42 'console': { 43 'level': 'DEBUG', 44 'class': 'logging.StreamHandler', # 打印到屏幕 45 'formatter': 'simple' 46 }, 47 # 打印到文件的日志,收集info及以上的日志 48 'default': { 49 'level': 'DEBUG', 50 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 51 'formatter': 'standard', 52 'filename': logfile_path, # 日志文件 53 'maxBytes': 1024*1024*5, # 日志大小 5M 54 'backupCount': 5, 55 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 56 }, 57 }, 58 'loggers': { 59 #logging.getLogger(__name__)拿到的logger配置 60 '': { 61 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 62 'level': 'DEBUG', 63 'propagate': True, # 向上(更高level的logger)传递 64 }, 65 }, 66 } 67 68 # 注意4 使用模块 69 def get_logger(user_type): 70 # 1.加载log配置字典到logging模块的配置中 71 logging.config.dictConfig(LOGGING_DIC) 72 73 # 2.获取日志对象 74 logger = logging.getLogger(user_type) 75 return logger 76 77 logger = get_logger('yang') 78 logger.info('登录成功')
防止导入模块时自动执行测试功能
if __name__ == '__main__':
执行测试模块
四、包
1.什么是包?
包是一个带有__init__.py的文件夹,包也可以被导入,
并且可以一并导入包下的所有模块。
2.为什么要使用包?
包可以帮我们管理模块,在包中有一个__init__.py, 由它来帮我们管理模块。
3.怎么使用包?
- import 包.模块名
包.模块.名字
- from 包 import 模块名
- from 包.模块名 import 模块中的名字
- 导入包时发生的事情:
1.当包被导入时,会以包中的__init__.py来产生一个名称空间。
2.然后执行__init__.py文件, 会将__init__.py中的所有名字添加到名称空间中。
3.接着会将包下所有的模块的名字加载到__init__.py产生的名称空间中。
4.导入的模块指向的名称空间其实就是__init__.py产生的名称空间中。