目录
一. 常用模块 - hashlib
二. 常用模块 - hmac
三. 常用模块 - logging
四. 常用模块 - re
五. 常用模块 - requests
六. 常用模块 - paramiko
一. 常用模块 - hashlib
hash: 一种算法, 3.x里代替了md5模块和sha模块, 主要提供 SHA1, SHA224, SHA256, SHA384, SHA512, MD5 算法
特点:
- 内容相同则hash运算结果相同, 内容稍微改变则hash值则变
- 不可逆推
- 相同算法: 无论校验多长的数据, 得到的哈希值长度固定.
1 import hashlib 2 3 m=hashlib.md5()# m=hashlib.sha256() 4 m.update('hello'.encode('utf8')) 5 print(m.hexdigest()) #5d41402abc4b2a76b9719d911017c592 6 m.update('alvin'.encode('utf8')) 7 print(m.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af 8 9 m2=hashlib.md5() 10 m2.update('helloalvin'.encode('utf8')) 11 print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af 12 13 ''' 14 注意: 把一段很长的数据update多次, 与一次update这段长数据, 得到的结果一样 15 但是update多次为校验大文件提供了可能. 16 '''
以上加密算法虽然依然非常厉害, 但时候存在缺陷, 即: 通过撞库可以反解. 所以, 有必要对加密算法中添加自定义key再来做加密.
1 import hashlib 2 # ######## 256 ######## 3 hash = hashlib.sha256('898oaFs09f'.encode('utf8')) 4 hash.update('alvin'.encode('utf8')) 5 print (hash.hexdigest()) 6 #e79e68f070cdedcfe63eaf1a2e92c83b4cfb1b5c6bc452d214c1b7e77cdfd1c7
二. 常用模块 - hmac
HMAC的应用
hmac主要应用在身份验证中,它的使用方法是这样的:
- (1) 客户端发出登录请求(假设是浏览器的GET请求)
- (2) 服务器返回一个随机值,并在会话中记录这个随机值
- (3) 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
- (4) 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法
在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的hmac结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使hmac只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了hmac算法,比如php的mhash、python的hmac.py、java的MessageDigest类,在web验证中使用hmac也是可行的,用js进行md5运算的速度也是比较快的。
1 import hmac 2 h = hmac.new('alvin'.encode('utf8')) 3 h.update('hello'.encode('utf8')) 4 print (h.hexdigest()) 5 #320df9832eab4c038b6c1d7ed73a5940
三. 常用模块 - logging
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别,
下面我们看一下怎么用。
1 import logging 2 3 logging.warning("user [alex] attempted wrong password more than 3 times") 4 logging.critical("server is down") 5 6 #输出 7 WARNING:root:user [alex] attempted wrong password more than 3 times 8 CRITICAL:root:server is down
上面是最简单的应用. 但明显还不足以与nginx, apache等成熟软件的日志媲美. 如果想达到nginx这种日志的效果我们需要深入了解一下这个模块.
logging : https://docs.python.org/3.6/library/logging.html
这个包中包含了以下几个类:
- Logger 日志类, 日志的公开接口, 应用程序代码直接使用.
- Handler 处理类, 处理程序发送日志记录到相应的目的地。比如将日志输入到终端/文件/内存等.
- Formatter 格式类, 最终的输出日志记录以格式器指定的格式输出。
- Filter 过滤类, 过滤器提供更细粒度的功能确定哪些日志记录输出。
- LogRecord XX类
- LoggerAdapter XX类
由于个人能力问题, 目前只说明Logger Handler Formatter 三个类的关系与用法. 其余三种等日后在更新.
Logger Handler Formatter
他们的关系请看下图.
Formatter 可以绑定给 Handler, Handler 可以绑定给Logger
如图上所示, Logger1 拥有 Handler1与Handler2的处理配置, 还拥有 Formatter 的格式配置. Logger2 拥有 Handler2与Handler3 的处理配置, 还拥有 Formatter 的格式配置.
那我们现在来看一下每个类到底都有什么功能.
Logger
1 Logger.propagate 2 若为True, 日志信息除了传递给该logger的handler之外, 也被传递给上游logger的handler, 不会考虑上游logger的级别与filter限制 3 若为False, 日志信息不会传递给上游logger的handler 4 构造函数设置该属性为True 5 6 Logger.setLevel(lvl) 7 设置该logger级别为lvl. 低于lvl的日志信息将被忽略. 8 当创建一个根logger时, 默认级别为WARNING 9 当创建一个非根logger时, 默认级别为NOTSET 10 11 Logger.isEnabledFor(lvl) 12 表明lvl级别的信息是否会被该logger处理. 该方法首先检查由logging.disable(lvl)设置的模块级的级别, 然后检查由getEffective()决定的该logger的有效级别. 13 判断这个级别的信息是否会被处理. 14 15 Logger.getEffectiveLevel() 16 获取该logger处理信息的级别 17 18 Logger.getChild(suffix) 19 20 Logger.debug(msg, *args, **kwargs) 21 给该logger记录一条级别为 DEBUG 的信息, msg为消息格式字符串,args为通过字符串格式操作符合并到msg的参数。(注意这意味着可以在格式字符串中使用关键字和一个字典参数。) 22 23 kwargs中有两个关键字参数会被检查:第一个是exc_info,如果它不为false,异常信息会被添加到日志消息。如果有提供异常元组(格式为sys.exc_info()返回值的格式),使用该元组;否则调用sys.exc_info()来得到异常信息。 24 第二个检查的关键字参数是extra,可以给它传递一个字典,用来填充LogRecord的__dict__,LogRecord用以表示日志事件,且有自定义属性。你可以随意使用这些自定义属性。例如,它们可以合并到日志消息中。示例: 25 26 FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s' 27 logging.basicConfig(format=FORMAT) 28 d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} 29 logger = logging.getLogger('tcpserver') 30 logger.warning('Protocol problem: %s', 'connection reset', extra=d) 31 将会打印 32 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset 33 34 extra的字典的键不应该与日志系统使用的键冲突。(参见Formatter文档了解日志系统所用键的信息。) 35 如果决定要在日志消息中使用这些属性,使用时要小心。拿上面的例子,Formatter的格式字符串期待LogRecord的属性字典中有'clientip'和'user'。如果缺失的话,该消息就不会被记录,因为会发生字符串格式异常。在这种情况下,你总是要传递带这些键的extra字典。 36 这可能有些麻烦,它主要在一些特定的环境下使用。如有一个多线程服务器,相同的代码会在许多上下文执行,而感兴趣的条件在上下文才会出现(如上例中的远端客户端IP地址和已认证用户名)。在这种环境下,很可能对特殊的Handler使用特定的Formatter。 37 38 Logger.info(msg, *args, **kwargs) 39 给该logger记录一条级别为 INFO 的信息 40 41 Logger.warning(msg, *args, **kwargs) 42 给该logger记录一条级别为 WARNING 的信息 43 44 Logger.error(msg, *args, **kwargs) 45 给该logger记录一条级别为 ERROR 的信息 46 47 Logger.critical(msg, *args, **kwargs) 48 给该logger记录一条级别为 CRITICAL 的信息 49 50 Logger.log(lvl, msg, *args, **kwargs) 51 给该logger记录一条级别为 lvl 的信息。 52 53 Logger.exception(msg, *args, **kwargs) 54 给该logger记录一条级别为 ERROR 的信息。异常信息将添加到日志信息中。该方法应该只在异常处理器调用。 55 56 Logger.addFilter(filt) 57 添加指定的filter filt 到该logger。 58 59 Logger.removeFilter(filt) 60 删除该logger中的filter filt。 61 62 Logger.filter(record) 63 对record应用该logger的filters,如果该record应该被处理,返回真。轮流调用filters,直到有一个返回假。如果没有filter返回假值,该record将会被处理(传递给handlers)。如果有一个返回了假值,将不会对record做进一步的处理。 64 65 Logger.addHandler(hdlr) 66 将指定的handlerhdlr添加到logger中。 67 68 Logger.removeHandler(hdlr) 69 从logger中移除指定的handler hdlr。 70 71 Logger.findCaller(stack_info=False) 72 查找调用者的源码文件名和行号。以3元组的形式返回文件名,行号和函数名。 73 2.4版本中的变动: 函数名被加入进来。在早期版本中,以2元组形式返回文件名和行号。 74 75 Logger.handle(record) 76 处理一个record,将它传给该logger及其祖先的所有的handler(直到propagate为假为止)。该方法用于从套接字接收到的反序列化的record,以及那些本地创建的。使用filter()日志级别过滤会应用。 77 78 Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None) 79 这是一个工厂方法,可以在子类中覆盖它来创建特定的LogRecord实例。 80 版本2.5中的改变:添加func 和extra 参数。 81 82 Logger.hasHandlers() 83 检查是否此日志记录器已配置的任何处理程序。这是通过寻找此日志记录器中的处理程序和其父母记录器层次结构中。如果处理程序被发现,否则错误将返回 True。方法停止搜索层次结构中向上,每当与 '传播' 属性设置为 false 的记录器发现 — — 这将是最后一个记录器,检查存在的处理程序。 84 3.2 版中的新增功能。
Handlers
Handlers是个基类, 在他下面有很多子类来实现了各种handlers的配置方向
StreamHandler | 流Handler |
FileHandler | 文件Handler |
NullHandler | 空Handler |
WatchedFileHandler | 守卫文件Handler |
BaseRotatingHandler | 基本轮询Handler |
RotatingFileHandler | 轮询文件Handler |
TimedRotatingFileHandler | 时间轮询文件Handler |
SocketHandler | Socket Handler |
DatagramHandler | 数据报Handler |
SysLogHandler | 系统日志Handler |
NTEventLogHandler | Windows事件日志Handler |
SMTPHandler | SMTP协议Handler |
MemoryHandler | 内存Handler |
HTTPHandler | HTTP协议Handler |
QueueHandler | 队列Handler |
QueueListener | 队列监听器 |
# 每个Handler具体使用方法, 请参考: http://python.usyiyi.cn/translate/python_278/library/logging.handlers.html#module-logging.handlers
# logging模块中文文档: http://python.usyiyi.cn/python_278/library/logging.html
# 待整理文档内容到博客中.
按天分割日志, 并保留最近七天的配置:
1 # /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # @Author: kys1230 4 # @Email: kys1230@126.com 5 # @Date: 2017-02-22 14:36:25 6 7 8 import logging 9 import logging.handlers 10 11 filename = "access.log" 12 13 # 创建logger对象, 设置logger的日志级别 14 acc_log = logging.getLogger("access") 15 acc_log.setLevel(logging.INFO) 16 17 # 创建文件Handler 18 fh = logging.handlers.TimedRotatingFileHandler(filename, when='D', interval=1, backupCount=7) 19 20 # 创建格式 21 fmt = logging.Formatter("%(asctime)s - %(name)s %(filename)s:%(funcName)s:%(lineno)d %(levelname)s %(message)s") 22 23 # 将格式绑定到Handler 24 fh.setFormatter(fmt) 25 26 # 将Handler绑定到Logger 27 acc_log.addHandler(fh) 28 29 # 使用Logger 30 acc_log.debug("debug") 31 acc_log.info("info") 32 acc_log.warning("warning") 33 acc_log.error("error") 34 acc_log.critical("critical")
四. 常用模块 - re
下图列出了Python支持的正则表达式元字符和语法:
数量词的贪婪模式与非贪婪模式
正则表达式通常用于在文本中查找匹配的字符串. Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪), 总是尝试匹配尽可能多的字符;非贪婪的则相反, 总是尝试匹配尽可能少的字符. 例如: 正则表达式"ab*"如果用于查找"abbbc", 将找到"abbb". 而如果使用非贪婪的数量词"ab*?", 将找到"a".
反斜杠的困扰
与大多数编程语言相同, 正则表达式里使用"\"作为转义字符, 这就可能造成反斜杠困扰. 假如你需要匹配文本中的字符"\", 那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\": 前两个和后两个分别用于在编程语言里转义成反斜杠, 转换成两个反斜杠后再在正则表达式里转义成一个反斜杠. Python里的原生字符串很好地解决了这个问题, 这个例子中的正则表达式可以使用r"\\"表示. 同样, 匹配一个数字的"\\d"可以写成r"\d". 有了原生字符串, 你再也不用担心是不是漏写了反斜杠, 写出来的表达式也更直观.
匹配模式
正则表达式提供了一些可用的匹配模式, 比如忽略大小写、多行匹配等, 这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍.
re模块方法
re.compile(pattern, flags=0) # 将字符串形式的正则表达式编译为 Pattern对象
- strPattern 字符串形式的正则表达式
- flag 匹配模式, 可以使用'|'表示同时生效, 比如re.I | re.M.
1 prog = re.compile(pattern) 2 result = prog.match(string) 3 # 上面用法等价于下面的用法, 区别在于可以将正则表达式的规则保存到对象中, 以便之后重复使用. 4 result = re.match(pattern, string)
Flags:
- re.A & re.ASCII # 待更新
- re.DEBUG # 显示调试信息
- re.I & re.IGNORECASE # 忽略大小写进行匹配
- re.L & re.LOCALE # 待更新
- re.M & re.MULTILINE # 多好模式, 自行测试
- re.S & re.DOTALL # 默认. 是不匹配换行符的, 如果加上 re.S 那么. 会匹配换行符
- re.X & re.VERBOSE # 详细模式. 这个模式下正则表达式可以是多行, 忽略空白字符, 并可以加入注释
1 a = re.compile(r"""\d + # the integral part 2 \. # the decimal point 3 \d * # some fractional digits""", re.X) 4 b = re.compile(r"\d+\.\d*")
re.search(pattern, string, flags=0) # 从头开始将内容与pattern进行匹配, 如果匹配则返回一个 匹配对象, 如果匹配不到, 最终返回None
re.match(pattern, string, flags=0) # match 相当于 search的正则表达式前加了个^, 他仅从数据的开始位置开始匹配.
re.fullmatch(pattern, string, flags=0) # 将string与pattern进行匹配, 如果完全匹配则返回 匹配对象, 否则 返回None, 这个方法是3.4中新增加的.
1 pattern = "o[gh]" 2 print(re.fullmatch(pattern, "dog")) # 返回None,没有og|oh开头 3 print(re.fullmatch(pattern, "ohr")) # 返回None,不是整串完全匹配,虽然有Oh开头,但是还包含字母r 4 print(re.fullmatch(pattern, "og")) # 返回og,完全匹配
re.split(pattern, string, maxsplit=0, flags=0) # str.split只能按照某个分隔符分割, re.split 可以按照正则规则分割. 在3.1版本后 添加了flags参数
- maxsplit 最大分割次数
re.findall(pattern, string, flags=0) # 获取全部的匹配字符,返回一个所有匹配字符串的列表
re.finditer(pattern, string, flags=0) # findall类似,只是 finditer 返回的是一个迭代器
1 import re 2 data = "My name is Kys1230" 3 for i in re.finditer("\w+", data): 4 print(i.group()) 5 # 执行结果 6 My 7 name 8 is 9 Kys1230
re.sub(pattern, repl, string, count=0, flags=0) # 将正则表达式匹配的字符串替换为新字符串
- repl 新字符串 或 函数地址
- count 替换几次, 0为不限制次数
1 >>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):', 2 ... r'static PyObject*\npy_\1(void)\n{', 3 ... 'def myfunc():') 4 'static PyObject*\npy_myfunc(void)\n{'
如果repl是个函数, 会将正则匹配到的字符串逐一传递给函数执行, 将函数的返回值加入到结果中. 例如:
1 >>> def dashrepl(matchobj): 2 ... if matchobj.group(0) == '-': return ' ' 3 ... else: return '-' 4 >>> re.sub('-{1,2}', dashrepl, 'pro----gram-files') 5 'pro--gram files'
re.subn(pattern, repl, string, count=0, flags=0) # 与re.sub类似, 返回的是元组(新字符串, 替换的次数)
re.escape(string) # 用于将string中的正则表达式元字符如 * + ? 等之前加上转义符再返回, 在需要大量匹配元字符时有那么一点用. 3.3版本后 "_" 不再进行转移
re.purge() # 清空缓存中的正则表达式
正则表达式对象的 方法 与 属性:
regex.search(string[, pos[, endpos]]) # 从字符串string的开始位置pos开始匹配正则表达式, 到位置endpos结束匹配. 匹配成功返回 match对象, 否则返回None
- pos 从字符串string的开始位置pos开始匹配正则表达式
- endpos 到位置endpos结束匹配
1 >>> pattern = re.compile("d") 2 >>> pattern.search("dog") # Match at index 0 3 <_sre.SRE_Match object; span=(0, 1), match='d'> 4 >>> pattern.search("dog", 1) # No match; search doesn't include the "d"
regex.match(string[, pos[, endpos]]) # 指定从字符串string头部或者指定位置的头部匹配
1 >>> pattern = re.compile("o") 2 >>> pattern.match("dog") # No match as "o" is not at the start of "dog". 3 >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". 4 <_sre.SRE_Match object; span=(1, 2), match='o'>
regex.fullmatch(string[, pos[, endpos]]) # 当整个string与正则表达式匹配时返回match对象, 否则返回None, 3.4版本中新增方法
1 >>> pattern = re.compile("o[gh]") 2 >>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog". 3 >>> pattern.fullmatch("ogre") # No match as not the full string matches. 4 >>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits. 5 <_sre.SRE_Match object; span=(1, 3), match='og'>
regex.split(string, maxsplit=0) # 本方法与re.split()一样
regex.findall(string[, pos[, endpos]]) # 与re.findall()一样, 本方法接收参数pos与endpos参数, 可以指定开始位置和结束位置
regex.finditer(string[, pos[, endpos]]) # 与re.finditer()一样, 本方法接收参数pos与endpos参数, 可以指定开始位置和结束位置
regex.sub(repl, string, count=0) # 与re.sub()一样
regex.subn(repl, string, count=0) # 与re.subn()一样
regex.flags # 待更新
The regex matching flags. This is a combination of the flags given to compile(), any (?...) inline flags in the pattern, and implicit flags such as UNICODE if the pattern is a Unicode string.
regex.groups # 正则表达式匹配分组的数量
pattern = re.compile('(?P<style>[^|]*)\|(?P<tags>[^|]*)') print(pattern.findall('OL|AAAAA')) print(pattern.groups) # 结果输出如下: [('OL', 'AAAAA')] 2
regex.groupindex # 返回分组的名称和序号, 以字典方式返回. 如果没有返回空字典
pattern = re.compile('(?P<style>[^|]*)\|(?P<tags>[^|]*)') print(pattern.findall('OL|AAAAA')) print(pattern.groupindex) # 结果输出如下: [('OL', 'AAAAA')] {'style': 1, 'tags': 2}
regex.pattern # 已经编译的正则表达式的字符串
pattern = re.compile('(?P<style>[^|]*)\|(?P<tags>[^|]*)') print(pattern.findall('OL|AAAAA')) print(pattern.pattern) # 结果输出如下: regex.pattern [('OL', 'AAAAA')] (?P<style>[^|]*)\|(?P<tags>[^|]*)
Match(匹配)对象
match对象 是通过正则表达式匹配成功之后返回的对象, 如果不成功也会返回, 不过其布尔值为False. 因此, 判断是否匹配成功, 只要判断match对象的布尔值就可以, 简单的就是使用if语句来判断.
match = re.search(pattern, string) if match:process(match)
match对象支持下面的方法和属性:
match.expand(template) # 在模板字符串template中指定位置替换为指定分组的内容, 可能过索引(\1,\2)或组名称(\g<1>, \g<name>)来引用.
pattern = re.compile('(?P<style>[^|]*)\|(?P<tags>[^|]*)') match = pattern.match('OL|AAAAA') print(pattern.groups) print(pattern.groupindex) print(match.expand(r'这是一个测试\2, 没错')) # 输出结果如下: 2 {'style': 1, 'tags': 2} 这是一个测试AAAAA, 没错
match.group([group1, ...]) # 返回分组中子分组的结果. 如果只有一个参数, 当作一个字符串返回. 如果有多个参数, 使用元组返回. 如果没有参数输入, 默认返回第一组的结果. 组号的范围在[1, 99]之间. 如果输入组号为负数, 或者大于匹配的分组最大值, 就抛出IndexError异常.
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m.group(0) # The entire match 'Isaac Newton' >>> m.group(1) # The first parenthesized subgroup. 'Isaac' >>> m.group(2) # The second parenthesized subgroup. 'Newton' >>> m.group(1, 2) # Multiple arguments give us a tuple. ('Isaac', 'Newton')>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.group('first_name') 'Malcolm' >>> m.group('last_name') 'Reynolds' >>> m.group(1) 'Malcolm' >>> m.group(2) 'Reynolds'>>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. >>> m.group(1) # Returns only the last match. 'c3'
match.groups(default=None) # 使用元组返回所有匹配的分组, 如果有分组没有匹配, 就返回None. 如果有设置参数, 就会使用参数来替换相应没有匹配到的分组.
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632')>>> m = re.match(r"(\d+)\.?(\d+)?", "24") >>> m.groups() # Second group defaults to None. ('24', None) >>> m.groups('0') # Now, the second group defaults to '0'. ('24', '0')
match.groupdict(default=None) # 以字典的方式返回分组命名的匹配结果. 如果没有匹配成的分组, 以参数替换, 如果没有参数, 就默认为None替换.
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'}
match.start([group])
match.end([group]) # 返回匹配组的开始位置和结束位置. 参数是group组号, 默认为0, 就所有组都返回.
>>> email = "tony@tiremove_thisger.net" >>> m = re.search("remove_this", email) >>> email[:m.start()] + email[m.end():] 'tony@tiger.net'
match.span([group]) # 返回匹配对象组的开始位置和结束位置(m.start(group), m.end(group)), 格式是元组方式. 如果没有匹配任何组返回(-1, -1).
match.pos
match.endpos # 在search和match里使用开始位置和结束位置.
match.lastindex # 保存最后分组的值. 如果没有组, 返回None.
match.lastgroup # 待更新
The name of the last matched capturing group, or None if the group didn’t have a name, or if no group was matched at all.
match.re # 在search和match中使用的正则表达式对象.
match.string # 传给search或match进行匹配的字符串.
五. 常用模块 - requests
# 待更新
六. 常用模块 - paramiko
# 待更新