目录
  1. 1. shutil
  2. 2. glob
  3. 3. shlex
  4. 4. argparse
  5. 5. re
    1. 5.1. 正则表达式语法
    2. 5.2. re库flags参数
    3. 5.3. re库正则函数
    4. 5.4. MatchObject对象
  6. 6. os
    1. 6.1. os.path
  7. 7. sys
  8. 8. time
  9. 9. datetime
    1. 9.1. datetime.date
    2. 9.2. datetime.time
    3. 9.3. datetime.datetime
    4. 9.4. datetime.timedelta
  10. 10. paramiko
  11. 11. subprocess
    1. 11.1. subprocess.Popen()
  12. 12. functools
  13. 13. smtplib
  14. 14. requests
  15. 15. hashlib
  16. 16. multiprocessing
  17. 17. MySQLdb
    1. 17.1. 执行sql
    2. 17.2. 获取查询结果
  18. 18. ConfigParser
  19. 19. lxml
  20. 20. BeautifulSoup
    1. 20.1. Tag——标签
    2. 20.2. find_all——搜索

 Python库非常多,为方便快速查询,将自己之前用到的库做个简单汇总记录,不定期持续更新。

shutil

shutil模块提供目录和文件的高级操作

  • 复制操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import shutil

    #将文件src复制到文件或目录dest,保留文件权限
    shutil.copy(src, dest)

    #在shutil.copy的基础上加上copystat(),即将文件的访问/修改时间等信息也复制
    shutil.copy2(src, dest)

    #复制文件src的内容(不包含元数据)到文件dest(不支持目录复制,dest中必须包含文件名)
    shutil.copyfile(src, dest)

    #将类文件对象src.log的内容复制到类文件对象dest.log
    shutil.copyfileobj(open('src.log','r'), open('dest.log', 'w'))

    #递归拷贝src。symlinks=True则软链接文件也会被复制
    shutil.copytree(src, dest, symlinks=True, ignore=shutil.ignore_patterns('*.pyc', '*.swap'))
  • 移动&删除操作

    1
    2
    3
    4
    5
    #递归方式移动文件或目录
    shutil.move(src, dest)

    #递归删除文件
    shutil.rmtree(folder1, folder2)
  • 压缩文件
     压缩文件。将目录”/usr/local/project”压缩到”/home/mogl/test.tar.gz”
     压缩包支持类型:zip/tar/gztar/bztar

    1
    shutil.make_archive("/home/mogl/test", 'gztar', root_dir="/usr/local/project")

glob

glob模块提供Unix Shell规则的文件匹配功能

1
2
3
4
5
6
7
8
9
import glob

glob.glob('*.png')
glob.glob('[0-9].*')

#返回迭代器
files = glob.iglob('*.log')
for each_file in files:
print each_file

shlex

shlex模块提供简单的Unix Shell命令参数词法分析功能,可结合subprocess使用。

1
2
3
4
import shlex

shlex.split("ls -lht /tmp")
#['ls', '-lht', '/tmp']

argparse

argparse是用于命令行参数解析的模块,功能非常强大,目前所用到的只是简单功能,直接上实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import argparse

parser = argparse.ArgumentParser(description='descritption', epilog="author:mogl")

parser.add_argument('-d', '--directory', action='store_true', help='Only search directory')
parser.add_argument('-i', '--ignore-case', action='store_true', help='Ignore case search')
parser.add_argument('-S', '--srcport', help='sorce port', type=int, required=True)
parser.add_argument('-D', '--destport', help='dest port', type=int, default=80)
parser.add_argument('file_name', nargs='+', help='The file name')

args = parser.parse_args()

#获取参数值
print args.ignore_case
print args.srcport
print args.destport

#python test.py -S 124 test mogl
#False
#124
#80
#['test', 'mogl']

  • 参数
    参数可分为两种:

    • 选项参数(optional)
      parser.add_argument('-i', '--ignore-case', action='store_true', help='Ignore case search')
      选项参数默认使用-做前缀标识
    • 位置参数(positional)
      parser.add_argument('file_name', nargs='+', help='The file name')
  • 选项参数解析
     选项参数默认返回None,可用default参数指定默认值。如果该参数是必须的则使用required=True
    parser.add_argument('-i', '--ignore-case', action='store_true', help='Ignore case search')
    parser.add_argument('-S', '--srcport', help='sorce port', type=int, required=True)

    • '-i', '--ignore-case'
      参数长短语法
    • action='store_true'
      表明该参数不接受参数传递,即不接收诸如-i test--ignore-case=true
    • type=int
      若不使用action='store_true'则可表明接受参数传递,使用type指定传入类型(type=int|str|complax)
    • help='sorce port'
      该参数的说明
  • 位置参数
     若指定了位置参数,则运行程序时必须传入该参数才能运行
    parser.add_argument('file_name', nargs='+', help='The file name')

    • nargs
      将多个参数关联在一起。+将多个参数存到一个列表里,至少要有一个参数否则报错
  • action
     argparse默认设置6个action:

    • store
      保存参数值,可能会先将参数值转换成另一个数据类型。若没有显式指定动作,则默认为该动作。
    • store_const
      保存一个被定义为参数规格一部分的值,而不是一个来自参数解析而来的值。这通常用于实现非布尔值的命令行标记。
    • store_ture/store_false
      保存相应的布尔值。这两个动作被用于实现布尔开关。
    • append
      将值保存到一个列表中。若参数重复出现,则保存多个值。
    • append_const
      将一个定义在参数规格中的值保存到一个列表中。
    • version
      打印关于程序的版本信息,然后退出

re

re提供正则表达式匹配操作,是处理字符串最常用的库。

正则表达式语法

 使用re库前,先大致了解正则表达式的基本语法,正好顺便总结一下有关正则表达式的内容。

符号 意义
基本关键字
. 常规模式下匹配除\n外的所有字符,DOTALL模式下\n也匹配
^ 常规模式下匹配字符串开头,MULTILINE模式下匹配每行开头
$ 常规模式下匹配字符串结尾,MULTILINE模式下匹配行尾
* 匹配*前面内容零次或多次,贪婪匹配
+ 匹配+前面内容零次或多次,贪婪匹配
? 匹配?前面内容零次或1次
{m} 匹配{m}前面内容m次
{m,n} 匹配{m,n}前面内容m~n次
*? +? ?? {m,n}? * + ? {m,n}都是贪婪匹配,后面加上?后为非贪婪匹配
[] 匹配[]内字符集的任一字符,^表示不匹配,-并用给出一段范围字符
单竖线 或,只匹配其中一个
分组关键字
(…) 匹配括号内任意正则表达式并形成一个分组
(?P<name>…) 为符合匹配的分组命名
(?#…) 注释,(?#..)里的内容会被忽略
(?=…) 匹配结果后面的字符串需要满足表达式...python_re_test——.*(?=_test)则匹配到字符串python_re
(?!…) 匹配结果后面的字符串必须不满足表达式...
(?<=…) 匹配结果前面的字符串需要满足表达式...python_re_test——(?<=python_).*则匹配字符串re_test
(?<!…) 匹配结果前面的字符串必须不满足表达式...
特殊关键字
\d 匹配数字,相当于[0-9]
\D 匹配非数字,相当于[^0-9]
\s 匹配空白字符,相当于[\t\r\n\f\v]
\S 匹配非空白字符,相当于[^\t\r\n\f\v]
\w 匹配字母或数字,相当于[0-9a-zA-Z]
\W 匹配非字母或数字,相当于[^0-9a-zA-Z]
\b 匹配字符串边界
\B 匹配非字符串边界
\A 匹配字符串开头
\Z 匹配字符串结尾

re库flags参数

 Python的re库API中可指定flags参数,通过这些flags参数指定正则表达式选项通常使用类似re.I这样的简写,比如re.compile(pattern, [flags])函数——re.compile(pattern, re.I)

符号 意义
re.A ASCII 使\w\W\b\B\d\D匹配ASCII字符
re.I IGNORECASE 忽略大小写
re.L LOCALE 使\w\W\b\B匹配本地字符集
re.M MULTILINE 多行模式,^ 匹配每行开头,$匹配每行结尾
re.X VERBOSE 详细模式,忽略空格和#后面的注释
re.U UNICODE 使\w\W\b\B\d\D匹配unicode字符集

re库正则函数

  • re.compile(pattern[, flags])
     将模式和标识编译成正则表达式对象,方便给search()match()findall()等函数使用。

    1
    2
    3
    4
    5
    import re

    pattern = re.compile(r'[0-9]+')
    print pattern.findall('abc123d')
    #123
  • re.search(pattern, string[, flags])
     在字符串string中查找匹配pattern表达式的串,成功则返回MatchObject对象,失败则返回None

    • re.search (string[, pos[, endpos]])
       对于已编译的正则表达式对象,search()函数可指定搜索的起始结束位置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import re

      #使用compile()
      pattern = re.compile("a+")
      print pattern.search("aabcde").group(0) #=>aa
      print pattern.search("aabcde", 1).group(0) #=>a

      #不使用compile()
      regx = re.search(r'a+', 'aabcde')
      print regx.group(0) #=>aa
  • re.match(pattern, string[, flags])
     在字符串string开头位置查找匹配pattern表达式的串(必须是开头位置)

    • re.match(string[, pos[, endpos]])
       同样对于已编译的正则对象可指定位置
      1
      2
      3
      pattern = re.compile("a+")
      print pattern.match('aabcde').group(0) #=>aa
      print pattern.match('xaabcde') #=>None
  • re.findall(pattern, string[, flags])
     在字符串string中查找所有匹配pattern的串,成功则返回列表,失败则返回空列表。
    re.search()re.match()仅匹配一次

    • re.findall(string[, pos[, endpos]])
       同样对于已编译的正则对象可指定位置
      1
      2
      3
      4
      5
      pattern = re.compile("\d+")

      print pattern.search('ab12cd34').group(0) #=>12
      print pattern.match('ab12cd34') #=>None
      print pattern.findall('ab12cd34') #=>['12', '34']
  • re.finditer(pattern, string[, flags])
    re.finditer()re.findall()类似,只是re.finditer()返回一个迭代器对象。

    1
    2
    3
    4
    5
    pattern = re.compile("\d+")
    for each_iter in piter:
    print each_iter.group(0)
    #=>12
    #=>34
  • re.sub(pattern, rep_string, string[, count, flags])

  • re.subn(pattern, rep_string, string[, count, flags])
     在字符串string中查找匹配pattern的字符串并用rep_string替换。rep_string可以是一个函数,成功则返回替换后的字符串,否则返回原字符串。
    re.subn()re.sub()类似,只是re.subn()返回替换后的字符串和替换次数
    1
    2
    3
    pattern = re.compile(r'(name|full_name)')
    pattern.sub('mogl', 'name test python re full_name')
    #=>'mogl test python re mogl'

MatchObject对象

re.match()re.search()re.finditer()若成功匹配的话都是返回一个MatchObject对象。(re.findall()返回列表)
 MatchObject对象可调用几个函数:

  • group():返回匹配的完整字符串
  • groups():返回分组信息
  • groupdict():返回所有命名分组字典
  • start():返回匹配字符串的起始位置
  • end():返回匹配字符串的结束位置
  • span():返回起始位置和结束位置的元组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re

mystr = '12ab34cd'

pattern = re.compile(r'(\d+)(?P<letter>[a-zA-Z]+)')

m = pattern.search(mystr)

m.group() #=>'12ab'

m.start() #=>0

m.end() #=>4

m.span() #=>(0, 4)

m.groups() #=>('12', 'ab')

m.groupdict() #=>{'letter': 'ab'}

m.group(0, 1, 2) #=>('12ab', '12', 'ab')

os

os模块提供统一的操作系统功能。

  • os.getcwd()
    获取当前工作目录

  • os.chdir(path)
    修改工作目录

  • os.listdir(path)
    列出path下所有文件/目录,不支持递归和通配符

  • os.walk(path)
    深度遍历path下所有文件/目录

    1
    2
    for root, dirs, files in os.walk('/tmp'):
    print "root=%s, dirs=%s, files=%s" % (root, dirs, files)**
  • os.mkdir(path)
    创建目录,若path已存在则抛出异常

  • os.makedirs(‘/path/subpath’)
    递归创建多级目录,相当于Linux命令mkdir -p

  • os.rmdir(path)
    删除空目录

  • os.removedirs(path)
    递归删除path下的子目录,目录非空则异常

  • os.remove(file)
    删除文件,若file为目录则抛出异常

  • os.rename(path1, path2)
    重命名

  • os.renames(path1, path2)
    重命名,会创建path2目标路径

  • os.chmod(path, mode)
    修改权限

    1
    os.chmod('/tmp/test', 0777)**
  • os.chown(path, uid, gid)
    修改拥有者

  • os.access(path, mode)
    权限测试

    1
    2
    3
    os.access('/tmp/test.txt', os.W_OK)**
    os.access('/tmp/test.txt', os.R_OK)**
    os.access('/tmp/test.txt', os.X_OK)**

os.path

os.path提供操作路径的函数。

  • os.path.abspath(path)
    获取绝对路径

  • os.path.realpath(path)
    软连接的真实路径

  • os.path.basename(path)
    获取文件名,以/结尾的路径返回空

  • os.path.dirname(path)
    获取目录名

  • os.path.exists(path)
    判断path路径是否存在

  • os.path.isfile(path) / os.path.isdir(path) / os.path.islink(path) / os.path.ismount(path)
    判断path是否为文件/目录/软连接/挂载点

  • os.path.split(path)
    path切分为(目录, 文件名)

  • os.path.splitext(path)
    path切分为(目录/主文件名, 后缀名)

  • os.path.join(path1, path2)
    拼接路径

sys

sys模块,主要处理Python环境的变量使得程序与系统环境有交互。

  • sys.argv[x]
    获取命令行参数列表。0表示程序名,1表示第一个命令行第一个参数,以此类推。
  • sys.exit(num)
    程序退出状态码,sys.exit(0)表示正常退出。
  • sys.path
    返回模块的搜索路径。
  • sys.stdout / sys.stdin / sys.stderr
    标准输出/输入/错误输出

time

time模块主要提供和时间相关的函数。

  • time.time()
    返回距离纪元(1970-01-01 00:00:00)开始的秒数,返回值为浮点数。
  • time.ctime()
    返回字符串型易读的时间,诸如:Wed Oct 1 20:18:59 2016
  • time.clock()
    返回当前进程消耗的CPU时间(秒)
  • time.gmtime() / time.localtime()
    time.gmtime()time.localtime()的结果都是使用struct_time格式显示,可使用time.asctime()转变成time.ctime()的字符串格式显示。
    time.gmtime()获取的是UTC时间,time.localtime()获取的是当前时区的时间。在中国:time.gmtime() = time.localtime() + 8hour

    1
    2
    3
    4
    5
    6
    7
    8
    time.gmtime()
    #time.struct_time(tm_year=2016, tm_mon=10, tm_mday=5, tm_hour=15, tm_min=29, tm_sec=14, tm_wday=2, tm_yday=279, tm_isdst=0)

    time.localtime()
    #time.struct_time(tm_year=2016, tm_mon=10, tm_mday=5, tm_hour=23, tm_min=30, tm_sec=4, tm_wday=2, tm_yday=279, tm_isdst=0)

    time.asctime(time.localtime())
    #'Wed Oct 5 23:30:35 2016'
  • time.strptime()
    将字符串格式时间转成struct_time格式时间

    1
    2
    time.strptime(time.ctime())
    #time.struct_time(tm_year=2016, tm_mon=10, tm_mday=5, tm_hour=23, tm_min=37, tm_sec=17, tm_wday=2, tm_yday=279, tm_isdst=-1)
  • time.strftime()
    将时间格式化输出

    1
    2
    time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    #'2016-10-05 23:39:23'
  • time.sleep(num)
    进程sleep时间

datetime

datetime模块提供操作日期和时间函数。
datetime模块常用的有四个类别,这些对象都是不可变对象:

  • datetime.date:用于操作日期
  • datetime.time:用于操作时分秒
  • datetime.datetime:用于操作日期和时分秒
  • datetime.timedelta:用于操作时间间隔

datetime.date

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from datetime import date

print date.today()
#2016-10-05

now = date.today()
#2016-10-05
tomorrow = now.replace(day=06)
#2016-10-06

#date.weekday(),星期一返回0,以此类推
now.weekday()
#2

#date.isoformat(),返回`YYYY-MM-DD`
now.isoformat()
#'2016-10-06'

#date.strftime(format),格式化时间输出
now.strftime('%Y-%m-%d %H:%M:%S')
'2016-10-06 00:00:00'

datetime.time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import datetime

mytime = datetime.time(23, 59, 10)

print mytime
#23:59:10

print "hour: %s, minute: %s, second: %s" % (mytime.hour, mytime.minute, mytime.second)
#hour: 23, minute: 59, second: 10

mytime2 = mytime.replace(hour=22)

print mytime2
#22:59:10

datetime.datetime

datetime.datetimedatetime.time差不多

1
2
3
4
5
6
7
8
start = datetime.datetime.now()
#2016-10-06 23:47:29.105713

end = datetime.datetime.now()
#2016-10-06 23:47:45.449751

print (end - start).seconds
#16

datetime.timedelta

 主要用于做时间的加减操作。

1
2
3
4
5
6
7
today = datetime.datetime.today()
#2016-10-06 23:41:46.170686

delta = datetime.timedelta(days=1)

yesterday = today - delta
#2016-10-05 23:41:46.170686

paramiko

paramiko模块提供ssh远程登录等相关功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import paramiko

def ssh_command(ip, username, passwd, cmd, port=22):
"""paramiko demo"""
try:
ssh = paramiko.SSHClient()
#允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip, port, username, passwd, timeout=30)
stdin, stdout, stderr = ssh.exec_command(cmd)
except Exception, e:
raise "%s ssh connect error: %s" % (ip, e)
finally:
ssh.close()
std_result = {'stdout': stdout.readlines(),
'stderr': stderr.readlines()
}
return std_result


#paramiko模块还支持sftp传输功能

scp=paramiko.Transport((host_ip, ssh_port))
scp.connect(username=ssh_username, password=ssh_passowrd)
sftp=paramiko.SFTPClient.from_transport(scp)
#如果之前已有一个建立连接的ssh对象,则可直接复用该ssh连接对象
#sftp = paramiko.SFTPClient.from_transport(ssh.get_transport())
#sftp = ssh.open_sftp()

#下载文件
sftp.get('/tmp/remote_file','/tmp/local_file')
#上传文件
sftp.put('/home/local_file','/tmp/remote_file')
scp.close()

subprocess

subprocess模块允许创建新进程并获取返回值/输出信息,常用来调用系统命令。
 常用已封装好的函数有三个:

  • subprocess.call()
    返回exit code。不管exit code是什么都不会抛出异常。
  • subprocess.check_call()
    返回exit code并检查exit codeexit code0则成功,否则抛出subprocess.CalledProcessError异常。
  • subprocess.check_output()
    返回输出信息且检查exit codeexit code不为0则抛出subprocess.CalledProcessError异常。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import subprocess

    #结果相同,返回的是exit code
    subprocess.call(['echo', 'test'])
    subprocess.call("echo test", shell=True)
    subprocess.check_call(['echo', 'test'])
    #test
    #0

    #返回的是输出信息
    subprocess.check_output(['echo', 'test'])
    #'test\n'

    #可用shlex模块解析参数而不使用`shell=True`
    subprocess.call(shlex.split("echo test"))
    #test
    #0

    #忽略输出,只获取exit code
    with open(os.devnull, 'w') as devnull:
    exit_code = subprocess.call(["echo", "test"], stdout=devnull, stderr=devnull, shell=True)

subprocess.Popen()

subprocess.callsubprocess.check_callsubprocess.check_output都是基于subprocess.Popen()的封装。在创建了Popen对象后,父进程不会主动等待子进程需要调用wait()方法使其等待。

1
2
3
4
5
subprocess.Popen('ps aux|grep ipython', shell=True).wait()
#命令输出
#=>mogl 28580 0.0 0.3 32568 15332 pts/26 Sl+ 16:35 0:01 /usr/bin/python /usr/local/bin/ipython
#返回值
#=>0

 可使用subprocess.PIPE改变输入输出对象并获取子进程的更详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
child = subprocess.Popen('ps aux|grep ipython', shell=True, stdout=subprocess.PIPE)

child.pid
#25556

child.stdout.readlines()
#mogl 28580 0.0 0.3 32568 15332 pts/26 Sl+ 16:35 0:01 /usr/bin/python /usr/local/bin/ipython

child.poll
#检查子进程是否已终止。没终止返回空,终止返回exit code

child.wait()
#0

child.returncode
#0

subprocess.PIPE还可以将多个子进程的输入输出连接
communicate()是Popen对象的方法,该方法会阻塞父进程,直到子进程完成。child2的输出也在PIPE中,需要调用child2.communicate()读取。

1
2
3
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()

functools

functools提供高阶函数功能。主要包含cmp_to_keypartialreducetotal_orderingupdate_wrapperwraps这几个函数。

  • functools.cmp_to_key(func)
    functools.cmp_to_key()是Python2.7新增的函数,用于将比较函数转成key函数,需要在接受key函数作为参数的函数中才使用。其实是为了兼容Python3,拿sorted()函数为例:Python2版本的sorted()函数支持cmp参数来处理用户自定义的排序函数,但Python3后将cmp参数移除了,所以需要使用到cmp_to_key将自定义的排序函数转成key函数

    1
    2
    3
    4
    5
    6
    7
    #Python2
    sorted([5, 2, 4, 1, 3], cmp=lambda x, y: y-x)
    #=>[5, 4, 3, 2, 1]

    #Python3
    sorted([5, 2, 4, 1, 3], key=cmp_to_key(lambda x, y: y-x))
    #=>[5, 4, 3, 2, 1]
  • functools.total_ordering(cls)
    functools.total_ordering()也是Python2.7新增的函数,主要用于简化比较函数的写法。当某个类定义了__eq__()方法,并且定义了__lt__()__le__()__gt__()__ge__()方法中的其中一个,若使用@total_ordering()装饰器,则该类会自动生成其他的比较方法。
    官方例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from functools import total_ordering


    @total_ordering
    class Student:
    def __eq__(self, other):
    return ((self.lastname.lower(), self.firstname.lower()) ==
    (other.lastname.lower(), other.firstname.lower()))

    def __lt__(self, other):
    return ((self.lastname.lower(), self.firstname.lower()) <
    (other.lastname.lower(), other.firstname.lower()))

    print dir(Student)
    #=>['__doc__', '__eq__', '__ge__', '__gt__', '__le__', '__lt__', '__module__']
  • functools.reduce(function, iterable)
    functools.reduce()和内置的reduce()功能一样,使用functools.reduce()也是为了兼容Python3。

  • functools.partial(func[,*args][, keywords])**
    functools.partial()简单的说就是实现柯里化的。

    1
    2
    3
    4
    5
    6
    7
    8
    from functools import partial

    def add(x, y):
    return x + y

    add_y = partial(add, 1)
    print add_y(1)
    #=>2
  • functools.wraps(wrapped[, assigned][, updated])
    functools.wraps()主要的作用是消除Python中装饰器带来的一些副作用。Python中使用装饰器,被装饰函数的__name__属性会被改变,需要用functools.wraps()进行修正。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from functools import wraps

    def decorator(func):
    #@wraps(func)
    def wrapper(*args, **kwargs):
    print "In decorator wrapper func."
    return func(*args, **kwargs)
    return wrapper

    @decorator
    def test():
    print "In test func."

    print test.__name__
    #=>wrapper
  • functools.update_wrapper(wrapper, wrapped[, assigned][, updated])
    functools.wraps()functools.update_wrapper()的简化版本,functools.update_wrapper()功能更强大,默认会将被装饰函数的functools.WRAPPER_ASSIGNMENTS(__module____name____doc__)和functools.WRAPPER_UPDATES(__dict__)都复制给装饰函数。
    实际上面的装饰器代码可用update_wrapper()写:

    1
    2
    3
    4
    5
    6
    7
    from functools import update_wrapper

    def decorator(func):
    def wrapper(*args, **kwargs):
    print "In decorator wrapper func."
    return func(*args, **kwargs)
    return update_wrapper(wrapper, func)

smtplib

smtplib模块用于发送邮件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

def send_mail(send_list, subject, content):
"""html/text邮件"""
mail_host = "your_smtp_server_address"
mail_user = 'your_email_user(xxx@gmail.com)'
mail_passwd = 'your_email_password'
mail_port = 25
send_from = "your_send_from" + "<" + mail_user + ">"

##TEXT邮件
#msg = MIMEText(content, _subtype='plain', _charset='utf-8')

##HTML邮件
#msg = MIMEText(content, _subtype='html', _charset='utf-8')

##带附件邮件
msg = MIMEMultipart()
#邮件正文
msg.attach(MIMEText(content, _charset='utf-8'))
#附件一
att_1 = MIMEText(open('/tmp/attachment1.txt', 'rb').read(), 'base64', 'utf-8')
att_1['Content-Type'] = 'application/octet-stream'
#邮件中附件显示的文件名字
att_1['Content-Disposition'] = 'attachment; filename=%s' % "附件1"
msg.attach(att_1)

msg['From'] = send_from
msg['Subject'] = subject

try:
mail_server = smtplib.SMTP()
mail_server.connect(mail_host, mail_port)
mail_server.login(mail_user, mail_passwd)
mail_server.sendmail(msg['From'], send_list, msg.as_string())
mail_server.close()
return True
except Exception, e:
print str(e)
return False

requests

requests是第三方模块,相比Python内置的urlib2更人性化。
 由于requests是第三方模块,需要使用pip进行安装,在一些版本中可能会在使用中出现SNIMissingWarningInsecurePlatformWarning警告,可按以下方法安装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#安装
yum install -y openssl-devel python-devel libffi-devel
pip install 'requests[security]'

#若在使用中出现SNIMissingWarning和InsecurePlatformWarning警告可
pip install pyopenssl ndg-httpsclient pyasn1

#若不想额外安装包可手动忽略警告
#关闭requests的https警告
try:
from requests.packages.urllib3.exceptions import (
SNIMissingWarning,
InsecureRequestWarning,
InsecurePlatformWarning
)
requests.packages.urllib3.disable_warnings(SNIMissingWarning)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
except ImportError:
pass

requests常用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import requests
import json

url = 'https://github.com/timeline.json'

#GET请求(10s超时)
r = requests.get(url, timeout=10)

#文本方式显示
print r.text
#字节方式显示,中文显示为字符
print r.content
#显示编码 & 设置编码
print r.encoding
r.encoding = 'utf-8'
#响应状态码
r.status_code
#HTTP header
r.headers
r.headers['Content-Type']
#JSON化,失败会抛出异常
r.json()


#POST请求
headers = {
'content-type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}
data = {
"mobile": mobile,
"templateId": template_id,
"templateVariable": {
self.mobile: {
"secode": vcode
}
},
"userName": user_name,
"randomKey": random_key,
"fingerprint": fingerprint
}

try:
r = requests.post(url, data=json.dumps(data), headers=headers)
result = r.json()
#result = json.loads(r.content)
except Exception, err:
raise err

hashlib

hashlib模块实现各种hash算法。hashlib基于OpenSSL,支持md5sha1sha224sha256sha384sha512等算法。
 基本用法。update()可多次调用,每次都会根据新增文本更新结果。

1
2
3
4
5
6
7
8
import hashlib

mystr = 'mogl'

mymd5 = hashlib.md5()
mymd5.update(mystr)
print mymd5.hexdigest()
#=>d2624edb33f3be2c461b1e95740b6b5c

 用new()方法通过字符串形式指定算法

1
2
3
4
5
6
7
import hashlib
import sys

hash_name = sys.argv[1]
myhash = hashlib.new(hash_name)
myhash.update(mystr)
print myhash.hexdigest()

multiprocessing

multiprocessing模块提供多进程编程。

  • multiprocessing.Process
    Process类的原型multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})。每个Process类会生成一个process对象并在新的进程中运行。
     每个process对象最多只能调用一次start()方法join([timeout])方法会阻塞调用process对象的进程等待子进程执行完毕。

    • group:仅为兼容threading.Thread,通常为None
    • target:子进程要执行的函数
    • name:进程名,默认为Process-1Process-2...
    • argstarget函数调用的参数
    • kwargstarget函数调用的字典参数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      from multiprocessing import Process
      import os

      def child_run(name):
      print "Run in child process name: %s, pid: %s" % (name, os.getpid())

      if __name__ == '__main__':
      print "Parent process pid: %s" % os.getpid()
      print "Creating child process..."
      #注意args参数中必须有,
      multi_proc = Process(target=child_run, args=('child_test',))
      #设置daemon属性
      #multi_proc.daemon = True
      print "Starting child process..."
      multi_proc.start()
      multi_proc.join()
      print "Process end."
      #Parent process pid: 12198
      #Creating child process...
      #Starting child process...
      #Run in child process name: child_test, pid: 12199
      #Process end.

       多进程并行运行。每个子进程都是先start(),最后在一起join()。若对每个process对象都一起执行start()join()则变成顺序执行而非并发执行。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
         from multiprocessing import Process
      import os

      def child_run(name):
      print "Run in child process name: %s, pid: %s" % (name, os.getpid())

      if __name__ == '__main__':
      names = ['mo', 'guo', 'liang', 'mogl']
      proc_list = []
      print "Parent process pid: %s" % os.getpid()
      for name in names:
      multi_proc = Process(target=child_run, args=(name,))
      proc_list.append(multi_proc)
      multi_proc.start()
      for each_child_proc in proc_list:
      each_child_proc.join()

      #Parent process pid: 14516
      #Run in child process name: mo, pid: 14517
      #Run in child process name: guo, pid: 14518
      #Run in child process name: liang, pid: 14519
      #Run in child process name: mogl, pid: 14520
  • multiprocessing.Lock
    multiprocessing.Lock提供锁功能,当多进程需要访问共享资源时,可用锁来避免冲突。使用锁的话多进程并行则会受到锁的限制。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
       from multiprocessing import Process, Lock
    import os

    def printer(string):
    print "In printer, string: %s, pid: %s" % (string, os.getpid())

    def printer_lock(string, lock):
    lock.acquire()
    print "In printer, string: %s, pid: %s" % (string, os.getpid())
    lock.release()

    if __name__ == '__main__':
    proc_list = []
    names = ['mo', 'guo', 'liang', 'mogl']
    print "Parent pid: %s" % os.getpid()

    for each_name in names:
    multi_proc = Process(target=printer, args=(each_name,))
    proc_list.append(multi_proc)
    multi_proc.start()

    lock = Lock()
    for each_name in names:
    multi_proc_lock = Process(target=printer_lock, args=(each_name, lock))
    proc_list.append(multi_proc_lock)
    multi_proc_lock.start()

    for each_proc in proc_list:
    each_proc.join()

    #Parent pid: 9313

    #In printer, string: mo, pid: 9314
    #In printer, string: liang, pid: 9316
    #In printer, string: guo, pid: 9315
    #In printer, string: mogl, pid: 9317

    #In printer, string: mo, pid: 9318
    #In printer, string: guo, pid: 9319
    #In printer, string: liang, pid: 9320
    #In printer, string: mogl, pid: 9321
  • multiprocessing.Queue & multiprocessing.Pipe
    multiprocessing支持两种进程间通信方式:QueuePIPEQueue是进程安全的。

    • multiprocessing.Queue
       使用Queue实现进程间通信,进程A使用put()方法将数据存入Queue,进程B使用get()方法从Queue中读取数据。
      put()get()方法都有两个参数:blocktimeout,若block=False,Queue为full/empty则抛出异常,若设置了timeout则阻塞等待再一次put/get

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
            from multiprocessing import Process, Queue
      import os

      def write_queue(queue):
      print "In write_queue pid: %s" % os.getpid()
      queue.put(['mogl'], block=False)

      def read_queue(queue):
      print "In read_queue pid: %s" % os.getpid()
      print queue.get(block=False)

      if __name__ == '__main__':
      print "Parent pid: %s" % os.getpid()
      queue = Queue()

      write_proc = Process(target=write_queue, args=(queue,))
      write_proc.start()

      read_proc = Process(target=read_queue, args=(queue,))
      read_proc.start()

      write_proc.join()
      read_proc.join()

      #Parent pid: 18383
      #In write_queue pid: 18384
      #In read_queue pid: 18385
      #['mogl']
    • multiprocessing.Pipe
      Pipe()方法会返回(c1, c2)代表一个管道的两端,默认Pipe()duplex=True表示全双工管道,即管道的任意一端都可发送和接收信息。若duplex=Falsec1只负责接收,c2只负责发送。当调用recv()方法从管道读取消息时候,若管道一端无消息,recv()则会一直阻塞

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
            from multiprocessing import Process, Pipe
      import os

      def process_one(pipe):
      pipe.send('hello, process_one send message.')
      print 'process_one receive message:', pipe.recv()
      print "process_one pid:", os.getpid()

      def process_two(pipe):
      print 'process_two receive message:', pipe.recv()
      pipe.send('hello, process_two send message.')
      print "process_two pid:", os.getpid()

      if __name__ == '__main__':
      pipe = Pipe()

      print "main pid:", os.getpid()
      pro1 = Process(target=process_one, args=(pipe[0],))
      pro2 = Process(target=process_two, args=(pipe[1],))

      pro1.start()
      pro2.start()
      pro1.join()
      pro2.join()

      #process_one发送消息后recv,由于管道一端无消息而阻塞,故先打印process_two的内容
      #main pid: 14215
      #process_two receive message: hello, process_one send message.
      #process_two pid: 14217
      #process_one receive message: hello, process_two send message.
      #process_one pid: 14216
  • multiprocessing.Pool
     Python多进程支持进程池,使用multiprocessing.Pool能创建一个进程池可指定进程数量,当请求提交给pool后,若pool没满则可创建新进程处理该请求,若已满则等待空闲后再处理。
     进程池中进程的创建可通过apply_async(func[, args[, kwds[, callback]]])apply(func[, args[, kwds]])方法。apply_async()非阻塞apply()阻塞(进程池中一个进程执行完才会执行下一个)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
      from multiprocessing import Pool
    import os
    import time
    import random

    def child_process_task(name):
    print "Running task %s, pid %s" % (name, os.getpid())
    start_time = time.time()
    time.sleep(random.random() * 3)
    end_time = time.time()
    print "Task %s is over and runs %0.2f seconds." % (name, (end_time - start_time))

    if __name__ == '__main__':
    print "Parrent process pid %s" % os.getpid()
    #创建进场池。限制并发数量:child_pool = Pool(processes=4)
    child_pool = Pool()
    for eachpid in range(5):
    #批量创建子进场(指定执行函数及其参数)
    child_pool.apply_async(child_process_task, args=(eachpid,))
    print "Waitting for all child process done..."
    #调用join()前必须先调用close()。close()关闭pool不在接受新请求。join()等待所有子进程结束
    child_pool.close()
    child_pool.join()
    print "All process is done."

MySQLdb

MySQLdb是第三方模块,用于连接及操作MySQL。
 一般先使用MySQLdb.connect()方法创建连接对象,该连接对象支持事务操作,即允许commit()rollback()。然后使用cursor()方法获取游标对象,该对象做两类操作——执行sql获取查询结果。最后操作完数据库后需要调用close()方法关闭相关对象。

执行sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import MySQLdb

create_sql = """CREATE TABLE STUDENT (
NAME CHAR(20) NOT NULL,
AGE INT,
GENDER CHAR(10))"""


try:
db_conn = MySQLdb.connect(host='localhost', user='root', passwd='fate', db='test', port=3306)
db_cur = db_conn.cursor()
db_cur.execute(create_sql)
except Exception, e:
raise e
finally:
db_cur.close()
db_conn.close()

 更新(update)、删除(delete)和插入(insert)等支持事务操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
insert_sql = 'INSERT INTO STUDENT(NAME, AGE, GENDER)\
VALUES ("%s", "%s", "%s")' % ('mogl', 10, 'male')


try:
db_conn = MySQLdb.connect(host='localhost', user='root', passwd='fate', db='test', port=3306)
db_cur = db_conn.cursor()
db_cur.execute(insert_sql)
db_conn.commit()
except Exception, e:
db_conn.rollback()
raise e
finally:
db_cur.close()
db_conn.close()

获取查询结果

 使用fetchall()返回select查询的所有结果,默认每行结果以元组形式返回,可使用cursorclass=MySQLdb.cursors.DictCursor返回字典形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select_sql = "select * from STUDENT"

try:
db_conn = MySQLdb.connect(host='localhost', user='root', passwd='fate', db='test', port=3306)
db_cur = db_conn.cursor(cursorclass=MySQLdb.cursors.DictCursor)
db_cur.execute(select_sql)
select_result = db_cur.fetchall()
except Exception, e:
raise e
finally:
db_cur.close()
db_conn.close()

for row in select_result:
print row['NAME'], row['AGE'], row['GENDER']

ConfigParser

ConfigParser模块用来解析类似ini格式的配置文件。
 ini格式配置文件大致如下:

  • section[test1][test2]
  • optionsection下所有的键
  • itemssection下所有的键值对
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #test.cfg

    [test1]
    name = mogl
    passwd = 1234

    [test2]
    name = moguoliang
    passwd = 4321
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import ConfigParser

config_file = 'test.cfg'
parser = ConfigParser.SafeConfigParser()
#parser.read(config_file)
with open(config_file, 'r') as cfg:
parser.readfp(cfg)

#读操作
print parser.sections()
#['test1', 'test2', 'test3']
print parser.options('test1')
#['name', 'passwd']
print parser.items('test1')
#[('name', 'mogl'), ('passwd', '1234')]
for key, value in parser.items('test1'):
print key, value
print parser.get('test1', 'name')
#mogl

#写操作
parser.set('test2', 'passwd', '1234')
if 'test3' not in parser.sections():
parser.add_section('test3')
parser.set('test3', 'name', 'mogltest')
with open(config_file, 'w') as cfg:
parser.write(cfg)

lxml

lxml用于解析xml或html,用法比较复杂,这里只记录自己用到的简单功能。

  • cssselect()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    from lxml import html

    #实例一
    page = '<img class="BDE_Image" src="http://test.com/test.jpg" width="560" height="323">'
    doc = html.fromstring(page)
    for index, content in enumerate(doc.cssselect('img.BDE_Image')):
    img_src = content.attrib['src']
    print index, img_src
    #0 http://test.com/test.jpg

    #实例二

    page = """
    <div class="buyer-name">mogl</div>
    <span class="item-price" src='http://test.com/test.html'>$2.9</span>
    <span class="item-price" src='http://test.com/test2.html'>$3.0</span>
    """


    htmlDoc = html.fromstring(page)

    buyers = htmlDoc.cssselect('div.buyer-name')
    prices = htmlDoc.cssselect('span.item-price')

    for eachBuyer in htmlDoc.cssselect('div.buyer-name'):
    print "buyer:", eachBuyer.text_content()

    for eachPrice in htmlDoc.cssselect('span.item-price'):
    print eachPrice.text_content()
    print eachPrice.attrib['src']

    #buyer: mogl
    #$2.9
    #http://test.com/test.html
    #$3.0
    #http://test.com/test2.html
  • xpath
     先简单记录xpath的路径表达式:

表达式 实例 意义
/ xpath(‘/div’) 从根节点选取div节点
// xpath(‘//div’) 选取所有div节点
. xpath(‘./div’) 选取当前节点下的div节点
.. xpath(‘..’) 选取父节点
@ xpath(‘//@class’) 选取属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from lxml import html

page = """
<div title="buyer-name">mogl</div>
<span class="item-price" src='http://test.com/test.html'>$2.9</span>
<span class="item-price" src='http://test2.com/test2.html'>$2.7</span>
"""

htmlDoc = html.fromstring(page)

buyers = htmlDoc.xpath('//div[@title="buyer-name"]/text()')
prices = htmlDoc.xpath('//span[@class="item-price"]/text()')

print "buyers:", buyers
print "prices:", prices
#buyers: ['mogl']
#prices: ['$2.9', '$2.7']

prices2 = htmlDoc.xpath('//span[@class="item-price"]/@src')
print prices2
#['http://test.com/test.html', 'http://test2.com/test2.html']

BeautifulSoup

BeautifulSoup模块和lxml类似都是用来解析HTML/XML的。该模块是是第三方模块需要安装。

1
$> pip install beautifulsoup4

 为了方便记录,就以以下的HTML代码为解析例子

1
2
3
4
5
html = """
<html><head><title>The BeautifulSoup test</title></head>
<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>
<img src=http://www.mogl.com/img/test.png width=270 height=129>
"""

 默认Python会使用内置的解析器进行解析,但速度较慢,这里使用lxml来进行解析(需要先装lxml)。

1
2
3
4
5
6
7
from bs4 import BeautifulSoup
import re

#创建BeautifulSoup对象
soup = BeautifulSoup(html, 'lxml')
#格式化输出
print soup.prettify('utf-8')

Tag——标签

Tag是HTML中的标签,诸如titlea等。BeautifulSoup对Tag的处理主要包含三种:

  • name
  • string
  • attrs

 获取整个Tag

1
2
3
4
5
6
7
8
print soup.title
print soup.head
print soup.a
print soup.img
#<title>The BeautifulSoup test</title>
#<head><title>The BeautifulSoup test</title></head>
#<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>
#<img height="129" src="http://www.mogl.com/img/test.png" width="270"/>

name是获取Tag的类型

1
2
3
4
print soup.title.name
print soup.a.name
#title
#a

string是获取Tag标签中的文本内容

1
2
3
4
print soup.title.string
print soup.a.string
#The BeautifulSoup test
#新闻

attrs是获取Tag标签中的属性

1
2
3
4
5
6
print soup.a.attrs
#{'href': 'http://news.mogl.com', 'class': ['mnav'], 'name': 'news'}
print soup.a['href']
#http://www.mogl.com/img/test.png
print soup.img.get('src')
#http://www.mogl.com/img/test.png

find_all——搜索

find_all()是搜索当前tag的所有节点,返回符合条件tag对象list
find_all()可对tag.nametag.attrs进行搜索,还支持对使用re的正则匹配。

1
2
3
4
5
6
7
8
print soup.find_all('a')
#[<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>]

print soup.find_all(['a', 'img'])
#[<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>, <img height="129" src="http://www.mogl.com/img/test.png" width="270"/>]

print soup.find_all(re.compile(r'^a|^i'))
#[<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>, <img height="129" src="http://www.mogl.com/img/test.png" width="270"/>]

 对tag.attrs进行搜索。当对HTML的class属性搜索时,为避免与Python内置的class关键字冲突,使用class_

1
2
3
4
5
6
7
8
9
print soup.find_all('a', class_="mnav")
#[<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>]

print soup.find_all(href=re.compile('mogl.com'), class_="mnav")
#[<a class="mnav" href="http://news.mogl.com" name="news">新闻</a>]

for num, each_tag in enumerate(soup.find_all('img')):
print num, each_tag['width']
#0 270

Powered: Hexo, Theme: Nadya remastered from NadyMain