第六章 模块

第六章 模块

6.1 模块的定义

  1. 模块的定义
    • py文件 写好了的 对程序员直接提供某方面功能的文件
  2. 包的定义re
    • 文件夹 存储了多个py文件的文件夹
    • 如果导入的是一个包,这个包里的模块默认是不能用的
    • 导入一个包相当于执行_ _ init _ _.py文件中的内容
  3. python2与python3的区别七
    • python2:文件夹中必须有_ _ init _ _.py文件
    • python3:不需要有_ _ init _ _.py文件
    • 建议:推荐以后写代码,无论是python2还是python3,都要加上此文件

6.2 模块的分类(类库)

6.2.1 内置模块

  • python内部提供的功能
  • 导入模块后,直接使用即可

6.2.1.1 random

  • 随机数模块
  1. randint:得到一个随机数
import random    # 导入一个模块 
v = random.randint(起始,终止)    # 得到一个随机数

#示例:生成随机验证码
import random     
def get_random_code(length=6):
    data = []
    for i in range(length):
        v = random.randint(65,90)  
        data.append(chr(v))		#ascci码中65-90 代表大小写字母
    return  ''.join(data)

code = get_random_code()
print(code)
  1. uniform:生成一个随机小数

  2. choice:抽取一个对象

    • 应用:验证码、抽奖

    • print(random.choice([1,2,3,4,5])) #1
      
  3. sample:抽取多个对象

    • 应用:一个奖项抽取多个人

    • print(random.sample([1,2,3],2)) #随机抽2个
      
  4. shuffle:打乱顺序

    • 应用:洗牌、算法

6.2.1.2 hashlib

  • 摘要算法模块

    • 密文验证
    • 校验文件的一致性

这两个加密后的一样,这可以验证文件的一致性

  1. md5
# 将指定的 “字符串” 进行 加密
import hashlib     # 导入一个模块
def get_md5(data):          # md5 加密函数  
    obj = hashlib.md5()
    obj.update(data.encode('utf-8'))
    result = obj.hexdigest()
    return result
val = get_md5('123')
print(val)

# 加盐
import hashlib
def get_md5(data):
    obj = hashlib.md5("sidrsdxff123ad".encode('utf-8'))            # 加盐
    obj.update(data.encode('utf-8'))
    result = obj.hexdigest()
    return result
val = get_md5('123')
print(val)
# 应用:用户注册+用户登录
import hashlib
USER_LIST = []
def get_md5(data):                   # md5 加密函数 
    obj = hashlib.md5("12:;idrsicxwersdfsaersdfs123ad".encode('utf-8'))         # 加盐
    obj.update(data.encode('utf-8'))
    result = obj.hexdigest()
    return result

def register():                      # 用户注册函数
    print('**************用户注册**************')
    while True:
        user = input('请输入用户名:')
        if user == 'N':
            return
        pwd = input('请输入密码:')
        temp = {'username':user,'password':get_md5(pwd)}
        USER_LIST.append(temp)

def login():                          # 用户登录函数
    print('**************用户登陆**************')
    user = input('请输入用户名:')
    pwd = input('请输入密码:')
    for item in USER_LIST:
        if item['username'] == user and item['password'] == get_md5(pwd):
            return True

register()
result = login()
if result:
    print('登陆成功')
else:
    print('登陆失败')
  1. sha

    import hashlib
    md5 = hashlib.sha1('盐'.encode())
    md5.update(b'str')
    print(md5.hexdigest())
    

6.2.1.3 getpass

  • 只能在终端运行
  1. getpass.getpass:输入密码时不显示
import getpass        # 导入一个模块
pwd = getpass.getpass('请输入密码:')
if pwd == '123':
    print('输入正确')

6.2.1.4 time【常用】

  • 时间模块
  1. time.time:时间戳(从1970年到现在经历的秒数)

    # https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=4ZwIFHM6iw==&tip=1&r=-781028520&_=1555559189206
    
  2. time.sleep:等待的秒数

  3. time.timezone

  • 示例

    # 计算函数执行时间
    import time
    def wrapper(func):
        def inner():
            start_time = time.time()
            v = func()
            end_time = time.time()
            print(end_time-start_time)
            return v
        return inner
    @wrapper
    def func1():
        time.sleep(2)
        print(123)   
    func1()
    

6.2.1.5 datetime

  • 时间模块
  1. datetime.now():当前本地时间

  2. datetime.utcnow():当前UTC时间

    import time,timezone,timedelta
    from datetime import datetime,timezone,timedelta
    # 获取datetime格式时间
    # 当前本地时间
    v1 = datetime.now() 
    
    # 当前东7区时间
    tz = timezone(timedelta(hours=7)) 
    v2 = datetime.now(tz)
    
    # 当前UTC时间
    v3 = datetime.utcnow() 
    print(v3)
    
  • 相互转换

    import time
    from datetime import datetime,timedelta
    # 1.datetime格式和字符串的相互转换
    # 把datetime格式转换成字符串:strftime
    v1 = datetime.now()
    val = v1.strftime("%Y-%m-%d %H:%M:%S")
    # 字符串转成datetime格式:strptime
    v1 = datetime.strptime('2011-11-11','%Y-%m-%d')
    
    # 2.datetime时间的加减
    v1 = datetime.strptime('2011-11-11','%Y-%m-%d')
    v2 = v1 - timedelta(days=140)
    # 再转换成字符串
    date = v2.strftime('%Y-%m-%d')
    
    # 3.时间戳和datetime的相互转换
    # 时间戳转换成datetime格式:fromtimestamp
    ctime = time.time()
    v1 = datetime.fromtimestamp(ctime)
    # datetime格式转换成时间戳:timestamp
    v1 = datetime.now().timestamp()
    例子:
    print(time.mktime(datetime.now().timetuple()))
    # 4.前一天时间
      import datetime
     time_yesterday = datetime.datetime.now() + datetime.timedelta(-1)
     time_yesterday1 = time_yesterday.strftime("%Y%m%d")  # 格式化时间 前1天的时间
    

6.2.1.6 sys

  • python解释器相关数据
  1. sys.getrefcount:获取一个值的应用计数

  2. sys.getrecursionlimit:python默认支持的递归数量

  3. sys.stdout.write:输入输出

    • 补充:\n:换行 \t:制表符 \r:回到当前行的起始位置

      import time
      for i in range(1,101):
          msg = "%s%%\r" %i
          print(msg,end='')
          time.sleep(0.05)
      
    • 示例:读取文件的进度条

    import os
    # 1. 读取文件大小(字节)
    file_size = os.stat('20190409_192149.mp4').st_size
    
    # 2.一点一点的读取文件
    read_size = 0
    with open('20190409_192149.mp4',mode='rb') as f1,open('a.mp4',mode='wb') as f2:
        while read_size < file_size:
            chunk = f1.read(1024) # 每次最多去读取1024字节
            f2.write(chunk)
            read_size += len(chunk)
            val = int(read_size / file_size * 100)
            print('%s%%\r' %val ,end='')
    
  4. sys.argv:获取用户执行脚本时,传入的参数

    • 示例:让用户执行脚本传入要删除的文件路径,在内部帮助用将目录删
    """
    让用户执行脚本传入要删除的文件路径,在内部帮助用将目录删除。
    C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py D:/test
    C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py
    """
    
    import sys
    # 获取用户执行脚本时,传入的参数。
    # C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py D:/test
    # sys.argv = [D:/code/s21day14/7.模块传参.py, D:/test]
    path = sys.argv[1]
    
    # 删除目录
    import shutil
    shutil.rmtree(path)
    
  5. sys.exit(0):程序终止,0代表正常终止

  6. sys.path:默认python去导入模块时,会按照sys.path中的路径

    • 添加目录:sys.path.append('目录')
    import sys
    sys.path.append('D:\\goodboy')
    
    如果导入模块报错,可以在sys.path中加搜索路径
    import sys, os
    sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))))
    
    
  7. sys.modules:存储了当前程序中用到的所有模块,反射本文件中的内容

    ab = 'test'
    import sys,demo
    sys.modules['__name__']
    print(getattr(sys.modules['____']),'ab')  #可以拿到test。用反射可以得到类中的方法名字
    

6.2.1.7 os【常用】

  • 和操作系统相关的数据
  1. os.path.exists(path):如果path存在,返回True;如果path不存在,返回False

  2. os.stat('文件路径').st_size / os.path.getsize :获取文件大小

  3. os.path.abspath():获取一个文件的绝对路径

    import os
    os.path.abspath(__file__)  #找到运行脚本的绝对路径
    v1 = os.path.abspath(path)
    print(v1)
    
  4. os.path.dirname():获取路径的上级目录

    import os
    v = r"D:\code\s21day14\20190409_192149.mp4"
    print(os.path.dirname(v))
    
    • 补充:转义

      v1 = r"D:\code\s21day14\n1.mp4"  (推荐) 加了r就相当于转义了
      v2 = "D:\\code\\s21day14\\n1.mp4"
      
  5. os.path.join:路径的拼接

    import os
    path = "D:\code\s21day14" # user/index/inx/fasd/
    v = 'n.txt'
    result = os.path.join(path,v)
    print(result)
    
  6. os.listdir:查看一个目录下所有的文件【第一层】

    import os
    result = os.listdir(r'D:\code\s21day14')
    for path in result:
        print(path)
    
  7. os.walk:查看一个目录下所有的文件【所有层】

    import os
    result = os.walk(r'D:\code\s21day14')
    for a,b,c in result:
        # a,正在查看的目录 b,此目录下的文件夹  c,此目录下的文件
        for item in c:
            path = os.path.join(a,item)
            print(path)
    
  8. os.makedir:创建目录,只能生产一层目录(基本不用这个)

  9. os.makedirs:创建目录及其子目录(推荐使用)

    # 将内容写入指定文件中
    import os
    file_path = r'db\xx\xo\xxxxx.txt'
    file_folder = os.path.dirname(file_path)
    if not os.path.exists(file_folder):
        os.makedirs(file_folder)
    with open(file_path,mode='w',encoding='utf-8') as f:
        f.write('asdf')
    
  10. os.rename:重命名

    # 将db重命名为sb
    import os
    os.rename('db','sb')
    
  11. os.path.isdir:判断是否是文件夹

  12. os.path.isfile:判断是否是文件

6.2.1.8 shutil

  • 用途:删除、重命名、压缩、解压等
  1. shutil.rmtree(path):删除目录

    # 删除目录
    import shutil
    shutil.rmtree(path)
    
  2. shutil.move:重命名

    # 重命名
    import shutil
    shutil.move('test','ttt')
    
  3. shutil.make_archive:压缩文件 #本质是调用的zipfile模块

    # 压缩文件
    import shutil
    shutil.make_archive('zzh','zip','D:\code\s21day16\lizhong')
    
  4. shutil.unpack_archive:解压文件 #本质是调用的zipfile模块

    # 解压文件
    import shutil
    shutil.unpack_archive('zzh.zip',extract_dir=r'D:\code\xxxxxx\xxxx',format='zip')
    
  • 示例

    import os
    import shutil
    from datetime import datetime
    ctime = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
    # 1.压缩lizhongwei文件夹 zip
    # 2.放到到 code 目录(默认不存在)
    # 3.将文件解压到D:\x1目录中。
    if not os.path.exists('code'):
        os.makedirs('code')
    shutil.make_archive(os.path.join('code',ctime),'zip','D:\code\s21day16\lizhongwei')
    
    file_path = os.path.join('code',ctime) + '.zip'
    shutil.unpack_archive(file_path,r'D:\x1','zip')
    

6.2.1.8 deepdiff

参考: https://www.cnblogs.com/Neeo/articles/12785493.html

6.2.1.9 json

  • json是一个特殊的字符串,长得像列表/字典/字符串/数字等嵌套
  • 序列化:把python中的值转化为json格式的字符串
  • 反序列化:将json格式的字符串转换成python的数据类型
  • json格式要求:本质是字符串
    • 只包含 int / float / str / list / dict
    • 最外层必须是 list / dict 除去引号的最外层
    • 在json中,内部str必须是双引号
    • 存在字典 字典的key只能是str
    • 不能连续load多次
  1. json.dumps():序列化

    • json只支持 dict / list / typle / str / int / float / True / False / None 序列化
    • 字典或列表中如果有中文,序列化时,如果想保留中文显示 ensure_ascii 保留中文
    import json
    v = {'k1':'alex','k2':'李杰'}
    val = json.dumps(v,ensure_ascii = False)  #ensure_ascii 保留中文
    #{"k1": "alex", "k2": "李杰"}
    
    import json
    info={'k1':123,'k2':456}
    print(json.dumps(info,separators=(",",":"))) #{"k1":123,"k2":456}   冒号前面和逗号前面不加空格
    
  2. json.loads():反序列化

    import json
    # 序列化,将python的值转换为json格式的字符串。
    v = [12,3,4,{'k1':'v1'},True,'asdf']
    v1 = json.dumps(v)
    print(v1)
    
    # 反序列化,将json格式的字符串转换成python的数据类型
    v2 = '["alex",123]'
    print(type(v2))
    v3 = json.loads(v2)
    print(v3,type(v3))
    
  3. json.dump:打开文件,序列化后,写入文件

    import json
    v = {'k1':'alex','k2':'李杰'}
    f = open('x.txt',mode='w',encoding='utf-8')
    val = json.dump(v,f)
    print(val)
    f.close()
    
  4. json.load:打开文件,读取文件内容

    import json
    v = {'k1':'alex','k2':'李杰'}
    f = open('x.txt',mode='r',encoding='utf-8')
    data = json.load(f)
    f.close()
    print(data,type(data))
    

6.2.1.10 pickle

  • pickle与json的区别
    • json
      • 优点:所有语言通用
      • 缺点:只能序列化基本的数据类型 list/dict 等
    • pickle
      • 优点:python中所有的东西都能被他序列化(socket对象),支持连续load多次
      • 缺点:序列化的内容只有python认识
  1. pickle.dumps:序列化

    • 序列化后的东西不可读
  2. pickle.loads:反序列化

    import pickle
    # 序列化
    v = {1,2,3,4}
    val = pickle.dumps(v)
    print(val)
    
    # 反序列化
    data = pickle.loads(val)
    print(data,type(data))
    
  3. pickle.dump:写入文件(注意:mode='wb')

  4. pickle.load:读取文件(注意:mode='rb')

    import pickle
    # 写入文件
    v = {1,2,3,4}
    f = open('x.txt',mode='wb')
    val = pickle.dump(v,f)
    f.close()
    
    # 读取文件
    f = open('x.txt',mode='rb')
    data = pickle.load(f)
    f.close()
    print(data)
    

6.2.1.11 copy

  • 拷贝模块
  1. copy.copy:浅拷贝

  2. copy.deepcopy:深拷贝

    import copy 
    v1 = [1,2,3]      
    v2 = copy.copy(v1)             #浅拷贝
    v3 = copy.deepcopy(v1)         #深拷贝
    

6.2.1.12 importlib

  1. importlib.import_module:通过字符串的形式导入模块

  2. os = import('os')

    这样也可以导入os模块

    #示例一:
    import importlib
    # 用字符串的形式导入模块。
    redis = importlib.import_module('utils.redis')
    
    # 用字符串的形式去对象(模块)找到他的成员。
    getattr(redis,'func')()
    
    #示例二:
    import importlib
    middleware_classes = [
        'utils.redis.Redis',
        # 'utils.mysql.MySQL',
        'utils.mongo.Mongo'
    ]
    for path in middleware_classes:
        module_path,class_name = path.rsplit('.',maxsplit=1)
        module_object = importlib.import_module(module_path)# from utils import redis
        cls = getattr(module_object,class_name)
        obj = cls()
        obj.connect()
    

6.2.1.13 logging

  • 日志模块:记录日志的

    • 给用户看的:流水类,如银行流水
    • 给程序员看的:
      • 统计用的
      • 用来做故障排除的,debug
      • 用来记录错误,完成代码的优化
  • 日志处理本质:Logger / FileHandler / Formatter

  • 两种配置方式:

    • logging.basicConfig

      • 优点:使用方便
      • 缺点:不能实现编码问题,不能同时向文件和屏幕上输出
      • 本质是logging.debug/logging.waring
    • logger对象

      • 优点:能实现编码问题,能同时向文件和屏幕上输出
      • 缺点:复杂
      • 本质是操作logger对象,吸星大法
    • 示例:

      import logging
      # 创建一个logger对象
      logger = logging.getLogger()
      # 创建一个文件操作符
      fh = logging.FileHandler('log.log')
      # 创建一个屏幕操作符
      sh = logging.StreamHandler()
      # 给logger对象绑定 文件操作符
      logger.addHandler(fh)
      # 给logger对象绑定 屏幕操作符
      logger.addHandler(sh)
      # 创建一个格式
      formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
      # 给文件操作符 设定格式
      fh.setFormatter(formatter)
      # 给屏幕操作符 设定格式
      sh.setFormatter(formatter)
      # 用logger对象来操作
      logger.warning('message')
      
  • 日志异常级别

    CRITICAL = 50       # 崩溃
    FATAL = CRITICAL
    ERROR = 40          # 错误
    WARNING = 30
    WARN = WARNING
    INFO = 20
    DEBUG = 10
    NOTSET = 0
    
    #level=logging.ERROR 这里时候设置的ERROR
    #logging.error('你好') 只要超过这个级别就会打到日志中
    
  • 日志处理的本质,适用于多个日志文件,创建不同logger对象即可。

import logging

file_handler = logging.FileHandler('x1.log','a',encoding='utf-8')
fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s")
file_handler.setFormatter(fmt)

logger =logging.Logger('XXX',level=logging.ERROR)  #创建
logger.addHandler(file_handler)

logger.error('你好')
#上面的封装成类,方便调用
import logging
class Logger():
    def __init__(self,name,filename,level=logging.ERROR):
        file_handler = logging.FileHandler(filename,'a',encoding='utf-8')
        fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s")
        file_handler.setFormatter(fmt)

        self.logger =logging.Logger(name,level=level)  #创建
        self.logger.addHandler(file_handler)

    def info(self,msg):
        self.logger.info(msg)
    def debug(self,msg):
        self.logger.debug(msg)
    def error(self,msg):
        self.logger.error(msg)

#直接调用,想用多个,实例多个logger对象即可
logger=Logger('log1','1.log')
logger.error('你好')


  • 推荐处理日志方式 # basicConfig多次配置是无效的(只取第一个),适用于只记录一个日志文件

    import loggingwenjai你
    
    file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
    
    logging.basicConfig(
        format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S %p',
        handlers=[file_handler,],
        level=logging.ERROR
    )  #logging.basicConfig 内部会把这些配置到logging对象中
    
    logging.error('你好')
    
  • 推荐处理日志方式 + 日志分割

    import time
    import logging
    from logging import handlers
    # file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
    file_handler = handlers.TimedRotatingFileHandler(filename='x3.log', when='s', interval=5, encoding='utf-8')
    logging.basicConfig(
        format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S %p',
        handlers=[file_handler,],
        level=logging.ERROR
    )
    
    for i in range(1,100000):
        time.sleep(1)
        logging.error(str(i))
    

    注意事项:想要保留异常的堆栈信息。

    # 在应用日志时,如果想要保留异常的堆栈信息。
    import logging
    import requests
    
    logging.basicConfig(
        filename='wf.log',
        format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S %p',
        level=logging.ERROR
    )
    
    try:
        requests.get('http://www.xxx.com')
    except Exception as e:
        msg = str(e)  # str()本质是调用e.__str__方法
        logging.error(msg,exc_info=True) 
        #这里加exc_info=True 会把异常代码打到日志里
    

6.2.1.14 collections

  1. OrderedDict:有序字典

    from collections import OrderedDict
    odic = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
    print(odic)
    for k in odic:
        print(k,odic[k])
    
  2. defaultdict:默认字典。可以给字典的value设置一个默认值

  3. deque:双端队列

  4. namedtuple:可命名元组

    # 创建一个类,这个类没有方法,所有属性的值都不能修改
    from collections import namedtuple       # 可命名元组
    Course = namedtuple('Course',['name','price','teacher'])
    python = Course('python',19800,'alex')
    print(python)
    print(python.name)
    print(python.price)
    

6.2.1.14.1 functools,itertools

from functools import wraps  #装饰器中用的
import itertools

v1=[11,22,33]
v2=[55,66,77]
v=itertools.chain(v1,v2)  #把两个列表组合,不会创建新的列表,创建的是一个对象
for item in v:
    print(item) #


#偏函数partial
def func(f1,f2):
    print(f1)
    print(f2)
func1 = functools.partial(func,'x1',) #下次调用函数 f1的值就不用填了
func1('x2') #传第二个参数即可

6.2.1.15 re

正则表达式

手册:https://tool.oschina.net/uploads/apidocs/jquery/regexp.html

  1. 定义
  • 定义:正则表达式是一种规则匹配字符串的规则
  • re模块本身只是用来操作正则表达式的,和正则本身没关系
  • 为什么要有正则表达式?
    • 匹配字符串
      • 一个人的电话号码
      • 一个人的身份证号
      • 一台机器的ip地址
    • 表单验证
      • 验证用户输入的信息是否准确
      • 银行卡号
    • 爬虫
      • 从网页源码中获取一些链接、重要数据
  1. 正则规则
  • 第一条规则 : 本身是哪一个字符,就匹配字符串中的哪一个字符
  • 第二条规则 : 字符组[字符1字符2],一个字符组就代表匹配一个字符,只要这个字符出现在字符组里,那么就说明这个字符能匹配上
    • 字符组中还可以使用范围
    • 所有的范围都必须遵循ascii码从小到大来指定
    • 常用:[0-9] [a-z] [A-Z]
  1. 元字符 (都是匹配一个字符)
  • \d(digit):表示所有的数字

    • \是转义符 转义符转义了d,让d能够匹配所有0-9之间的数
  • \w(word):表示大小写字母、数字、下划线

  • \s(space):表示空白、空格、换行符、制表符

  • (\t(table):匹配制表符\n(next):匹配换行符)

  • \D:表示所有的非数字

  • \W:表示除数字、字母、下划线之外的所有字符

  • \S:表示非空白

  • . :表示除了换行符之外的任意内容

  • [] 字符组:只要在中括号内的所有字符都是符合规则的字符 (为了匹配的更精准)

  • [^ ]非字符组:只要在中括号内的所有字符都是不符合规则的字符

  • ^:表示一个字符的开始 ^a 这个字符串中以^a开头的

  • $:表示一个字符的结束 a$

    (^abc$) 只能匹配到abc

  • |:表示或 (想匹配一个或者另外一个)

    • 注意:如果两个规则有重叠部分,总是长的在前面,短的在后面 eg: abc|ab 优先匹配abc,匹配不上在匹配ab
  • ():表示分组,给一部分正则规定为一组,|这个符号的作用域就可以缩小了

  • 特殊:

    • [\d]、[0-9]、\d:没有区别 都是要匹配一位数字
    • [\d\D]、[\W\w]、[\S\s] 匹配所有一切字符。只能写到字符组里[ ]
  1. 量词 P: { }只描述前面的元字符出现多少次
  • {n}:表示只能出现n次
  • {n,}:表示至少出现n次
  • {n,m}:表示至少出现n次,至多出现m次
  • ?:表示匹配0次或1次,表示可有可无,但是有只能有一个,比如小数点 #0次或1次 都匹配出来
  • +:表示匹配1次或多次
  • *:表示匹配0次或多次,表示可有可无,但是有可以有多个比如小数点后n位
  • 匹配0次出现的情况:
    • 匹配任意的保留两位小数的数字 \d+.\d
    • 匹配一个整数或者小数(?小数点可有可无,*小数点后几位) \d+(.\d+)? ## .和\d+ 分组绑定在一起,要么一起有,要么都没有
  1. 贪婪匹配
  • 默认贪婪匹配,总是会在符合量词条件的范围内尽量多匹配 \d

  • 非贪婪匹配 :惰性匹配 (这里的?是在量词后面)

    • 总是匹配符合条件范围内尽量小的字符串

    • 格式:元字符 量词 ?x

      • 表示按照元字符规则在量词范围内匹配,一旦遇到x就停止

      • 示例:.*?x 匹配任意的内容任意多次遇到x就立即停止

  1. 转义符:\
  • 正则表达式中的转义符在python的字符串中也刚好有转移的作用
  • 但是正则表达式中的转义符和字符串中的转义符并没关系,且还容易有冲突
  • 为了避免这种冲突,我们所有的正则都以在工具中的测试结果为结果
  • 然后只需要在正则和待匹配的字符串外面都加r即可
邮箱规则
@之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
@和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),且两个点不能挨着
最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字节,小于等于6个字节

邮箱验证的正则表达式:
^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$
import re

regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+')

def isValid(email):
    if re.fullmatch(regex, email):
      print("有效的email地址")
    else:
      print("无效的email地址")

isValid("name.surname@gmail.com") # 有效的email地址
isValid("anonymous123@yahoo.co.uk") # 有效的email地址
isValid("anonymous123@...uk") # 无效的email地址
isValid("...@domain.us") # 无效的email地址


ps :身份证号(15或者18位,不能以0开头,15位位全数字,18位最后一位为x或其他数字)
[1-9](\d{16}[\d|x]|\d{14})
正则模块
  1. re.findall:会匹配字符串中所有符合规则的项,并返回一个列表,如果没匹配到,返回空列表
    findall 查找所有. 返回list

    import re
    ret = re.findall('\d+','alex83')
    print(ret)
    # findall 会匹配字符串中所有符合规则的项
    # 并返回一个列表
    # 如果未匹配到返回空列表
    
  2. re.search:如果匹配到,返回一个对象,用group取值,如果没匹配到,返回None,不能用group
    search 会进行匹配. 但是如果匹配到了第一个结果. 就会返回这个结果. 如果匹配不上search返回的则是None

    import re
    ret = re.search('\d+','alex83')
    print(ret)                 # 如果能匹配上返回一个对象,如果不能匹配上返回None
    if ret:
        print(ret.group())     # 如果是对象,那么这个对象内部实现了group,所以可以取值
                               # 如果是None,那么这个对象不可能实现了group方法,所以报错
    # 会从头到尾从带匹配匹配字符串中取出第一个符合条件的项
    # 如果匹配到了,返回一个对象,用group取值
    # 如果没匹配到,返回None,不能用group
    
  3. re.match:match = search + ^正则

    match 只能从字符串的开头进行匹配(再见)

    ret = re.match('a', 'abc').group()  
    print(ret)     # a
    
    import re
    ret = re.match('\d','alex83') == re.match('^\d','alex83')
    print(ret)
    # 会从头匹配字符串,从第一个字符开始是否符合规则
    # 如果符合,就返回对象,用group取值
    # 如果不符合,就返回None
    
  4. re.finditer:在查询的结果超过1个的情况下,能够有效的节省内存,降低空间复杂度,从而也降低了时间复杂度
    finditer 和findall差不多. 只不过这时返回的是迭代器(重点)

    import re
    ret = re.finditer('\d','safhl02urhefy023908'*20000000)  # ret是迭代器
    for i in ret:    # 迭代出来的每一项都是一个对象
        print(i.group())  # 通过group取值即可
    
  5. re.compile:在同一个正则表达式重复使用多次的时候使用能够减少时间的开销
    compile() 可以将一个长长的正则进行预加载. 方便后面的使用

    obj = re.compile(r'\d{3}')  # 将正则表达式编译成为一个 正则表达式对象, 规则要匹配的是3个数字
    ret = obj.search('abc123eeee') # 正则表达式对象调用search, 参数为待匹配的字符串
    print(ret.group())  # 结果: 123
    
    import re
    ret = re.compile('\d+')
    r1 = ret.search('alex83')
    r2 = ret.findall('wusir74')
    r3 = ret.finditer('taibai40')
    for i in r3:
        print(i.group())
    
  6. re.split:利用正则规则进行切割

    import re
    ret = re.split('\d(\d)','alex83wusir74taibai')  # 默认自动保留分组中的内容
    print(ret)
    r = re.split(r"\d+", "我今年19岁了, 你知道么, 19岁就已经很大了. 周杰伦20岁就得奖了")
    print(r)  # ['我今年', '岁了, 你知道么, ', '岁就已经很大了. 周杰伦', '岁就得奖了']
    
  7. re.sub / re.subn:利用正则规则进行替换

    import re
    ret = re.sub('\d','D','alex83wusir74taibai',1)
    print(ret)      # 'alexD3wusir74taibai'
    
    ret = re.subn('\d','D','alex83wusir74taibai')
    print(ret)      # ('alexDDwusirDDtaibai', 4)
    
  8. 正则中的内容如何单独提取? 单独获取到正则中的具体内容可以给分组起名字

    s = """
    <div class='西游记'><span id='10010'>中国联通</span></div>
        """
    obj = re.compile(r"<span id='(?P<id>\d+)'>(?P<name>\w+)</span>", re.S)
    
    result = obj.search(s)
    print(result.group())  # 结果: <span id='10010'>中国联通</span>
    print(result.group("id"))  # 结果: 10010 # 获取id组的内容
    print(result.group("name"))  # 结果: 中国联通 # 获取name组的内容
    

正则表达式修饰符 - 可选标志

re.I(不区分大小写)
re.S (使 . 匹配包括换行在内的所有字符)
re.M (多行匹配,影响 ^ 和 $)

本来呢: ^只匹配字符串的开头,$只匹配字符串结尾,.不匹配换行符.
re.S做的事情是: 让.也匹配换行符
re.M做的事情是: 让^匹配每行的开头,$匹配每行的结尾

import re
a = """sdfkhellolsdlfsdfiooefo:
877898989worldafdsf"""
b = re.findall('hello(.*?)world',a)   
c = re.findall('hello(.*?)world',a,re.S)  # re.S 把
print ('b is ' , b)
print ('c is ' , c)
 
# 输出结果:
# b is  []
# c is  ['lsdlfsdfiooefo:\n877898989']

在字符串a中,包含换行符\n,在这种情况下:
如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始。
而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。

  • 分组和re模块
  1. 关于group取值

    import re
    ret = re.search('<(\w+)>(.*?)</\w+>',s1)
    print(ret)
    print(ret.group(0))   # group参数默认为0 表示取整个正则匹配的结果
    print(ret.group(1))   # 取第一个分组中的内容
    print(ret.group(2))   # 取第二个分组中的内容
    
    
  2. 分组命名:(?P<名字>正则表达式)

    import re
    ret = re.search('<(?P<tag>\w+)>(?P<cont>.*?)</\w+>',s1)
    print(ret)
    print(ret.group('tag'))   # 取tag分组中的内容
    print(ret.group('cont'))   # 取cont分组中的内容
    
  3. 引用分组:(?P=组名) 这个组中的内容必须完全和之前已经存在的组匹配到的内容一模一样

    import re
    # 方法一:
    s = '<h1>wahaha</h1>'
    ret = re.search('<(?P<tag>\w+)>.*?</(?P=tag)>',s)
    print(ret.group('tag'))      # 'h1'
    
    # 方法二:
    s = '<h1>wahaha</h1>'
    ret = re.search(r'<(\w+)>.*?</\1>',s)
    print(ret.group(1))          # 'h1'
    
  4. 分组和findall:默认findall 优先显示分组内的内容,取消分组优先显示 :(?:正则)

    import re
    ret = re.findall('\d(\d)','aa1alex83')
    # findall遇到正则表达式中的分组,会优先显示分组中的内容
    print(ret)
    
    # 取消分组优先显示:
    ret = re.findall('\d+(?:\.\d+)?','1.234+2')
    print(ret)
    
  5. 有的时候我们想匹配的内容包含在不相匹配的内容当中,这个时候只需要把不想匹配的先匹配出来,再通过手段去掉

    import re
    ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
    print(ret)       # ['1', '2', '60', '', '5', '4', '3']
    ret.remove('')
    print(ret)       # ['1', '2', '60', '5', '4', '3']
    
  • 爬虫示例

    # 方法一:
    import re
    import json
    import requests
    
    def parser_page(par,content):
        res = par.finditer(content)
        for i in res:
            yield {'id': i.group('id'),
                   'title': i.group('title'),
                   'score': i.group('score'),
                   'com_num': i.group('comment_num')}
    
    def get_page(url):
        ret = requests.get(url)
        return  ret.text
    
    
    pattern = '<div class="item">.*?<em class="">(?P<id>\d+)</em>.*?<span class="title">(?P<title>.*?)</span>.*?' \
                  '<span class="rating_num".*?>(?P<score>.*?)</span>.*?<span>(?P<comment_num>.*?)人评价</span>'
    par = re.compile(pattern,flags=re.S)
    num = 0
    with open('movie_info',mode = 'w',encoding='utf-8') as f:
        for i in range(10):
            content = get_page('https://movie.douban.com/top250?start=%s&filter=' % num)
            g = parser_page(par,content)
            for dic in g:
                f.write('%s\n'%json.dumps(dic,ensure_ascii=False))
            num += 25
    
    # 方法二:进阶
    import re
    import json
    import requests
    
    def parser_page(par,content):
        res = par.finditer(content)
        for i in res:
            yield {'id': i.group('id'),
                   'title': i.group('title'),
                   'score': i.group('score'),
                   'com_num': i.group('comment_num')}
    
    def get_page(url):
        ret = requests.get(url)
        return  ret.text
    
    def write_file(file_name):
        with open(file_name,mode = 'w',encoding='utf-8') as f:
            while True:
                dic = yield
                f.write('%s\n' % json.dumps(dic, ensure_ascii=False))
    
    pattern = '<div class="item">.*?<em class="">(?P<id>\d+)</em>.*?<span class="title">(?P<title>.*?)</span>.*?' \
                  '<span class="rating_num".*?>(?P<score>.*?)</span>.*?<span>(?P<comment_num>.*?)人评价</span>'
    par = re.compile(pattern,flags=re.S)
    num = 0
    f = write_file('move2')
    next(f)
    for i in range(10):
        content = get_page('https://movie.douban.com/top250?start=%s&filter=' % num)
        g = parser_page(par,content)
        for dic in g:
            f.send(dic)
        num += 25
    f.close()
    
    

6.2.2 第三方模块

6.2.2.1 基本知识

  • 需要下载安装后才能导入使用

  • 安装方式:

    • pip包管理工具
    # 把pip.exe 所在的目录添加到环境变量中。
    pip install 要安装的模块名称  # pip install xlrd
    
    • 源码安装
    # 下载源码包(压缩文件) -> 解压 -> 打开cmd窗口,并进入此目录:cd C:\Python37\Lib\site-packages
    # 执行:python3 setup.py build
    # 执行:python3 setup.py install
    
  • 安装路径:C:\Python37\Lib\site-packages

6.2.2.2 常用的第三方模块

  1. requests
  2. xlrd

6.2.3 自定义模块

  • 自己写的 xx.py

    def f1():
        print('f1')
    
    def f2():
        print('f2')
    
  • 在yy.py中调用

    # 调用自定义模块中的功能
    import xx
    xx.f1()
    xx.f2()
    
  • 运行

    python yy.py 
    

6.3 模块的调用

注意:文件和文件夹的命名不能是导入的模块名称相同,否则就会直接在当前目录中查找

6.3.1 绝对导入

1.模块的基本导入及调用

  • 导入XXX.py文件
  • 方式一
# 导入模块,加载此模块中所有的值到内存。
import XXX

# 调用模块中的函数
XXX.func()
  • 方式二
# 导入XXX.py中的func和show
from XXX import func,show

# 导入XXX.py中的所有值
from XXX import *

# 调用模块中的函数
func()
  • 方式三
# 如果有重名情况,就导入时起一个别名
# 导入XXX.py中的func,并起一个别名为f
from XXX import func as f

# 调用模块中的函数
f()
  • 总结

    • 导入:import 模块,调用:模块.函数()
    • 导入:from 模块 import 函数,调用:函数()
    • 导入:from 模块 import 函数 as 别名,调用:别名()
    • 知识点:
      • as:起别名
      • *:代表全部
  • 补充

    • 多次导入不再重新加载

      import jd # 第一次加载:会加载一遍jd中所有的内容。
      import jd # 由已经加载过,就不再加载。
      print(456)
      
    • 非要重新加载

      import importlib
      import jd
      importlib.reload(jd)
      print(456)
      
  1. 导入文件夹中的py文件及调用
  • 导入YYY文件夹中的XXX.py文件
  • 方式一
# 导入模块
import YYY.XXX

# 调用模块中的函数
XXX.func()
  • 方式二
# 导入模块
from YYY import XXX

# 调用模块中的函数
XXX.func()
  • 方式三
# 导入模块
from YYY.XXX import func

# 调用模块中的函数
func()
  1. 总结
  • 模块和要执行的py文件在同一目录 且 需要 模块中的很多功能时,推荐用:
    • 导入:import 模块,调用:模块.函数()
  • 其他推荐:
    • 导入:from 模块 import 模块,调用:模块.函数()
    • 导入:from 模块.模块 import 函数,调用:函数()

6.3.2 相对导入(不推荐)

from . import xxx
from .. import xxx
posted @ 2019-09-23 09:27  hanfe1  阅读(214)  评论(0编辑  收藏  举报