正则表达式

正则表达式是匹配字符串规则的一种规则正则:通用的,处理字符串,给我们提供一些规则,让我们从杂乱无章的文字中提取有效信息

字符组:[字符组]

在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
表示数字的字符组:
[13456782]
[0123456789]
[0-9]
[2-8]
简写模式必须由小到大

表示字母的字符组
[abcd]
[a-z]
[A-Z]
字符:
元字符 
匹配内容
. 除换行符外的任意字符
\w 字母或数字或下划线
\s 任意空白符
\d 数字
\n 换行符
\t 制表符
\b 一个单词的结尾
^ 字符串的开始
$ 字符串的结尾
\W 非字母或数字或下划线
\D 非数字
\S 非空白符
a|b 字符a或字符b
() 匹配括号内的表达式,也表示一个组
[...] 字符组中的字符
[^...] 除了字符组中字符的所有字符






















量词
量词 用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次











.^$

正则 待匹配字符 匹配结果 说明
海. 海燕海娇海东 海燕海娇海东 匹配所有"海."的字符
^海. 海燕海娇海东 海燕 只从开头匹配"海."
海.$ 海燕海娇海东 海东 只匹配结尾的"海."





 

 

 

 

* + ? { }

待匹配字符:李杰和李莲英和李二棍子

正则 匹配
结果
说明
李.?

李杰
李莲
李二

 
?表示重复零次或一次,即只匹配"李"后面一个任意字符
 
李.* 李杰和李莲英和李二棍子
*表示重复零次或多次,即匹配"李"后面0或多个任意字符
李.+ 李杰和李莲英和李二棍子
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符
李.{1,2}

李杰和
李莲英
李二棍

{1,2}匹配1到2次任意字符
李[^和]{1,3}

李杰

李莲英

李二棍子

[^和]{1,3}匹配除和外1到3次任意字符

 

 

 

 

 

 

 

 

 

 

 

 

 

 

前面的*,+,?等都是贪婪匹配

后面加?号使其变成惰性匹配

 待匹配字符:李杰和李莲英和李二棍子

正则 匹配
结果
说明
李.*?

惰性匹配

 

 

 

 

 

字符集[][^]

正则 待匹配字符 匹配
结果
说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

 
表示匹配"李"字后面[杰莲英二棍子]的字符任意次
 
李[^和]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

表示匹配一个不是"和"的字符任意次
[\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分组 ()与 或 |[^]

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部由数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

   表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}

 

 

 

 

 

  

 

 

 

 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。

正则 待匹配字符 匹配
结果
说明
\d \d  False
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配
\\d \d  True
转义\之后变成\\,即可匹配
"\\\\d" '\\d'  True
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\d' r'\d'  True
在字符串之前加r,让整个字符串不转义

 

 

 

 

 

 

 

 

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script>
默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r'\d'  

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串












几个常用的非贪婪匹配
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x
就是取前面任意长度的字符,直到一个x出现


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()  # 同search,不过尽在字符串开始处进行匹配
print(ret)
#结果 : '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)

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

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])  #查看剩余的左右结果

 findall的优先级查询

import re
ret = re.findall(r'www\.baidu\.com|www\.oldboy\.com',r'www.baidu.com')
print(ret)  #['www.baidu.com']
ret = re.findall(r'www\.(baidu|oldboy)\.com',r'www.baidu.com') #findall取组内 
print(ret)  #['baidu']
ret
= re.findall(r'www\.(?:baidu|oldboy)\.com',r'www.baidu.com') #findall取足所有匹配的
print(ret)  #
['www.baidu.com']
#分组优先:优先显示括号内部的内容 
#
取消分组优先

search的优先级查询

ret = re.search(r'www\.(?P<web_name>baidu|oldboy)\.com',r'www.baidu.com').group('web_name')  #search取组内
print(ret)  #baidu
ret = re.search(r'www\.(?P<web_name>baidu|oldboy)\.com',r'www.baidu.com').group()    #search取全组
print(ret)  #www.baidu.com

split的优先级查询

ret=re.split("\d+","eva3egon4yuan")
print(ret) #['eva', 'egon', 'yuan']

ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #['eva', '3', 'egon', '4', 'yuan']
#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。
匹配标签
ret = re.search("<\w+>\w+</\w+>","<h1>hello</h1>")
print(ret.group())  #<h1>hello</h1>
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
print(ret.group('tag_name'))  #h1
print(ret.group())  #<h1>hello</h1>
#分组的命名和组的引用

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
print(ret.group(1)) #['h1']
# 如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# 获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group())  #<h1>hello</h1>
ret = re.findall(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
print(ret)      #['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、 匹配一段文本中的每行的邮箱
      http://blog.csdn.net/make164492212/article/details/51656638
2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;
   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$
3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}
4、 匹配一个浮点数。       ^(-?\d+)(\.\d+)?$   或者  -?\d+\.?\d*
5、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$ 
6、 匹配出所有整数        -?\d+\.\d*|(-?\d+)    remove ""

爬虫

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

print(r'\n') #表示取消字符串内所有转译符的转译作用 real
print('\n') #'\'转译符,n,加上转译符 \n --> 换行了
print('\\n') #'\'转译符,n,加上转译符 \n --> 换行了

结论:在正则表达式工具里什么样,挪到python里加个r

collections模块
from collections import Iterator  #迭代器
from collections import Iterable #可迭代对象

collections模块提供了几个数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

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

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

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

4.OrderedDict: 有序字典

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

 

1.namedtuple可命名元组

from collections import namedtuple
point1 = (1,1)
x = point1[0]
y = point1[1]
P = namedtuple('Point',['x','y'])
p1 = P(1,2)
p2 = P(3,4)
print(p1.x)     #1
print(p1.y)     #2
print(p2.x)     #3
print(p2.y)     #4
#生日,年月日
P = namedtuple('birthday',['y','m','d']) birth = P(2017,11,14) print(
birth.y,birth.m,birth.d)  #2017 11 14
#圆,圆心坐标和半径
#
namedtuple('名称', [属性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])

描述一类东西的时候,这一类东西都有相同的特征。

想直接用特征的名字就描述这个值的时候,就可以用可命名元祖。

deque

队列queue
import queue   #队列_多线程多进程
q = queue.Queue()
q.put([1])
q.put(2)
q.put(300)
q.put('aaa')
print(q.qsize())    #5
print(q.get())    #[1]
print(q.get())    #2
print(q.get())    #300
print(q.get())    #aaa
print(q.qsize())    #0
print(q.get())   #hold住的功能,
print(q.get_nowait())  #如果没有不会hold住,且会报错

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

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

from collections import deque
dq = deque()
dq.append('a')  #末尾添加
dq.append('b')
dq.appendleft('c')  #左侧添加
print(dq.popleft())  #左侧删除c
print(dq)  #deque(['a','b'])

OrderedDict

 

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。

 

如果要保持Key的顺序,可以用OrderedDict

from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
for k in od:
    print(k,od[k])
>>> 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']

defaultdict

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

即: {'k1': 大于66 'k2': 小于66}
l = [11,22,33,44,55,66,77,88,99,90]
dic = {}
for i in l:
    if i > 66:
        if 'k1' in dic:
            dic['k1'].append(i)
        else:
            dic['k1'] = []
            dic['k1'].append(i)
    elif i < 66:
        if 'k2' in dic:
            dic['k2'].append(i)
        else:
            dic['k2'] = []
            dic['k2'].append(i)

print(dic)
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)
print(my_dict)

使dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict

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

Counter

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。

c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

 时间模块

import time

time.time()当前时间戳

time.sleep()延迟指定时间

表示时间的三种方法:

1,时间戳timestamp

从1970年1月1日00:00:00开始到现在的所有秒,类型是float

import time

print(time.time())
#1510655823.2724009 时间戳

2,时间字符串Format String
'2017-11-14'
python中时间日期格式化符号:
%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 当前时区的名称 %% %号本身

3,元组struct_time

共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)


import time

#时间戳
print(time.time())
1510655823.2724009
#时间字符串 
print(time.strftime("%Y-%m-%d %X"))
'2017-11-14 17:00:00'
print(time.strftime("%Y-%m-%d %a %H:%M:%S"))
'2017-11-14 Tue 17:00:00'
#时间元组:localtime将一个时间戳转换为当前时区的struct_time
print(time.localtime()) time.struct_time(tm_year=2017, tm_mon=11, tm_mday=14,           
                    tm_hour
=17, tm_min=0, tm_sec=0,
                   tm_wday
=1, tm_yday=318, tm_isdst=0)

时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的

几种时间格式之间的转化
#时间戳<-->结构化时间
time.gmtime(时间戳)    #英国伦敦当地时间
time.localtime(时间戳) #当地时间,gmtime + 8小时 = localtime

时间戳-->结构化时间
ret1 = time.localtime(3000000000)
ret2 = time.gmtime(3000000000)
print(ret1)
print(ret2)
#time.struct_time(tm_year=2065, tm_mon=1, tm_mday=24, tm_hour=13, tm_min=20, tm_sec=0, tm_wday=5, tm_yday=24, tm_isdst=0)
#time.struct_time(tm_year=2065, tm_mon=1, tm_mday=24, tm_hour=5, tm_min=20, tm_sec=0, tm_wday=5, tm_yday=24, tm_isdst=0
结构化时间-->时间戳
print(time.mktime(ret1))
#3000000000.0
结构化时间<-->字符串时间
#结构化时间-->字符串时间
#time.strftime("格式定义","结构化时间")  结构化时间参数若不传,则现实当前时间
print(time.strftime("%Y-%m-%d %X"))
'2017-07-24 14:55:36'
print(time.strftime("%Y-%m-%d",time.localtime(1500000000)))
'2017-07-14'

#字符串时间-->结构化时间
#time.strptime(时间字符串,字符串对应格式)
print(time.strptime("2017-03-16","%Y-%m-%d"))
time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)
print(time.strptime("07/24/2017","%m/%d/%Y"))
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)

结构化时间<-->%a %b %d %H:%M:%S %Y串

#结构化时间 --> %a %b %d %H:%M:%S %Y串
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
print(time.asctime(time.localtime(1500000000)))
'Fri Jul 14 10:40:00 2017'
print(time.asctime())
'Mon Jul 24 15:18:33 2017'

#%a %d %d %H:%M:%S %Y串 --> 结构化时间
#time.ctime(时间戳)  如果不传参数,直接返回当前时间的格式化串
print(time.ctime())
'Mon Jul 24 15:19:07 2017'
print(time.ctime(1500000000))
'Fri Jul 14 10:40:00 2017' 

 

计算时间差
import time
true_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2017-09-12 11:00:00','%Y-%m-%d %H:%M:%S'))
dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)
print('过去了%d年%d月%d天%d小时%d分钟%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
                                       struct_time.tm_mday-1,struct_time.tm_hour,
                                       struct_time.tm_min,struct_time.tm_sec))

sys模块

sys模块是与python解释器交互的一个接口

sys.argv 命令行参数List,第一个元素是程序本身路径

sys.exit(n) 退出程序,正常退出时exit(0),错误退出sys.exit(1)

sys.version 获取Python解释程序的版本信息

sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值

sys.platform 返回操作系统平台名称


print(sys.path)
就是模块导入的时候从这个列表中的路径依次去寻找模块,找到了就停止
sys.path的第一位元素是当前被执行的python文件所在的地址,之后的地址依次是python内部的库
检测执行某个文件的用户
print(sys.argv)>>>python 6sys.py>>>python 6sys.py alex 3714 args_lst = sys.argv #['6sys.py', 'alex', '3714'] if len(args_lst) ==3 and args_lst[1] == 'alex' and args_lst[2] == '3714': print('执行程序了') else: sys.exit()
sys.argv的第一个值是固定的的,就是这个文件的名字
之后的参数是在控制台执行py文件的时候传入的参数 python 6sys.py alex 3714
我们可以用这些参数来直接完成一些校验类的工作
结构化>>>时间戳mktime()
时间戳>>>结构化gmtime(),localtime()
结构化>>>字符串strftime()
字符串>>>结构化strptime()
结构化>>>%格式化字符串asctime()
%格式化字符串>>>结构化ctime()

 random模块

import random随机取值

print(random.random())  #0,1之间的小数
print(random.uniform(n,m)) #n,m 之间的小数

生成随机整数

print(random.randint(1,2))  #必须是两个参数,规定一个范围 [1,2]
print(random.randrange(100))  #一个参数
print(random.randrange(1,2))  #两个参数 [1,2)
print(random.randrange(90,100,2))  #三个参数,最后一个是步长
从一个序列中随机选择:一个choice,多个sample
print(random.choice('abc'))
print(random.sample([1,'23',[4,5]],2))

打乱一个序列的顺序

item=[1,3,5,7,9]
random.shuffle(item)  #改变了原列表
print(item)

生成一个随机6位数字的验证码

l = []
for i in range(6):
    rand_num = random.randint(0,9)
    l.append(str(rand_num))
print(''.join(l))
生成一个6位数字+字母的验证码
l = []
for i in range(6):
    alpha = chr(random.randint(65, 90))  # random.randrange(65,91)
    alpha_lower = chr(random.randint(97, 122))  # random.randrange(65,91)
    num = str(random.randint(0, 9))
    ret = random.choice([alpha,num,alpha_lower])
    l.append(ret)
print(''.join(l))

os模块

import os

#和系统路径有关的
print(os.getcwd())  #获取当前工作目录,即当前python脚本工作的目录路径

os.chdir(r'C:\Users')  #改变当前脚本工作目录;相当于shell下cd
print(os.curdir)  #返回当前目录: ('.')
print(os.pardir)  #获取当前目录的父目录字符串名:('..')

#和系统文件夹和文件相关的
os.mkdir('dir1')  #生成单级递归目录
os.makedirs('dir3\\dir4')  #可生成多层递归目录
os.rmdir('dir3\\dir4')  #删除单级空目录,若目录不为空则无法删除,报错
os.removedirs('dir3')  #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.remove('文件路径')  #删除文件
os.rename(
'文件路径')  #重命名文件
os.listdir(r'C:\Users\PycharmProjects\全栈s8'))#
列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印

#和操作系统特征相关
print(os.stat(r'D:/LX/day21/3.os模块.py'))    #获取文件/目录信息
print(os.sep)  #输出操作系统特定的路径分隔符,win下为"\\"
print(os.environ)    #获取系统环境变量
#和操作系统的命令相关 —— dir cd
os.system('dir')  #没有返回值,且直接执行代码,把结果直接输出
ret = os.popen('dir')  #如果有结果就将结果返回回来
print(ret.read())   #ret.read()获取结果
#os.path
os.path.split(path) #将path分割成目录和文件名以元组返回
os.path.basename(path) #返回path最后的文件名。path以/或\结尾,那么就会返回空值。
os.path.isfile(path)  #如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  #如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getsize(path) #返回path的大小

序列化模块

什么叫序列化:将字典,列表,元组等内容转换成一个字符串的过程就叫做序列化

字典和列表不能写到文件里

把python中的数据转换成str —— 序列化
可以str转换成python的数据 —— 反序列化

json
所有的语言都通用,它能序列化的数据是有限的:字典列表元组
序列化中的内容只能包含:字典 列表 数字 字符串,如果是元组——自动转成列表的样子
包含四种方法:dumps,loads,dump,load
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"}

loads

 

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
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'}]

dump和load

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)

 

pickle

python专有的不能和其他语言兼容,结果是bytes

用pickle序列化的数据,反序列化也必须用pickle

pickle模块提供了四个功能:dumps、dump序列化,loads反序列化,load可以把python中任意的数据类型序列化

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  #一串二进制内容

dic2 = pickle.loads(str_dic)
print(dic2)    #字典

import time
struct_time  = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()

f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)

shelve

shelve 只提供一个open,shelve.open('文件名')拿到一个文件句柄,这个文件句柄就可以当做字典操作
正常情况下shelve打开的文件句柄感知不到值的修改,设置writeback = True就可以保存修改内容了
正常情况下不支持多个人同时写,支持多个人同时读,如果只是读的化,就设置flag=‘r’

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)
import shelve
f = shelve.open('shelve_file',flag='r')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操作,就可以存入数据
f.close()

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

f = shelve.open('shelve_file',flag='r')
print(f['key'])
f.close()
#正常情况下shelve打开的文件句柄感知不到值的修改,设置writeback = True就可以保存修改内容了
import shelve
f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])
f2['key']['new_value'] = {1,2,3}
f2.close()
import shelve
f = shelve.open('shelve_file',flag='r')
print(f['key'])
f.close()
json :所有语言通用,能转换的数据类型有限
pickle :只限于python,能转换所有的数据类型
shelve : 只限于python语言,能转换所有的数据类型,使用方法类似字典
*******常用模块补充(面向对象相关)******** 
一,hashlib

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

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

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

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

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

import hashlib
md5_obj = hashlib.md5()   #选择摘要算法中的md5类进行实例化,得到md5_obj
md5_obj.update(b'how to use md5 in python hashlib?') #对一个字符串进行摘要
md5_obj.update(b'alex') #对一个字符串进行摘要
print(md5_obj.hexdigest()) #找摘要算法要结果

md5_obj = hashlib.md5()   #选择摘要算法中的md5类进行实例化,得到md5_obj
md5_obj.update(b'how to use md5 in python hashlib?alex') #对一个字符串进行摘要
print(md5_obj.hexdigest()) #找摘要算法要结果

如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:


md5 = hashlib.md5()
md5.update('how to use md5 in ')
md5.update('python hashlib?')
print md5.hexdigest()
对比两个文件是否一致----文件的一致性校验
分别打开两个文件,一行一行读,没一行update一下 对比最终的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,不过越安全的算法越慢,而且摘要长度更长。

 

*****加密认证 —— 在存储密码的时候是使用密文存储的,校验密码的时候对用户的输入再做一次校验

任何允许用户登录的网站都会存储用户登录的用户名和口令。用户名和口令存到数据库表中:


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,黑客就获得了使用常用口令的用户账号。

 

import hashlib
md5_obj = hashlib.sha()
md5_obj.update(b'alex3714')
print(md5_obj.hexdigest())

import hashlib
md5_obj = hashlib.md5('*!金老板'.encode('utf-8'))
md5_obj.update(b'123456')
print(md5_obj.hexdigest())

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

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

如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

 

hashlib小结
hashilib 摘要算法的模块

md5 sha1 sha256 sha512
摘要的过程 不可逆
能做的事儿:
文件的一致性检测
用户的加密认证
单纯的mg5不够安全
加盐处理 简单的盐可能被破解 且破解之后所有的盐都失效了
动态加盐

 


二,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方法Section下的key对应的value

增删改操作


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"))
配置文件其实是多种多样的
configparser是专门解决一种样式的配置文件而生的
yaml 是另一种配置规则 python也提供了扩展模块

 

三,logging模块

日志模块

日志就是在程序的运行过程中,人为的添加一些要打印的中间信息
在程序的排错、对一些行为、结果的记录

函数式简单配置

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='%a, %d %b %Y %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用户输出的消息

 

logger对象配置

import logging

logger = logging.getLogger()# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log')# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
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')

logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过

fh.setLevel(logging.Debug)单对文件流设置某个级别。

import logging

logger = logging.getLogger()   #实例化一个logger对象
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8')  # 文件句柄-日志文件操作符
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #日志输出格式
formatter2 = logging.Formatter('%(asctime)s - %(name)s  [%(levelname)s]  %(message)s') #日志输出格式
fh.setFormatter(formatter)   #文件句柄绑定格式
logger.setLevel(logging.DEBUG)  #设置日志等级,默认是Warning
logger.addHandler(fh) #logger绑文件句柄
sh = logging.StreamHandler()  #屏幕流对象
sh.setFormatter(formatter2)
logger.addHandler(sh)
logger.info('hello!')
logging
basicConfig:
配置简单,配了就能直接
对象的模式:
可以随意的控制往那些地方输出日志
可以分别控制输出到不同位置的格式

 

 

 

 

 

 

 

 

 

 

 


 

 



 

 

 

 

 











posted on 2017-11-13 16:46  Py行僧  阅读(419)  评论(0编辑  收藏  举报