Python 经常使用模块

一、time 模块

时间表示形式

时间相关的操做,时间有三种表示方式:html

  • 时间戳               1970年1月1日以后的秒,即:time.time()
  • 格式化的字符串    2014-11-11 11:11,    即:time.strftime('%Y-%m-%d')
  • 结构化时间          元组包含了:年、日、星期等... time.struct_time    即:time.localtime()

时间戳 :node

import time
print(time.time())

>>> 1526703163.746542

格式化的字符串:python

import time
print(time.strftime("%Y-%m-%d,%H-%M-%S"))

>>> 2018-05-19,12-18-12
  • %y 两位数的年份表示(00-99)
  • %Y 四位数的年份表示(000-9999)
  • %m 月份(01-12)
  • %d 月内中的一天(0-31)
  • %H 24小时制小时数(0-23)
  • %I 12小时制小时数(01-12)
  • %M 分钟数(00=59)
  • %S 秒(00-59)
  • %a 本地简化星期名称
  • %A 本地完整星期名称
  • %b 本地简化的月份名称
  • %B 本地完整的月份名称
  • %c 本地相应的日期表示和时间表示
  • %j 年内的一天(001-366)
  • %p 本地A.M.或P.M.的等价符
  • %U 一年中的星期数(00-53)星期天为星期的开始
  • %w 星期(0-6),星期天为星期的开始
  • %W 一年中的星期数(00-53)星期一为星期的开始
  • %x 本地相应的日期表示
  • %X 本地相应的时间表示
  • %Z 当前时区的名称
  • %% %号自己

结构化时间:linux

import time
print(time.localtime())

>>> time.struct_time(tm_year=2018, tm_mon=5, tm_mday=19, tm_hour=12, tm_min=19, tm_sec=8, tm_wday=5, tm_yday=139, tm_isdst=0)

小结:时间戳是计算机可以识别的时间;时间字符串是人可以看懂的时间;元组则是用来操做时间的ios

几种时间形式的转换

print time.time()
print time.mktime(time.localtime())
  
print time.gmtime()    #可加时间戳参数
print time.localtime() #可加时间戳参数
print time.strptime('2014-11-11', '%Y-%m-%d')
  
print time.strftime('%Y-%m-%d') #默认当前时间
print time.strftime('%Y-%m-%d',time.localtime()) #默认当前时间
print time.asctime()
print time.asctime(time.localtime())
print time.ctime(time.time())

二、datetime 模块

datetime包下面的模块

  • object: 
  • timedelta # 主要用于计算时间跨度 
  • tzinfo # 时区相关 
  • time # 只关注时间 
  • date # 只关注日期 
  • datetime # 同时有时间和日期

其中class中datetime和timedelta比较经常使用,time和date和datetime在使用方法上基本是相同的。正则表达式

获取当前日期和时间

from datetime import datetime
now = datetime.now() # 获取当前datetime
print(now)

>>> 2018-05-19 12:29:09.400586

注意到 datetime 是模块,datetime 模块还包含一个 datetime 类,经过 from datetime import datetime 导入的才是 datetime 这个类。算法

若是仅导入 import datetime,则必须引用全名 datetime.datetimeshell

datetime.now() 返回当前日期和时间,其类型是 datetime数据库

获取指定日期和时间

from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间建立datetime
print(dt)

>>> 2015-04-19 12:20:00

datetime转换为timestamp

在计算机中,时间其实是用数字表示的。咱们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0(1970年之前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。编程

你能够认为:

timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00

对应的北京时间是:

timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00

可见timestamp的值与时区毫无关系,由于timestamp一旦肯定,其UTC时间就肯定了,转换到任意时区的时间也是彻底肯定的,这就是为何计算机存储的当前时间是以timestamp表示的,由于全球各地的计算机在任意时刻的timestamp都是彻底相同的(假定时间已校准)。

把一个 datetime 类型转换为 timestamp 只须要简单调用 timestamp() 方法:

from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间建立datetime
print(dt.timestamp()) # 把datetime转换为timestamp

timestamp转换为datetime

要把 timestamp 转换为 datetime ,使用 datetime 提供的 fromtimestamp() 方法:

from datetime import datetime
t = 1429417200.0
print(datetime.fromtimestamp(t))

>>> 2015-04-19 12:20:00

注意到timestamp是一个浮点数,它没有时区的概念,而datetime是有时区的。上述转换是在timestamp和本地时间作转换。

timestamp也能够直接被转换到UTC标准时区的时间:

from datetime import datetime
t = 1429417200.0
print(datetime.fromtimestamp(t)) # 本地时间

>>> 2015-04-19 12:20:00

print(datetime.utcfromtimestamp(t)) # UTC时间

>>> 2015-04-19 04:20:00

str转换为datetime

不少时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是经过datetime.strptime()实现,须要一个日期和时间的格式化字符串:

from datetime import datetime
cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(cday)

>>> 2015-06-01 18:19:59

注意转换后的datetime是没有时区信息的。

datetime转换为str

若是已经有了datetime对象,要把它格式化为字符串显示给用户,就须要转换为str,转换方法是经过strftime()实现的,一样须要一个日期和时间的格式化字符串:

from datetime import datetime
now = datetime.now()
print(now.strftime('%a, %b %d %H:%M'))

>>> Sat, May 19 12:41

datetime加减

对日期和时间进行加减实际上就是把datetime日后或往前计算,获得新的 datetime。加减能够直接用  +和 运算符,不过须要导入timedelta这个类:

>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
>>> now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
>>> now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)

可见,使用timedelta你能够很容易地算出前几天和后几天的时刻。

本地时间转换为UTC时间

时区转换

>>> 廖雪峰大大的教程

三、 random模块

>>> import random
>>> random.random()      # 大于0且小于1之间的小数
0.7664338663654585

>>> random.randint(1,5)  # 大于等于1且小于等于5之间的整数
2

>>> random.randrange(1,3) # 大于等于1且小于3之间的整数
1

>>> random.choice([1,'23',[4,5]])  # #1或者23或者[4,5]
1

>>> random.sample([1,'23',[4,5]],2) # #列表元素任意2个组合
[[4, 5], '23']

>>> random.uniform(1,3) #大于1小于3的小数
1.6270147180533838

>>> item=[1,3,5,7,9]
>>> random.shuffle(item) # 打乱次序
>>> item
[5, 1, 3, 7, 9]
>>> random.shuffle(item)
>>> item
[5, 9, 7, 1, 3]

练习:生成验证码

import random

def v_code():

    code = ''
    for i in range(5):

        num=random.randint(0,9)
        alf=chr(random.randint(65,90))
        add=random.choice([num,alf])
        code="".join([code,str(add)])

    return code

print(v_code())

四、 hashlib 

4.1 算法介绍

Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它经过一个函数,把任意长度的数据转换为一个长度固定的数据串(一般用16进制的字符串表示)。

摘要算法就是经过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之因此能指出数据是否被篡改过,就是由于摘要函数是一个单向函数,计算f(data)很容易,但经过digest反推data却很是困难。并且,对原始数据作一个bit的修改,都会致使计算出的摘要彻底不一样。

咱们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

import hashlib
 
md5 = hashlib.md5()
md5.update(b'how to use md5 in python hashlib?')
# md5.update(bytes("
how to use md5 in python hashlib?","utf-8"))
print(md5.hexdigest()) 
计算结果以下: d26a53750bc40b38b65a520292f69306

若是数据量很大,能够分块屡次调用update(),最后计算的结果是同样的:

md5 = hashlib.md5()
md5.update(b'how to use md5 in ')
md5.update(b'python hashlib?')
print(md5.hexdigest())

MD5是最多见的摘要算法,速度很快,生成结果是固定的128 bit字节,一般用一个32位的16进制字符串表示。另外一种常见的摘要算法是SHA1,调用SHA1和调用MD5彻底相似:

import hashlib
 
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in ')
sha1.update('python hashlib?')
print sha1.hexdigest()

SHA1的结果是160 bit字节,一般用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,并且摘要长度更长。

4.2 摘要算法应用

任何容许用户登陆的网站都会存储用户登陆的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008

若是以明文保存用户口令,若是数据库泄露,全部用户的口令就落入黑客的手里。此外,网站运维人员是能够访问数据库的,也就是能获取到全部用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,好比MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

考虑这么个状况,不少用户喜欢用123456,888888,password这些简单的口令,因而,黑客能够事先计算出这些经常使用口令的MD5值,获得一个反推表:

'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'

这样,无需破解,只须要对比数据库的MD5,黑客就得到了使用经常使用口令的用户帐号。

对于用户来说,固然不要使用过于简单的口令。可是,咱们可否在程序设计上对简单口令增强保护呢?

因为经常使用口令的MD5值很容易被计算出来,因此,要确保存储的用户口令不是那些已经被计算出来的经常使用口令的MD5,这一方法经过对原始口令加一个复杂字符串来实现,俗称“加盐”:

hashlib.md5("salt".encode("utf8"))

通过Salt处理的MD5口令,只要Salt不被黑客知道,即便用户输入简单口令,也很难经过MD5反推明文口令。

可是若是有两个用户都使用了相同的简单口令好比123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是同样的。有没有办法让使用相同口令的用户存储不一样的MD5呢?

若是假定用户没法修改登陆名,就能够经过把登陆名做为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不一样的MD5。

摘要算法在不少地方都有普遍的应用。要注意摘要算法不是加密算法,不能用于加密(由于没法经过摘要反推明文),只能用于防篡改,可是它的单向计算特性决定了能够在不存储明文口令的状况下验证用户口令。

五、hmac

Python自带的hmac模块实现了标准的Hmac算法

  咱们首先须要准备待计算的原始消息message,随机key,哈希算法,这里采用MD5,使用hmac的代码以下:

import hmac
message = b'Hello world'
key = b'secret'
h = hmac.new(key,message,digestmod='MD5')
# 若是消息很长,能够屡次调用h.update(msg)
print(h.hexdigest())

  可见使用hmac和普通hash算法很是相似。hmac输出的长度和原始哈希算法的长度一致。须要注意传入的key和message都是bytes类型,str类型须要首先编码为bytes

跟hashlib的比较,没有加盐是同样的

import hashlib

md5_obj = hashlib.md5(b'hello')
md5_obj.update(b'world')
print(md5_obj.hexdigest())  # fc5e038d38a57032085441e7fe7010b0

print(hashlib.md5(b'helloworld').hexdigest())  # fc5e038d38a57032085441e7fe7010b0

  hashlib和hmac均可以进行加盐的md5加密,即便是相同的盐和数据,加密出来的结果是不同的哦!

# coding:utf-8
import hmac
import hashlib

content = "hello world"
salt_str = "谁言寸草心,报得三春晖."

obj_md5 = hashlib.md5(salt_str.encode("utf-8"))
obj_md5.update(content.encode("utf-8"))
hashlib_md5 = obj_md5.hexdigest()
print(hashlib_md5)  # 051f2913990c618c0757118687f02354

hmac_md5 = hmac.new(salt_str.encode("utf-8"), content.encode("utf-8"), "md5").hexdigest()
print(hmac_md5)  # 9c1c1559002fd870a4fca899598ba408

五、 os模块 

os模块是与操做系统交互的一个接口。

'''
os.getcwd() 获取当前工做目录,即当前python脚本工做的目录路径
os.chdir("dirname")  改变当前脚本工做目录;至关于shell下cd
os.curdir  返回当前目录: ('.')
os.pardir  获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;至关于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则没法删除,报错;至关于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的全部文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.sep    输出操做系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  运行shell命令,直接显示
os.environ  获取系统环境变量
os.path.abspath(path)  返回path规范化的绝对路径
os.path.split(path)  将path分割成目录和文件名二元组返回
os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path)  返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)  若是path存在,返回True;若是path不存在,返回False
os.path.isabs(path)  若是path是绝对路径,返回True
os.path.isfile(path)  若是path是一个存在的文件,返回True。不然返回False
os.path.isdir(path)  若是path是一个存在的目录,则返回True。不然返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径以前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小

os.getpid() 得到进程号
os.getppid() 得到父进程号
'''
os.urandom(20) 生成20位随机字符

注意:

一、os.stat('path/filename')  获取文件/目录信息。
stat 结构:

st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的连接数。
st_uid: 全部者的用户ID。
st_gid: 全部者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操做系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是建立时间(详细信息参见平台的文档)。

import os
a = os.stat("D:\WeChat\WeChat.exe").st_size
print(a)

os.popen(command[, mode[, bufsize]])

cmd:要执行的命令。
mode:打开文件的模式,默认为'r',用法与open()相同。
buffering:0意味着无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲。负的bufsize意味着使用系统的默认值,通常来讲,对于tty设备,它是行缓冲;对于其它文件,它是全缓冲。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os, sys

# 使用 mkdir 命令
a = 'mkdir nwdir'

b = os.popen(a,'r',1)

print(b)

------------------------------
open file 'mkdir nwdir', mode 'r' at 0x81614d0

递归删除非空目录

import os
def remove_dir(dir):
    dir = dir.replace('\\', '/')
    if(os.path.isdir(dir)):
        for p in os.listdir(dir):
            remove_dir(os.path.join(dir,p))
        if(os.path.exists(dir)):
            os.rmdir(dir)
    else:
        if(os.path.exists(dir)):
            os.remove(dir)
if __name__ == '__main__':
    remove_dir(r'D:/python/practice/') #函数使用



import shutil
 
path = 'g:\zhidao'
shutil.rmtree(path)

os.urandom

import os
import base64

# 生成32位随机字符
a = os.urandom(32)
# 编码为base64
base64.b64encode(a)
>>> b'2QDq4HSpT8U4W6iZ2xDzGW3CcY2WVsJXVEwYv0qludY='

六、 sys模块 

sys 模块提供了许多函数和变量来处理 Python 运行时环境的不一样部分.

sys.argv           命令行参数List,第一个元素是程序自己路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操做系统平台名称
sys.stdout.write("sdads")

sys.modules 是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否须要从新导入。
sys.setrecursionlimit() 递归的最大层数
 

七、 logging模块

7.1 函数式简单配置

import logging  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('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='%%Y-%m-%d,%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用户输出的消息

禁用日志

在调试完程序后,你可能不但愿全部这些日志消息出如今屏幕上。logging.disable() 函数禁用了这些消息,这样就没必要进入到程序中,手工删除全部的日志调用。只要向 logging.disable() 传入一个日志级别,它就会禁止该级别和更低级别的所有日志消息。因此,若是想要禁用全部日志,只要在程序中添加 logging. disable(logging.CRITICAL)。

import logging
logging.basicConfig(level=logging.INFO, format=' %(asctime)s -
%(levelname)s - %(message)s')
logging.critical('Critical error! Critical error!')
>>>2015-05-22 11:10:48,054 - CRITICAL - Critical error! Critical error!
logging.disable(logging.CRITICAL)
logging.critical('Critical error! Critical error!')
logging.error('Error! Error!')

由于 logging.disable() 将禁用它以后的全部消息,你可能但愿将它添加到程序中接近 import logging 代码行的位置。这样就很容易找到它,根据须要注释掉它,或取消注释,从而启用或禁用日志消息。

#basicconfig 简单,能作的事情相对少。

  #中文乱码问题,这个目前不知道怎么解决。

  #不能同时往文件和屏幕上输出。

#配置log对象,稍微有点复杂,能作的事情相对多。

7.2 logger对象配置

若是想同时把log打印在屏幕和文件日志里,就须要了解一点复杂的知识 了


Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的归纳最为合适:

  • logger提供了应用程序能够直接使用的接口;
  • handler将(logger建立的)日志记录发送到合适的目的输出;
  • filter提供了细度设备来决定输出哪条日志记录;
  • formatter决定日志记录的最终输出格式。

logger:

  每一个程序在输出信息以前都要得到一个Logger。Logger一般对应了程序的模块名。

好比聊天工具的图形界面模块能够这样得到它的Logger:

LOG=logging.getLogger(”chat.gui”)

而核心模块能够这样:
LOG=logging.getLogger(”chat.kernel”)

-

Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增长或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():能够设置的日志级别

handler

  handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可使用。有些Handler能够把信息输出到控制台,有些Logger能够把信息输出到文件,还有些 Handler能够把信息发送到网络上。若是以为不够用,还能够编写本身的Handler。能够经过addHandler()方法添加多个多handler

Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象


  每一个Logger能够附加多个Handler。接下来咱们就来介绍一些经常使用的Handler:


  1) logging.StreamHandler
  使用这个Handler能够向相似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:

StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr

  2) logging.FileHandler
  和StreamHandler相似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:

FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。

  3) 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并不会被改名,而是被删除。


  4) logging.handlers.TimedRotatingFileHandler
  这个Handler和RotatingFileHandler相似,不过,它没有经过判断文件大小来决定什么时候从新建立日志文件,而是间隔必定时间就 自动建立新的日志文件。重命名的过程与RotatingFileHandler相似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:

TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具备相同的意义。
interval是时间间隔。

backupCount表示保留的文件个数。

when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有如下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时表明星期一)
midnight 天天凌晨

实例一:

import logging

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')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter) ch.setFormatter(formatter) 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')

实例二:

import logging
 
#create logger
logger = logging.getLogger('TEST-LOG')
logger.setLevel(logging.DEBUG)
 
 
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
 
# create file handler and set level to warning
fh = logging.FileHandler("access.log")
fh.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
# add formatter to ch and fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)
 
# add ch and fh to logger
logger.addHandler(ch)
logger.addHandler(fh)
 
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

其余参考:日志滚动和过时日志删除

八、 序列化模块

8.一、什么是序列化?

什么叫序列化——将本来的字典、列表等内容转换成一个字符串的过程就叫作序列化

咱们把对象(变量)从内存中变成可存储或传输的过程称之为序列化。序列化以后,就能够把序列化后的内容写入磁盘,或者经过网络传输到别的机器上。反过来,把变量内容从序列化的对象从新读到内存里称之为反序列化,即unpickling。

8.二、为何要有序列化模块

好比,咱们在python代码中计算的一个数据须要给另一段程序使用,那咱们怎么给?
如今咱们能想到的方法就是存在文件里,而后另外一个python程序再从文件里读出来。
可是咱们都知道,对于文件来讲是没有字典这个概念的,因此咱们只能将数据转换成字典放到文件中。
你必定会问,将字典转换成一个字符串很简单,就是str(dic)就能够办到了,为何咱们还要学习序列化模块呢?
没错序列化的过程就是从dic 变成str(dic)的过程。如今你能够经过str(dic),将一个名为dic的字典转换成一个字符串,
可是你要怎么把一个字符串转换成字典呢?
聪明的你确定想到了eval(),若是咱们将一个字符串类型的字典str_dic传给eval,就会获得一个返回的字典类型了。
eval()函数十分强大,可是eval是作什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。
BUT!强大的函数有代价。安全性是其最大的缺点。
想象一下,若是咱们从文件中读出的不是一个数据结构,而是一句"删除文件"相似的破坏性语句,那么后果实在不堪设设想。
而使用eval就要担这个风险。
因此,咱们并不推荐用eval方法来进行反序列化操做(将str转换成python中的数据结构)

8.三、序列化的目的

一、以某种存储形式使自定义 对象持久化
二、将对象从一个地方传递到另外一个地方。
三、使程序更具维护性。

8.四、json模块 

若是咱们要在不一样的编程语言之间传递对象,就必须把对象序列化为标准格式,好比XML,但更好的方法是序列化为JSON,由于JSON表示出来就是一个字符串,能够被全部语言读取,也能够方便地存储到磁盘或者经过网络传输。JSON不只是标准格式,而且比XML更快,并且能够直接在Web页面中读取,很是方便。

JSON表示的对象就是标准的JavaScript语言的对象一个子集,JSON和Python内置的数据类型对应以下:

Json模块提供了四个功能:dumps、dump、loads、load

loads和dumps

import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)  #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}


list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) #也能够处理嵌套的数据类型 
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]

load和dump

import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()

f = open('json_file')
dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2),dic2)

ensure_ascii关键字参数

import json
f = open('file','w')
json.dump({'国籍':'中国'},f)
ret = json.dumps({'国籍':'中国'})
f.write(ret+'\n')
json.dump({'国籍':'美国'},f,ensure_ascii=False)
ret = json.dumps({'国籍':'美国'},ensure_ascii=False)
f.write(ret+'\n')
f.close()

其余参数

Serialize obj to a JSON formatted str.(字符串表示的json对象) 
Skipkeys:默认值是False,若是dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key 
ensure_ascii:,当它为True的时候,全部非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False便可,此时存入json的中文便可正常显示。) 
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse). 
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity). 
indent:应该是一个非负的整型,若是是0就是顶格分行显示,若是为空就是一行最紧凑显示,不然会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json 
separators:分隔符,其实是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。 
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. 
sort_keys:将数据根据keys的值进行排序。 
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
其余参数

json的格式化输出

import json
data = {'username':['李华','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)

关于json屡次写入的问题

import json
json dump load
dic = {1:"中国",2:'b'}
f = open('fff','w',encoding='utf-8')
json.dump(dic,f,ensure_ascii=False)
json.dump(dic,f,ensure_ascii=False)
f.close()
f = open('fff',encoding='utf-8')
res1 = json.load(f)
res2 = json.load(f)
f.close()
print(type(res1),res1)
print(type(res2),res2)

----------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/Administrator/Desktop/py/sss.py", line 13, in <module>
    res1 = json.load(f)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\json\__init__.py", line 296, in load
    parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\json\__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\json\decoder.py", line 340, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 22 (char 21)

写入的时候没有问题,能够屡次写入,可是写入后,{"1": "中国", "2": "b"}{"1": "中国", "2": "b"},文件里相似于这种格式,json无法读取。

若是须要分次写入和分次读取,能够以下操做

import json
l = [{'k':'111'},{'k2':'111'},{'k3':'111'}]
f = open('file','w')
import json
for dic in l:
    str_dic = json.dumps(dic)
    f.write(str_dic+'\n')
f.close()

f = open('file')
import json
l = []
for line in f:
    dic = json.loads(line.strip())
    l.append(dic)
f.close()
print(l)

分次读写实例二

import json

dic = {1:"中国",2:'b'}
f = open('fff','w',encoding='utf-8')
json.dump(dic,f,ensure_ascii=False)
f.write("\n") #只要分隔开,而后读取的时候分行读取就行。
json.dump(dic,f,ensure_ascii=False)
f.close()
f = open('fff',encoding='utf-8')
for i in f:
    res1 = json.loads(i)
    res2 = json.loads(i)
f.close()
print(type(res1),res1)
print(type(res2),res2)

8.五、pickle模块

用于序列化的两个模块

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换

pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load  (不只能够序列化字典,列表...能够把python中任意的数据类型序列化

##----------------------------序列化
import pickle
 
dic={'name':'alvin','age':23,'sex':'male'}
 
print(type(dic))#<class 'dict'>
 
j=pickle.dumps(dic)
print(type(j))#<class 'bytes'>
 
 
f=open('序列化对象_pickle','wb')#注意是w是写入str,wb是写入bytes,j是'bytes'
f.write(j)  #-------------------等价于pickle.dump(dic,f)
 
f.close()
#-------------------------反序列化
import pickle
f=open('序列化对象_pickle','rb')
 
data=pickle.loads(f.read())#  等价于data=pickle.load(f)
 
print(data['age'])    

pickle自带屡次写入和读取功能,先写先读

import time
struct_time1  = time.localtime(1000000000)
struct_time2  = time.localtime(2000000000)
f = open('pickle_file','wb')
pickle.dump(struct_time1,f)
pickle.dump(struct_time2,f)
f.close()
f = open('pickle_file','rb')
struct_time1 = pickle.load(f)
struct_time2 = pickle.load(f)
print(struct_time1.tm_year)
print(struct_time2.tm_year)
f.close()

8.六、shelve模块

8.6.一、在已有json和pickle的状况下,为何用shelve?

  使用json或者 pickle 持久化数据,能 dump 屡次,但 load 的话只能取到最新的 dump,由于先前的数据已经被后面 dump 的数据覆盖掉了。若是想要实现 dump 屡次不被覆盖,可使用 shelve 模块。

8.6.二、shelve模块的特色

  shelve 是一个简单的数据存储方案,相似 key-value 数据库,能够很方便的保存 python 对象,其内部是经过 pickle 协议来实现数据序列化。shelve 只有一个 open() 函数,这个函数用于打开指定的文件(一个持久的字典),而后返回一个 shelf 对象。shelf 是一种持久的、相似字典的对象。

  • shelve 模块能够看作是 pickle 模块的升级版,能够持久化全部 pickle 所支持的数据类型,并且 shelve 比 pickle 提供的操做方式更加简单、方便;
  • 在 shelve 模块中,key 必须为字符串,而值能够是 python 所支持的数据类型。
  • shelve 只提供给咱们一个 open 方法,是用 key 来访问的,使用起来和字典相似。能够像字典同样使用get来获取数据等。
  • shelve 模块其实用 anydbm 去建立DB而且管理持久化对象的。

8.6.三、shelve的使用

持久化及解析内容

import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操做,就能够存入数据
f.close()

import shelve
f1 = shelve.open('shelve_file')
existing = f1['key']  #取出数据的时候也只须要直接用key获取便可,可是若是key不存在会报错
f1.close()
print(existing)

  shelve模块有个限制,它不支持多个应用同一时间往同一个DB(文件)进行写操做。 
  因此若是只需进行读操做,能够修改默认参数flag=’r’ 让shelve经过只读方式打开DB(文件)。

  注:经测试,目前发现的是r模式在python2.7环境下能够生效

import shelve
f = shelve.open('shelve_file', flag='r')
existing = f['key']
print(existing)

f.close()

f = shelve.open('shelve_file', flag='r')
existing2 = f['key']
f.close()
print(existing2)

通常状况下,咱们经过shelve来open一个对象后,只能进行一次赋值处理,赋值后不能再次更新处理。(能够整个从新赋值,可是不能作修改)

缘由:从shelve的db文件中从新再访问一个key拿的是它的拷贝! 修改此拷贝后不作拷贝写回并不影响原来的key, 但你要是直接作的操做是赋值新的值到一个key里,那确定就是指向原来的key,会被覆盖的。 

因为shelve在默认状况下是不会记录待持久化对象的任何修改的,因此咱们在shelve.open()时候须要修改默认参数,不然对象的修改不会保存。

import shelve
# f1 = shelve.open('shelve_file')
# print(f1['key'])
# f1['key']['new_value'] = 'this was not here before'
# f1.close()

f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])
# f2['key']['new_value'] = 'this was not here before'
f2.close()

  writeback方式有优势也有缺点。优势是减小了咱们出错的几率,而且让对象的持久化对用户更加的透明了;但这种方式并非全部的状况都须要,首先,使用writeback之后,shelf在open()的时候回增长额外的内存消耗,而且当DB在close()的时候会将缓存中的每个对象都写入到DB,这也会带来额外的等待时间。由于shelve没有办法知道缓存中哪些对象修改了,哪些对象没有修改,所以全部的对象都会被写入

九、collections模块

在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

1.namedtuple: 生成可使用名字来访问元素内容的tuple

2.deque: 双端队列,能够快速的从另一侧追加和推出对象

3.Counter: 计数器,主要用来计数

4.OrderedDict: 有序字典

5.defaultdict: 带有默认值的字典

9.1 namedtuple

们知道tuple能够表示不变集合,例如,一个点的二维坐标就能够表示成:

>>> p = (1, 2)

可是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。

这时,namedtuple就派上了用场:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2

似的,若是要用坐标和半径表示一个圆,也能够用namedtuple定义:

#namedtuple('名称', [属性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])

9.2 deque

使用list存储数据时,按索引访问元素很快,可是插入和删除元素就很慢了,由于list是线性存储,数据量大的时候,插入和删除效率很低。

deque是为了高效实现插入和删除操做的双向列表,适合用于队列和栈:

队列
import queue
q = queue.Queue()
q.put([1,2,3])
q.put(5)
q.put(6)
print(q)
print(q.get())
print(q.get())
print(q.get())
print(q.get())   # 阻塞
print(q.qsize())

from collections import deque
dq = deque([1,2])
dq.append('a')   # 从后面放数据  [1,2,'a']
dq.appendleft('b') # 从前面放数据 ['b',1,2,'a']
dq.insert(2,3)    #['b',1,3,2,'a']
print(dq.pop())      # 从后面取数据
print(dq.pop())      # 从后面取数据
print(dq.popleft())  # 从前面取数据
print(dq)

deque除了实现list的append()pop()外,还支持appendleft()popleft(),这样就能够很是高效地往头部添加或删除元素。

9.3 OrderedDict

使用dict时,Key是无序的。在对dict作迭代时,咱们没法肯定Key的顺序。

若是要保持Key的顺序,能够用OrderedDict

复制代码
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是无序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
复制代码

意,OrderedDict的Key会按照插入的顺序排列,不是Key自己排序:

>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> od.keys() # 按照插入的Key的顺序返回
['z', 'y', 'x']

9.4 defaultdict 

有以下值集合 [11,22,33,44,55,66,77,88,99,90...],将全部大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。

即: { 'k1' : 大于 66  'k2' : 小于 66 }
原生字典
values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = {}

for value in  values:
    if value>66:
        if my_dict.has_key('k1'):
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if my_dict.has_key('k2'):
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]

defaultdict字典

from collections import defaultdict

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)

for value in  values:
    if value>66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)

使dict时,若是引用的Key不存在,就会抛出KeyError。若是但愿key不存在时,返回一个默认值,就能够用defaultdict

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默认值
'N/A'

Counter

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素做为key,其计数做为value。计数值能够是任意的Interger(包括0和负数)。Counter类和其余语言的bags或multisets很类似。

c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
其余详细内容 http://www.cnblogs.com/Eva-J/articles/7291842.html

十、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()  
print(ret)
# match是从头开始匹配,若是正则规则从头开始能够匹配上,
#就返回一个变量。匹配的内容须要用group才能显示,若是没匹配上,就返回None,调用group会报错
#结果 : '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)   结果:('evaHegonHyuanH', 3)

obj = re.compile('\d{3}')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  #结果 : 123
flags有不少可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为 re.S(DOTALL)点能够匹配任意字符,包括换行符 re.L(LOCALE)作本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用 re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag re.X(VERBOSE)冗长模式,该模式下pattern字符串能够是多行的,忽略空白字符,并能够添加注释
phoneRegex = re.compile(r"""( (\d{3}|\(\d{3}\))? #注释一 (\s|-|\.)? #注释二 ...... #注释三 )""",re.VERBOSE)
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']

#在匹配部分加上()以后所切出的结果是不一样的,
#没有()的没有保留所匹配的项,可是有()的却可以保留了匹配的项,
#这个在某些须要保留匹配部分的使用过程是很是重要的。

练习

一、匹配标签

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>

二、匹配整数

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))")
print(ret) #['1', '-2', '60', '', '5', '-4', '3']
ret.remove("")
print(ret) #['1', '-2', '60', '5', '-4', '3']

三、数字匹配

1、 匹配一段文本中的每行的邮箱
y='123@qq.comaaa@163.combbb@126.comasdfasfs33333@adfcom'
import re
ret=re.findall('\w+@(?:qq|163|126).com',y)
print(ret)

二、 匹配一段文本中的每行的时间字符串,好比:‘1990-07-12’;
time='asfasf1990-07-12asdfAAAbbbb434241'
import re
ret=re.search(r'(?P<year>19[09]\d)-(?P<month>\d+)-(?P<days>\d+)',time)
print(ret.group('year'))
print(ret.group('month'))
print(ret.group('days'))

# 三、 匹配一段文本中全部的身份证数字。
a='sfafsf,34234234234,1231313132,154785625475896587,sdefgr54184785ds85,4864465asf86845'
import re
ret=re.findall('\d{18}',a)
print(ret)

# 四、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}
q='3344,88888,7778957,10000,99999,414,4,867287672'
import re
ret=re.findall('[1-9][0-9]{4,}',q)
print(ret)

# 五、 匹配一个浮点数
import re
ret=re.findall('-?\d+\.?\d*','-1,-2.5,8.8,1,0')
print(ret)

# 六、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$
import re
ret=re.findall('[\u4e00-\u9fa5]{0,}','的沙发斯蒂芬')
print(ret)

# 七、 匹配出全部整数
a='1,-3,a,-2.5,7.7,asdf'
import re
ret=re.findall(r"'(-?\d+)'",str(re.split(',',a)))
print(ret)

 

 

四、爬虫练习

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
简化版
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
复杂版

 

flags有不少可选值:

re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点能够匹配任意字符,包括换行符
re.L(LOCALE)作本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用
re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag
re.X(VERBOSE)冗长模式,该模式下pattern字符串能够是多行的,忽略空白字符,并能够添加注释
phoneRegex
= re.compile(r"""( (\d{3}|\(\d{3}\))? #注释一 (\s|-|\.)? #注释二 ...... #注释三 )""",re.VERBOSE)

 

十一、 configparser模块

该模块适用于配置文件的格式与windows ini文件相似,能够包含一个或多个节(section),每一个节能够有多个参数(键=值)。

建立文件

来看一个好多软件的常见文档格式以下:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
  
[bitbucket.org]
User = hg
  
[topsecret.server.com]
Port = 50022
ForwardX11 = no

若是想用python生成一个这样的文档怎么作呢?

import configparser

config = configparser.ConfigParser()

config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9',
                     'ForwardX11':'yes'
                     }

config['bitbucket.org'] = {'User':'hg'}

config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}

with open('example.ini', 'w') as configfile:

   config.write(configfile)

查找文件

import configparser

config = configparser.ConfigParser()

#---------------------------查找文件内容,基于字典的形式

print(config.sections())        #  []

config.read('example.ini')

print(config.sections())        #   ['bitbucket.org', 'topsecret.server.com']

print('bytebong.com' in config) # False
print('bitbucket.org' in config) # True


print(config['bitbucket.org']["user"])  # hg

print(config['DEFAULT']['Compression']) #yes

print(config['topsecret.server.com']['ForwardX11'])  #no


print(config['bitbucket.org'])          #<Section: bitbucket.org>

for key in config['bitbucket.org']:     # 注意,有default会默认default的键
    print(key)

print(config.options('bitbucket.org'))  # 同for循环,找到'bitbucket.org'下全部键

print(config.items('bitbucket.org'))    #找到'bitbucket.org'下全部键值对

print(config.get('bitbucket.org','compression')) # yes       get方法取深层嵌套的值

增删改操做

import configparser

config = configparser.ConfigParser()

config.read('example.ini')

config.add_section('yuan')



config.remove_section('bitbucket.org')
config.remove_option('topsecret.server.com',"forwardx11")


config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')

config.write(open('new2.ini', "w")) 

十二、 subprocess模块

 当咱们须要调用系统的命令的时候,最早考虑的os模块。用os.system()和os.popen()来进行操做。可是这两个命令过于简单,不能完成一些复杂的操做,如给运行的命令提供输入或者读取命令的输出,判断该命令的运行状态,管理多个命令的并行等等。这时subprocess中的Popen命令就能有效的完成咱们须要的操做。

      subprocess模块容许一个进程建立一个新的子进程,经过管道链接到子进程的stdin/stdout/stderr,获取子进程的返回值等操做。 

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.

This module intends to replace several other, older modules and functions, such as: os.system、os.spawn*、os.popen*、popen2.*、commands.*

这个模块只一个类:Popen。

简单命令

import subprocess

#  建立一个新的进程,与主进程不一样步  if in win: s=subprocess.Popen('dir',shell=True)
s=subprocess.Popen('ls')
s.wait()                  # s是Popen的一个实例对象,等待ls命令完成再执行ending

print('ending...') 

命令带参数

linux:
import subprocess

subprocess.Popen('ls -l',shell=True)

#subprocess.Popen(['ls','-l'])

shell = True(windows必须有)

shell=True参数会让subprocess.call接受字符串类型的变量做为命令,并调用shell去执行这个字符串,当shell=False是,subprocess.call只接受数组变量做为命令,并将数组的第一个元素做为命令,剩下的所有做为该命令的参数。

举个例子来讲明:

from subprocess import call  
import shlex  
cmd = "cat test.txt; rm test.txt"  
call(cmd, shell=True)

上述脚本中,shell=True的设置,最终效果是执行了两个命令

cat test.txt 和 rm test.txt

把shell=True 改成False,

from subprocess import call  
import shlex  
cmd = "cat test.txt; rm test.txt"  
cmd = shlex(cmd)  
call(cmd, shell=False)

则调用call的时候,只会执行cat的命令,且把 "test.txt;" "rm" "test.txt" 三个字符串看成cat的参数,因此并非咱们直观看到的好像有两个shell命令了。

也许你会说,shell=True 不是很好吗,执行两个命令就是我指望的呀。但其实,这种作法是不安全的,由于多个命令用分号隔开,万一检查不够仔细,执行了危险的命令好比 rm -rf / 这种那后果会很是严重,而使用shell=False就能够避免这种风险。

整体来讲,看实际须要而定,官方的推荐是尽可能不要设置shell=True。

控制子进程

当咱们想要更个性化咱们的需求的时候,就要转向Popen类,该类生成的对象用来表明子进程。刚才咱们使用到了一个wait方法

此外,你还能够在父进程中对子进程进行其它操做:

s.poll() # 检查子进程状态
s.kill() # 终止子进程
s.send_signal() # 向子进程发送信号
s.terminate() # 终止子进程

s.pid:子进程号

子进程的文本流控制

能够在Popen()创建子进程的时候改变标准输入、标准输出和标准错误,并能够利用subprocess.PIPE将多个子进程的输入和输出链接在一块儿,构成管道(pipe):

import subprocess

# s1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
# print(s1.stdout.read())



#s2.communicate()

s1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
s2 = subprocess.Popen(["grep","0:0"],stdin=s1.stdout, stdout=subprocess.PIPE)
out = s2.communicate()

print(out)

subprocess.PIPE实际上为文本流提供一个缓存区。s1的stdout将文本输出到缓存区,随后s2的stdin从该PIPE中将文本读取走。s2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

快捷API

'''
subprocess.call()

父进程等待子进程完成
返回退出信息(returncode,至关于Linux exit code)


subprocess.check_call()
父进程等待子进程完成
返回0,检查退出信息,若是returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含
有returncode属性,可用try…except…来检查


subprocess.check_output()
父进程等待子进程完成
返回子进程向标准输出的输出结果
检查退出信息,若是returncode不为0,则举出错误subprocess.CalledProcessError,该对象包含
有returncode属性和output属性,output属性为标准输出的输出结果,可用try…except…来检查。


'''

为何要用subprocess模块,由于能够把stdout和stderr分开读取。

import subprocess
res = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(res.stdout.read().decode('gbk'))
print(res.stderr.read().decode('gbk'))

1三、base64 

  base64模块是用来做base64编码解码,经常使用于小型数据的传输。编码后的数据是一个字符串,其包括a-z、A-Z、0-九、/、+共64个字符,便可用6个字节表示,写出数值就是0-63.故三个字节编码的话就变成了4个字节,若是数据字节数不是3的倍数,就不能精确地划分6位的块,此时须要在原数据后添加1个或2个零值字节,使其字节数为3的倍数,而后在编码后的字符串后添加1个或2个‘=’,表示零值字节,故事实上总共由65个字符组成。

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']     +       “=”

  而后,对二进制数据进行处理,每3个字节一组,一共是3x8=24bit,划为4组,每组正好6个bit:

 

将3个字节的‘Xue‘进行base64编码:

将2个字节’Xu‘进行base64编码:

将1个字节’X'进行base64编码:

  base64模块真正用的上的方法只有8个,分别是encode, decode, encodestring, decodestring, b64encode,b64decode, urlsafe_b64decode,urlsafe_b64encode。他们8个能够两两分为4组,encode,decode一组,专门用来编码和解码文件的,也能够StringIO里的数据作编解码;encodestring,decodestring一组,专门用来编码和解码字符串; b64encode和b64decode一组,用来编码和解码字符串,而且有一个替换符号字符的功能;urlsafe_b64encode和urlsafe_b64decode一组,这个就是用来专门对url进行base64编解码的。

13.一、代码实例

13.1.一、b64encode和b64decode:对字符串操做

import base64
 
st = 'hello world!'.encode()#默认以utf8编码
res = base64.b64encode(st)
print(res.decode())#默认以utf8解码
res = base64.b64decode(res)
print(res.decode())#默认以utf8解码


>>>
aGVsbG8gd29ybGQh
hello world!

-

import os, base64

# 图片装换
with open("./robot.png", "rb") as f:
    # 将读取的二进制文件转换为base64字符串
    bs64_str = base64.b64encode(f.read())
    # 打印图像转换base64格式的字符串,type结果为<class 'bytes'>
    print(bs64_str, type(bs64_str))
    # 将base64格式的数据装换为二进制数据
    imgdata = base64.b64decode(bs64_str)
    # 将二进制数据装换为图片
    with open("./robot2.png", "wb") as f2:
        f2.write(imgdata)

13.1.二、encode和code

对文件操做,有两个参数,一个是input,一个是output。

import base64
import io
 
st = "hello world!"
f = io.StringIO() #建立文件
out1 = io.StringIO()
out2 = io.StringIO()
f.write(st)
f.seek(0)
base64.encode(f,out1)
print(out1.getvalue())
out1.seek(0)
base64.decode(out1,out2)
print(out2.getvalue())

1四、csv

一、CSV介绍

  CSV,全称为Comma-Separated Values,它以逗号分隔值,其文件以纯文本形式存储表格数据,该文件是一个字符序列,能够由任意数目的记录组成,每条记录有字段组成,字段间分隔符是逗号或制表符,至关于结构化的纯文本形式,它比Excel文件更简洁,用来存储数据比较方便

二、CSV经常使用类与方法

csv.reader(csvfile,dialect='excel',**fmtparams)

  遍历CSV文件对象并返回,csvfiel能够是任何支持迭代器协议的对象,若是csvfile是一个文件对象,它须要指定newline=''

csv.writer(csvfile,dialect='excel',**fmtparams)

  写入数据到csv文件中,csvfile能够是具备写入方法的任何对象,若是csvfiel是一个文件对象,应该用newline=''指定换行符(unix上位'\n',windows上位'\r\n')#!/usr/bin/env python

# -*- coding: utf-8 -*-
# @Time    : 2018/6/27 11:44
# @Author  : Py.qi
# @File    : csv_file1.py
# @Software: PyCharm
import csv
iterable=[['1','zs',20,8998,20180627],['1','zs',20,8998,20180627],['1','zs',20,8998,20180627]]

with open('csvfile.csv','w',newline='') as csvf:
    spanwriter=csv.writer(csvf,dialect='excel')   #建立writer对象
    spanwriter.writerow(['id','name','age','salary','date'])  #使用writer的方法writerow写入到文件
    spanwriter.writerows(iterable)  #迭代写入数据

with open('csvfile.csv','r',newline='') as csvf:
    spamreader=csv.reader(csvf)  #建立reader对象
    for i in spamreader:
        print('\t'.join(i))   #指定分隔符,读取csv文件数据
    # for j in i:
    #   print("%-10s"%j,end="")
    # print("\n")

# id name age salary date 1 zs 20 8998 20180627 1 zs 20 8998 20180627 1 zs 20 8998 20180627

class csv.DictReader(f, fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds)

  以字典的形式读取csv文件的行,fileldnames参数指定键,restkey指定默认key,restval指定默认value,dialect指定方言

class csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

  建立一个字典形式的csv操做对象,f为写入的文件,fieldnames为制定key,restval制定默认value,extrasaction表示,若是在使用writerow()方法写入数据的字典中字段名找不到的键,则此参数将执行操做,若是设置为saise则会引起valueError,若是设置为ignore则字典中的额外值将被忽略

reader对象,DictReader实例和reader()对象具备的方法和属性

  csvreader.__next__():迭代读取对象的下一行

  csvreader.dialect:解析器使用的方言

  csvreader.line_num:从源迭代器读取的行数

  csvreader.fieldnames:若是在建立对象时为做为参数传递,则在首次访问文件或读取第一条记录是初始化此属性,此属性只适用于DictReader对象

writer对象,DictWriter和writer()实例对象具备的方法和属性:

  csvwriter.writerow():将行参数写入到文件对象,根据当前的方言格式化

  csvwriter.writerows(row):将row中的全部元素,行对象的迭代写入到文件对象

  csvwriter.dialect:解析器使用的方言

  DictWriter.writeheader():写入一行字段名,只适用于DictWriter对象

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/6/27 12:13
# @Author  : Py.qi
# @File    : csv_file2.py
# @Software: PyCharm
import csv
import pandas

iter=[
    {'id':2,'name':'wanwu','age':23,'date':20180627},
    {'id':3,'name':'zhaoliu','age':24,'date':20180627},
    {'id':4,'name':'tianqi','age':25,'date':20180627}
]
#写入文件
with open('names.csv','w',newline='') as csvf:
    fieldnames=['id','name','age','date']
    writer=csv.DictWriter(csvf,fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'id':1,'name':'lisii','age':22,'date':20180627})
    writer.writerows(iter)

#读取文件
with open('names.csv','r') as csvf:
    reader=csv.DictReader(csvf,fieldnames=fieldnames)
    for i in reader:
        print(i['id'],i['name'],i['age'],i['date'])

#也可使用pandas模块来读取csv文件
df=pandas.read_csv('names.csv')
print(df)

#
id name age date
1 lisii 22 20180627
2 wanwu 23 20180627
3 zhaoliu 24 20180627
4 tianqi 25 20180627

   id     name  age      date
0   1    lisii   22  20180627
1   2    wanwu   23  20180627
2   3  zhaoliu   24  20180627
3   4   tianqi   25  20180627

1五、paramiko

  paramiko实现了SSHv2协议(底层使用cryptography)。有了Paramiko之后,咱们就能够在Python代码中直接使用SSH协议对远程服务器执行操做,而不是经过ssh命令对远程服务器进行操做。

pip3 install paramiko

15.一、Paramiko介绍

paramiko包含两个核心组件:SSHClientSFTPClient

  • SSHClient的做用相似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输(Transport),通道(Channel)及SFTPClient创建的方法(open_sftp),一般用于执行远程命令。
  • SFTPClient的做用相似与Linux的sftp命令,是对SFTP客户端的封装,用以实现远程文件操做,如文件上传、下载、修改文件权限等操做。
# Paramiko中的几个基础名词:
 
1、Channel:是一种类Socket,一种安全的SSH传输通道;
2、Transport:是一种加密的会话,使用时会同步建立了一个加密的Tunnels(通道),这个Tunnels叫作Channel;
三、Session:是client与Server保持链接的对象,用connect()/start_client()/start_server()开始会话。

15.二、Paramiko基本使用

基于用户名和密码的 sshclient 方式登陆

import paramiko
# 建立SSH对象(实例化SSHClient)
ssh = paramiko.SSHClient()

# 加载本地HOSTS主机文件
ssh.load_system_host_keys()
# 容许链接不在know_hosts文件中的主机 # 自动添加策略,保存服务器的主机名和密钥信息,若是不添加,那么再也不本地know_hosts文件中记录的主机将没法链接 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 ssh.connect(hostname='192.168.199.146', port=22, username='fishman', password='9') # 执行命令 stdin, stdout, stderr = ssh.exec_command('df') # 获取命令结果 res,err = stdout.read(),stderr.read() result = res if res else err print(result.decode()) # 关闭链接 ssh.close()

基于秘钥连接登陆

# 配置私人密钥文件位置
private = paramiko.RSAKey.from_private_key_file('/Users/ch/.ssh/id_rsa')
 
#实例化SSHClient
client = paramiko.SSHClient()
 
#自动添加策略,保存服务器的主机名和密钥信息,若是不添加,那么再也不本地know_hosts文件中记录的主机将没法链接
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
#链接SSH服务端,以用户名和密码进行认证
client.connect(hostname='10.0.0.1',port=22,username='root',pkey=private)

基于用户名和密码的 transport 方式登陆

  基于SSHClient是传统的链接服务器、执行命令、关闭的一个操做,有时候须要登陆上服务器执行多个操做,好比执行命令、上传/下载文件,上面方法则没法实现,能够经过以下方式来操做。

  SSHClient()里面有一个transport变量,这个是用于获取链接的,所以咱们也能够单独的获取到transport变量,而后执行链接操做

#SSHClient 封装 Transport

import paramiko

# 实例化一个transport对象
transport = paramiko.Transport(('192.168.199.146', 22))

# 创建链接
transport.connect(username='fishman', password='9')

# 将sshclient的对象的transport指定为以上的transport
ssh = paramiko.SSHClient()
ssh._transport = transport

# 执行命令,和传统方法同样
stdin, stdout, stderr = ssh.exec_command('df')
print (stdout.read().decode())

# 关闭链接
transport.close()

基于秘钥的 transport 方式登陆

import paramiko

private_key = paramiko.RSAKey.from_private_key_file('/Users/ljf/.ssh/id_rsa')

# 实例化一个transport对象
transport = paramiko.Transport(('192.168.199.146', 22))

# 创建链接
transport.connect(username='fishman', pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
 
# 执行命令及获取结果
stdin, stdout, stderr = ssh.exec_command('df')
res,err = stdout.read(),stderr.read()
result = res if res else err
print(result.decode())

# 关闭链接
transport.close()

SFTPClient用于链接远程服务器并执行上传下载-----基于用户名密码

import paramiko

# 实例化一个trans对象# 实例化一个transport对象
transport = paramiko.Transport(('192.168.199.146', 22))

# 创建链接
transport.connect(username='fishman', password='9')
# 实例化一个 sftp对象,指定链接的通道
sftp = paramiko.SFTPClient.from_transport(transport)
 
#LocalFile.txt 上传至服务器 /home/fishman/test/remote.txt
sftp.put('LocalFile.txt', '/home/fishman/test/remote.txt')

# 将LinuxFile.txt 下载到本地 fromlinux.txt文件中
sftp.get('/home/fishman/test/LinuxFile.txt', 'fromlinux.txt')
transport.close()

 

SFTPClient用于链接远程服务器并执行上传下载-----基于秘钥

 

import paramiko
private_key
= paramiko.RSAKey.from_private_key_file('/Users/ljf/.ssh/id_rsa') transport = paramiko.Transport(('192.168.199.146', 22)) transport.connect(username='fishman', password='9') sftp = paramiko.SFTPClient.from_transport(transport) # LocalFile.txt 上传至服务器 /home/fishman/test/remote.txt sftp.put('LocalFile.txt', '/home/fishman/test/remote.txt') # 将LinuxFile.txt 下载到本地 fromlinux.txt文件中 sftp.get('/home/fishman/test/LinuxFile.txt', 'fromlinux.txt') transport.close()

综合实例

class SSHConnection(object):
 
    def __init__(self, host_dict):
        self.host = host_dict['host']
        self.port = host_dict['port']
        self.username = host_dict['username']
        self.pwd = host_dict['pwd']
        self.__k = None
 
    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport
 
    def close(self):
        self.__transport.close()
 
    def run_cmd(self, command):
        """
         执行shell命令,返回字典
         return {'color': 'red','res':error}或
         return {'color': 'green', 'res':res}
        :param command:
        :return:
        """
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 执行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 获取命令结果
        res = unicode_utils.to_str(stdout.read())
        # 获取错误信息
        error = unicode_utils.to_str(stderr.read())
        # 若是有错误信息,返回error
        # 不然返回res
        if error.strip():
            return {'color':'red','res':error}
        else:
            return {'color': 'green', 'res':res}
 
    def upload(self,local_path, target_path):
        # 链接,上传
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 将location.py 上传至服务器 /tmp/test.py
        sftp.put(local_path, target_path, confirm=True)
        # print(os.stat(local_path).st_mode)
        # 增长权限
        # sftp.chmod(target_path, os.stat(local_path).st_mode)
        sftp.chmod(target_path, 0o755)  # 注意这里的权限是八进制的,八进制须要使用0o做为前缀
 
    def download(self,target_path, local_path):
        # 链接,下载
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 将location.py 下载至服务器 /tmp/test.py
        sftp.get(target_path, local_path)
 
    # 销毁
    def __del__(self):
        self.close()
 
  
#unicode_utils.py
def to_str(bytes_or_str):
    """
    把byte类型转换为str
    :param bytes_or_str:
    :return:
    """
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value

实现输入命令立马返回结果的功能

  以上操做都是基本的链接,若是咱们想实现一个相似xshell工具的功能,登陆之后能够输入命令回车后就返回结果:

import paramiko
import os
import select
import sys
 
# 创建一个socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 启动一个客户端
trans.start_client()
 
# 若是使用rsa密钥登陆的话
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 若是使用用户名和密码登陆
trans.auth_password(username='super', password='super')
# 打开一个通道
channel = trans.open_session()
# 获取终端
channel.get_pty()
# 激活终端,这样就能够登陆到终端了,就和咱们用相似于xshell登陆系统同样
channel.invoke_shell()
# 下面就能够执行你全部的操做,用select实现
# 对输入终端sys.stdin和 通道进行监控,
# 当用户在终端输入命令后,将命令交给channel通道,这个时候sys.stdin就发生变化,select就能够感知
# channel的发送命令、获取结果过程其实就是一个socket的发送和接受信息的过程
while True:
    readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
    # 若是是用户输入命令了,sys.stdin发生变化
    if sys.stdin in readlist:
        # 获取输入的内容
        input_cmd = sys.stdin.read(1)
        # 将命令发送给服务器
        channel.sendall(input_cmd)
 
    # 服务器返回告终果,channel通道接受到结果,发生变化 select感知到
    if channel in readlist:
        # 获取结果
        result = channel.recv(1024)
        # 断开链接后退出
        if len(result) == 0:
            print("\r\n**** EOF **** \r\n")
            break
        # 输出到屏幕
        sys.stdout.write(result.decode())
        sys.stdout.flush()
 
# 关闭通道
channel.close()
# 关闭连接
trans.close()

支持tab自动补全

import paramiko
import os
import select
import sys
import tty
import termios
 
'''
实现一个xshell登陆系统的效果,登陆到系统就不断输入命令同时返回结果
支持自动补全,直接调用服务器终端
'''
# 创建一个socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 启动一个客户端
trans.start_client()
 
# 若是使用rsa密钥登陆的话
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 若是使用用户名和密码登陆
trans.auth_password(username='super', password='super')
# 打开一个通道
channel = trans.open_session()
# 获取终端
channel.get_pty()
# 激活终端,这样就能够登陆到终端了,就和咱们用相似于xshell登陆系统同样
channel.invoke_shell()
 
# 获取原操做终端属性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 将如今的操做终端属性设置为服务器上的原生终端属性,能够支持tab了
    tty.setraw(sys.stdin)
    channel.settimeout(0)
 
    while True:
        readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
        # 若是是用户输入命令了,sys.stdin发生变化
        if sys.stdin in readlist:
            # 获取输入的内容,输入一个字符发送1个字符
            input_cmd = sys.stdin.read(1)
            # 将命令发送给服务器
            channel.sendall(input_cmd)
 
        # 服务器返回告终果,channel通道接受到结果,发生变化 select感知到
        if channel in readlist:
            # 获取结果
            result = channel.recv(1024)
            # 断开链接后退出
            if len(result) == 0:
                print("\r\n**** EOF **** \r\n")
                break
            # 输出到屏幕
            sys.stdout.write(result.decode())
            sys.stdout.flush()
finally:
    # 执行完后将如今的终端属性恢复为原操做终端属性
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
 
# 关闭通道
channel.close()
# 关闭连接
trans.close()

1六、XML

  在XML解析方面,Python贯彻了本身“开箱即用”(batteries included)的原则。在自带的标准库中,Python提供了大量能够用于处理XML语言的包和工具,数量之多,甚至让Python编程新手无从选择。

16.一、有哪些能够解析XML的Python包?

  Python的标准库中,提供了6种能够用于处理XML的包。

一、xml.dom

  xml.dom实现的是W3C制定的DOM API。若是你习惯于使用DOM API或者有人要求这这样作,可使用这个包。不过要注意,在这个包中,还提供了几个不一样的模块,各自的性能有所区别。

  DOM解析器在任何处理开始以前,必须把基于XML文件生成的树状数据放在内存,因此DOM解析器的内存使用量彻底根据输入资料的大小。

二、xml.dom.minidom

  xml.dom.minidom是DOM API的极简化实现,比完整版的DOM要简单的多,并且这个包也小的多。那些不熟悉DOM的朋友,应该考虑使用xml.etree.ElementTree模块。据lxml的做者评价,这个模块使用起来并不方便,效率也不高,并且还容易出现问题。

三、xml.dom.pulldom

  与其余模块不一样,xml.dom.pulldom模块提供的是一个“pull解析器”,其背后的基本概念指的是从XML流中pull事件,而后进行处理。虽然与SAX同样采用事件驱动模型(event-driven processing model),可是不一样的是,使用pull解析器时,使用者须要明确地从XML流中pull事件,并对这些事件遍历处理,直处处理完成或者出现错误。

pull解析(pull parsing)是近来兴起的一种XML处理趋势。此前诸如SAX和DOM这些流行的XML解析框架,都是push-based,也就是说对解析工做的控制权,掌握在解析器的手中。

四、xml.sax

  xml.sax模块实现的是SAX API,这个模块牺牲了便捷性来换取速度和内存占用。SAX是Simple API for XML的缩写,它并非由W3C官方所提出的标准。它是事件驱动的,并不须要一次性读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。

五、xml.parser.expat

  xml.parser.expat提供了对C语言编写的expat解析器的一个直接的、底层API接口。expat接口与SAX相似,也是基于事件回调机制,可是这个接口并非标准化的,只适用于expat库。

expat是一个面向流的解析器。您注册的解析器回调(或handler)功能,而后开始搜索它的文档。当解析器识别该文件的指定的位置,它会调用该部分相应的处理程序(若是您已经注册的一个)。该文件被输送到解析器,会被分割成多个片段,并分段装到内存中。所以expat能够解析那些巨大的文件。

六、xml.etree.ElementTree(如下简称ET)

  xml.etree.ElementTree模块提供了一个轻量级、Pythonic的API,同时还有一个高效的C语言实现,即xml.etree.cElementTree。与DOM相比,ET的速度更快,API使用更直接、方便。与SAX相比,ET.iterparse函数一样提供了按需解析的功能,不会一次性在内存中读入整个文档。ET的性能与SAX模块大体相仿,可是它的API更加高层次,用户使用起来更加便捷。

16.二、利用ElementTree解析XML

  Python标准库中,提供了ET的两种实现。一个是纯Python实现的xml.etree.ElementTree,另外一个是速度更快的C语言实现xml.etree.cElementTree。请记住始终使用C语言实现,由于它的速度要快不少,并且内存消耗也要少不少。若是你所使用的Python版本中没有cElementTree所需的加速模块,你能够这样导入模块:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

  若是某个API存在不一样的实现,上面是常见的导入方式。固然,极可能你直接导入第一个模块时,并不会出现问题。请注意,自Python 3.3以后,就不用采用上面的导入方法,由于ElemenTree模块会自动优先使用C加速器,若是不存在C实现,则会使用Python实现。所以,使用Python 3.3+的朋友,只须要import xml.etree.ElementTree便可。

以country.xml为例,内容以下:

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

1.解析

1)调用 parse() 方法,返回解析树

import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")  # <class 'xml.etree.ElementTree.ElementTree'>
root = tree.getroot()           # 获取根节点 <Element 'data' at 0x02BF6A80>

本质上和方法三相同,parse() 源码以下:

def parse(source, parser=None):
    """Parse XML document into element tree.

    *source* is a filename or file object containing XML data,
    *parser* is an optional parser instance defaulting to XMLParser.

    Return an ElementTree instance.

    """
    tree = ElementTree()
    tree.parse(source, parser)
    return tree

2)调用 from_string() ,返回解析树的根元素

import xml.etree.ElementTree as ET
data = open("country.xml").read()
root = ET.fromstring(data)   # <Element 'data' at 0x036168A0>

3)调用 ElementTree模块的 ElementTree(self, element=None, file=None)类  # 这里的element做为根节点

import xml.etree.ElementTree as ET
tree = ET.ElementTree(file="country.xml")  # <xml.etree.ElementTree.ElementTree object at 0x03031390>
root = tree.getroot()  # <Element 'data' at 0x030EA600>

2.遍历

1)简单遍历

import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()
print(root.tag, ":", root.attrib)  # 打印根元素的tag和属性

# 遍历xml文档的第二层
for child in root:
    # 第二层节点的标签名称和属性
    print("\t" + child.tag,":", child.attrib)
    # 遍历xml文档的第三层
    for children in child:
        # 第三层节点的标签名称和属性
        print("\t\t" + children.tag, ":", children.attrib)

能够经过下标的方式直接访问节点

# 访问根节点下第一个country的第二个节点year,获取对应的文本
year = root[0][1].text    # 2008

2)ElementTree提供的方法

  • find(match)                                                        # 查找第一个匹配的子元素, match能够时tag或是xpaht路径
  • findall(match)                                                         # 返回全部匹配的子元素列表
  • findtext(match, default=None)                               #
  • iter(tag=None)                                                       # 以当前元素为根节点 建立树迭代器,若是tag不为None,则以tag进行过滤
  • iterfind(match)                                                       #

例子:

# 过滤出全部neighbor标签
for neighbor in root.iter("neighbor"):
    print(neighbor.tag, ":", neighbor.attrib)

---

# 遍历全部的counry标签
for country in root.findall("country"):
    # 查找country标签下的第一个rank标签
    rank = country.find("rank").text
    # 获取country标签的name属性
    name = country.get("name")
    print(name, rank)

3.修改xml结构

1) 属性相关

# 将全部的rank值加1,并添加属性updated为yes
for rank in root.iter("rank"):
    new_rank = int(rank.text) + 1
    rank.text = str(new_rank)  # 必须将int转为str
    rank.set("updated", "yes") # 添加属性

# 再终端显示整个xml
ET.dump(root)
# 注意 修改的内容存在内存中 还没有保存到文件中
# 保存修改后的内容
tree.write("output.xml")

---

import xml.etree.ElementTree as ET

tree = ET.parse("output.xml")
root = tree.getroot()

for rank in root.iter("rank"):
    # attrib为属性字典
    # 删除对应的属性updated
    del rank.attrib['updated']  

ET.dump(root)

小结: 关于class xml.etree.ElementTree.Element 属性相关

  • attrib                                             #  为包含元素属性的字典
  • keys()                                                #  返回元素属性名称列表
  • items()                                               #  返回(name,value)列表
  • get(key, default=None)                      #  获取属性
  • set(key, value)                                   #  跟新/添加 属性
  • del xxx.attrib[key]                              # 删除对应的属性

2) 节点/元素 相关

删除子元素remove()

import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()

# 删除rank大于50的国家
for country in root.iter("country"):
    rank = int(country.find("rank").text)
    if rank > 50:
        # remove()方法 删除子元素
        root.remove(country)

ET.dump(root)

添加子元素

import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()

country = root[0]
last_ele = country[len(list(country))-1]
last_ele.tail = '\n\t\t'
# 建立新的元素, tag为test_append
elem1 = ET.Element("test_append")
elem1.text = "elem 1"
# elem.tail = '\n\t'
country.append(elem1)

# SubElement() 其实内部调用的时append()
elem2 = ET.SubElement(country, "test_subelement")
elem2.text = "elem 2"

# extend()
elem3 = ET.Element("test_extend")
elem3.text = "elem 3"
elem4 = ET.Element("test_extend")
elem4.text = "elem 4"
country.extend([elem3, elem4])

# insert()
elem5 = ET.Element("test_insert")
elem5.text = "elem 5"
country.insert(5, elem5)

ET.dump(country)

添加子元素方法总结:

  • append(subelement)
  • extend(subelements)
  • insert(index, element)

4.建立xml文档

  想建立root Element,而后建立SubElement,最后将root element传入ElementTree(element),建立tree,调用tree.write()方法写入文件

  对于建立元素的3个方法: 使用ET.Element、Element对象的makeelement()方法以及ET.SubElement

import xml.etree.ElementTree as ET


def subElement(root, tag, text):
    ele = ET.SubElement(root, tag)
    ele.text = text
    ele.tail = '\n'


root = ET.Element("note")

to = root.makeelement("to", {})
to.text = "peter"
to.tail = '\n'
root.append(to)

subElement(root, "from", "marry")
subElement(root, "heading", "Reminder")
subElement(root, "body", "Don't forget the meeting!")

tree = ET.ElementTree(root)
tree.write("note.xml", encoding="utf-8", xml_declaration=True)

效果:

  因为原生保存的XML时默认无缩进,若是想要设置缩进的话, 须要修改保存方式

import xml.etree.ElementTree as ET
from xml.dom import minidom


def subElement(root, tag, text):
    ele = ET.SubElement(root, tag)
    ele.text = text


def saveXML(root, filename, indent="\t", newl="\n", encoding="utf-8"):
    rawText = ET.tostring(root)
    dom = minidom.parseString(rawText)
    with open(filename, 'w') as f:
        dom.writexml(f, "", indent, newl, encoding)


root = ET.Element("note")

to = root.makeelement("to", {})
to.text = "peter"
root.append(to)

subElement(root, "from", "marry")
subElement(root, "heading", "Reminder")
subElement(root, "body", "Don't forget the meeting!")

# 保存xml文件
saveXML(root, "note.xml")

1七、gzip

建立gzip文件

import gzip
content = "Lots of content here.\n第二行"
f = gzip.open('file.txt.gz', 'wb')
f.write(content.encode("utf-8"))
f.close()

对gzip文件的解压

import gzip
f = gzip.open("file.txt.gz", 'rb')#打开压缩文件对象
f_out=open("file","w")#打开解压后内容保存的文件
file_content = f.read()    #读取解压后文件内容
f_out.write(file_content.decode("utf-8")) #写入新文件当中
print(file_content.decode("utf-8")) #打印读取内容
f.close() #关闭文件流
f_out.close()

压缩现有文件

 

import gzip
f_in = open('test.png', 'rb')
f_out = gzip.open('test.png.gz', 'wb')

f_out.writelines(f_in)
# f_out.write(f_in.read())

f_out.close()
f_in.close()

 

压缩数据

import gzip
data = "这是一串字符串"
bytes_data = gzip.compress(data.encode("utf-8"))
print("字节压缩: ", bytes_data)

>>>
字节压缩:  b'\x1f\x8b\x08\x00\xbb5Y]\x02\xff{\xb1\x7f\xe6\xb3\x19\xeb\x9f\xechx\xb2c\xd3\xd3\xb5\xd3\x9f\xafY\x06d\x00\x00u\xa1\x12E\x15\x00\x00\x00'

数据解压

import gzip
data = "这是一串字符串"
bytes_data = gzip.compress(data.encode("utf-8"))
print("字节压缩: ", bytes_data)

bytes_dedata = gzip.decompress(bytes_data)
print("字节解压: ", bytes_dedata.decode("utf-8"))
相关文章
相关标签/搜索