【Python学习】常用模块

(一)、 datetime-日期和时间模块

Python处理日期和时间的标准库
获取当前日期和时间,获取指定日期和时间:

from datetime import datetime

# 获取当前日期和时间
now_date = datetime.now()
print(now_date)
print(type(now_date))

# 获取指定日期和时间
d_time = datetime(2020,3,6,12,18)
print(d_time)
2020-03-06 14:17:14.286800
<class 'datetime.datetime'>
2020-03-06 12:18:00
1、 datetime\timestamp互转

在计算机中,时间实际上是用数字表示的。我们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为 0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。Python的timestamp是一个浮点数。如果有小数位,小数位表示毫秒数。

d_time = datetime(2020,3,6,12,18)
print(d_time.timestamp())
'''
1583468280.0
'''

t = 1583462280.0
print(datetime.fromtimestamp(t))
'''
2020-03-06 10:38:00
'''

2、str/datetime互转
# str-datetime
obj_time = datetime.strptime('2020-09-11 14:36:01', '%Y-%m-%d %H:%M:%S')
print(obj_time)
print(type(obj_time))

now_time = datetime.now()
str_time = now_time.strftime('%Y-%m-%d %H:%M:%S')
print(str_time)
'''
2020-09-11 14:36:01
<class 'datetime.datetime'>
2020-03-06 14:48:42
'''
3、 datetime加减

对日期和时间进行加减实际上就是把datetime后或往前计算,得到新的datetime。加减可以直接用+和-运算符。

from datetime import datetime, timedelta
now_a = datetime.now()
print(now_a)
print(now_a+timedelta(days=1))
print(now_a+timedelta(days=1,hours=10,minutes=20))
2020-03-09 09:31:52.615500
2020-03-10 09:31:52.615500
2020-03-10 19:51:52.615500
4、 本地时间转换为UTC时间

本地时间是指系统设定时区的时间,例如北京时间是UTC+8:00时区的时间,而UTC时间指UTC+0:00时区的时间。
一个datetime类型有一个时区属性tzinfo,但是默认为None,所以无法区分这个datetime到底是哪个时区,除非强行给datetime设置一个时区。·

from datetime import datetime, timedelta, timezone
t_utc8 = timezone(timedelta(hours=8))  # 时区UTC+8:00
now_b = datetime.now()
print(now_b)
dt = now_b.replace(tzinfo=t_utc8)
print(dt)

2020-03-09 09:31:52.615500
2020-03-09 09:31:52.615500+08:00
5、 时区转换

先通过utcnow()拿到当前的UTC时间,再转换为任意时区的时间:

from datetime import datetime, timedelta, timezone
# 获取UTC时间,强制设置时区为UTC+0:00
utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
print(utc_dt)
# 转换时区为北京时间
bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
print(bj_dt)
# 转换时区为东京时间
dj_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
print(dj_dt)
2020-03-09 01:47:44.125500+00:00
2020-03-09 09:47:44.125500+08:00
2020-03-09 10:47:44.125500+09:00
from datetime import datetime, timedelta, timezone
import re


# 获取了用户输入的日期和时间如2020-3-18 9:01:30,以及一个时区信息如UTC+5:00,均是str,请编写一个函数将其转换为timestamp
def to_timestamp(dt_str, tz_str):
    # 获取时间
    r_time = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')
    # 获取时区
    utc = re.match(r'^UTC([\+\-])(\d{1,2})\:00$', tz_str)
    if utc[1] == '+':
        t_utc = int(utc[2])
    else:
        t_utc = -int(utc[2])

    # 强制设置时区
    utc_n = r_time.replace(tzinfo=timezone(timedelta(hours=t_utc))).astimezone(timezone.utc)

    # 返回时间戳
    return utc_n.timestamp()


print(to_timestamp('2020-3-18 9:01:30', 'UTC+5:00'))


(二)、collections-集合模块

collections是Python内建的一个集合模块,提供了许多有用的集合类。

1、namedtuple

用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。

from collections import namedtuple

Pit = namedtuple('Pit',['x','y','z'])
p = Pit(1,2,3)
print(p)
print(p.x,p.y,p.z)
2、deque

使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

from collections import deque

q = deque(['a', 'b', 'c'])
q.append('d')
q.appendleft('x')
print(q)
3、defaultdict

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

from collections import  defaultdict

dd = defaultdict(lambda :'N/A')
dd['key1'] = 'abc'
print(dd['key1'])
print(dd['key2'])
4、Counter

Counter是一个简单的计数器,统计字符出现的个数:

from collections import Counter

c = Counter('dasdasdf asda')
print(c)

c = Counter()
c.update('asffwerr fdsfs')
print(c)
Counter({'d': 4, 'a': 4, 's': 3, 'f': 1, ' ': 1})
Counter({'f': 4, 's': 3, 'r': 2, 'a': 1, 'w': 1, 'e': 1, ' ': 1, 'd': 1})

(三)、base64

Python内置的base64可以直接进行base64的编解码,Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据

import base64

# 编码
s = base64.b64encode(b'binary\x00string')
print(s)
# 解码
b = base64.b64decode(b'YmluYXJ5AHN0cmluZw==')
print(b)

标准的Base64编码后可能出现字符+/,在URL中就不能直接作为参数,所以又有一种"url safe"的base64编码,其实就是把字符+/分别变成-_:

d = base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
print(d)
f = base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
print(f)
rf =  base64.urlsafe_b64decode('abcd--__')
print(rf)

Base64是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。
Base64适用于小段内容的编码,比如数字证书签名、Cookie的内容等。
由于=字符也可能出现在Base64编码中,但=用在URLCookie里面会造成歧义,所以,很多Base64编码后会把=去掉,Base64编码的长度永远是4的倍数,因此,需要加上=Base64字符串的长度变为4的倍数,就可以正常解码

(四)、struct

Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。structpack函数把任意数据类型变成bytes

import struct

i = struct.pack('>I',10240099)
print(i)
'''
b'\x00\x9c@c'
'''

>I的意思是:>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。后面的参数个数要和处理指令一致。

unpackbytes变成相应的数据类型:

import struct

s = struct.unpack('>IH',b'\xf0\xf0\xf0\xf0\x80\x80')
print(s)

'''
(4042322160, 32896)
'''

I4字节无符号整数 H2字节无符号整数

(五)、hashlib

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

摘要算法MD5:

import hashlib

md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())

'''
d26a53750bc40b38b65a520292f69306
'''

摘要算法是SHA1:

import hashlib

sha1 = hashlib.sha1()
sha1.update('hhhhhhsdadw'.encode('utf-8'))
sha1.update('dawrqr qrqr?'.encode('utf-8'))
print(sha1.hexdigest())

'''
f14475f43f4cb8d5f86b1f0ce89d9ec7369ab600
'''

练习1:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import hashlib

db = {
    'michael': 'e10adc3949ba59abbe56e057f20f883e',
    'bob': '878ef96e86145580c38c87f0410ad153',
    'alice': '99b1c2188db85afee403b1536010c2c9'
}

def login(user, password):
    return db[user] == hashlib.md5(password.encode('utf-8')).hexdigest()

# 测试
assert login('michael', '123456')
assert login('bob', 'abc999')
assert login('alice', 'alice2008')
assert not login('michael', '1234567')
assert not login('bob', '123456')
assert not login('alice', 'Alice2008')
print('作业一ok')

练习2:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import hashlib, random


def get_md5(pwd):
    return hashlib.md5(pwd.encode('utf-8')).hexdigest()


class User():
    def __init__(self, uname, pwd):
        self.uname = uname
        self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)])
        self.pwd = get_md5(pwd + self.salt)


db = {
    'michael': User('michael', '123456'),
    'bob': User('bob', 'abc999'),
    'alice': User('alice', 'alice2008')
}


def login(uname, pwd):
    user = db[uname]
    return user.pwd == get_md5(pwd + user.salt)


# 测试:
assert login('michael', '123456')
assert login('bob', 'abc999')
assert login('alice', 'alice2008')
assert not login('michael', '1234567')
assert not login('bob', '123456')
assert not login('alice', 'Alice2008')
print('ok')

(六)、hmac

Hmac算法:Keyed-Hashing for Message Authentication。它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。Python自带的hmac模块实现了标准的Hmac算法。

import hmac

message = b'Hello,Python!'
key = b'secret'
h = hmac.new(key, message, digestmod='MD5')
print(h.hexdigest())
'''
e18f0514cec1694534724ea2eff6d401
'''

使用hmac和普通hash算法非常类似。hmac输出的长度和原始哈希算法的长度一致。需要注意传入的keymessage都是bytes类型,str类型需要首先编码为bytes

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import hmac, random


def hmac_md5(key, s):
    return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'MD5').hexdigest()


class User():
    def __init__(self, uname, pwd):
        self.uname = uname
        self.key = ''.join([chr(random.randint(48, 122)) for i in range(20)])
        self.pwd = hmac_md5(self.key, pwd)


db = {
    'michael': User('michael', '123456'),
    'bob': User('bob', 'abc999'),
    'alice': User('alice', 'alice2008')
}


def login(uname, pwd):
    user = db[uname]
    return user.pwd == hmac_md5(user.key, pwd)


# 测试:
assert login('michael', '123456')
assert login('bob', 'abc999')
assert login('alice', 'alice2008')
assert not login('michael', '1234567')
assert not login('bob', '123456')
assert not login('alice', 'Alice2008')
print('ok')

(七)、itertools

模块itertools提供了非常有用的用于操作迭代对象的函数
count()会创建一个无限的迭代器
repeat()负责把一个元素无限重复下去,第二个参数可以限定重复次数
takewhile()等函数根据条件判断来截取出一个有限的序列

import itertools

# count()会创建一个无限的迭代器
'''
ns = itertools.count(1)
for n in ns:
    print(n)
'''

# cycle()会把传入的一个序列无限重复下去
'''
cs = itertools.cycle('ABC')
for c in cs:
    print(c)
'''

# repeat()负责把一个元素无限重复下去,不提供第二个参数就可以限定重复次数
rs = itertools.repeat('A', 4)
for r in rs:
    print(r)

# takewhile()函数根据条件判断来截取出一个有限的序列
ns = itertools.count(1)
n = itertools.takewhile(lambda x: x <= 10, ns)
print(list(n))

import itertools

# count()会创建一个无限的迭代器
'''
ns = itertools.count(1)
for n in ns:
    print(n)
'''

# cycle()会把传入的一个序列无限重复下去
'''
cs = itertools.cycle('ABC')
for c in cs:
    print(c)
'''

# repeat()负责把一个元素无限重复下去,不提供第二个参数就可以限定重复次数
rs = itertools.repeat('A', 4)
for r in rs:
    print(r)
'''
A
A
A
A
'''

# takewhile()函数根据条件判断来截取出一个有限的序列
ns = itertools.count(1)
n = itertools.takewhile(lambda x: x <= 10, ns)
print(list(n))
'''
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
'''

# chain()可以把一组迭代对象串联起来,形成一个更大的迭代器
for c in itertools.chain('abc', 'xyz'):
    print(c)
'''
a
b
c
x
y
z
'''

# groupby()把迭代器中相邻的重复元素挑出来放在一起
for key, group in itertools.groupby('aaabbbcccaaassAaBbb',lambda c: c.upper()):
    print(key, list(group))
'''
A ['a', 'a', 'a']
B ['b', 'b', 'b']
C ['c', 'c', 'c']
A ['a', 'a', 'a']
S ['s', 's']
A ['A', 'a']
B ['B', 'b', 'b']
'''

练习:

# -*- coding: utf-8 -*-
import itertools
from functools import reduce


def pi(N):
    '计算pi值'
    # 1、创建奇数序列
    odd = itertools.count(1, 2)

    # 2、取前N项
    lodd = itertools.takewhile(lambda x: x <= 2 * N - 1, odd)

    # 3、添加正负号 并用4除
    nums = itertools.cycle((4, -4))
    s = [nums.__next__() / i for i in lodd]

    # 4、求和
    sum = reduce(lambda x, y: x + y, s)

    return sum


# 测试:
print(pi(10))
print(pi(100))
print(pi(1000))
print(pi(10000))
assert 3.04 < pi(10) < 3.05
assert 3.13 < pi(100) < 3.14
assert 3.140 < pi(1000) < 3.141
assert 3.1414 < pi(10000) < 3.1415
print('ok')

itertools模块提供的全部是处理迭代功能的函数,它们的返回值不是list,而是Iterator,只有用for循环迭代的时候才真正计算

(八)、contextlib

Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭。@contextmanager这个decorator接受一个generator,用yield语句把with ... as var把变量输出出去,然后with语句就可以正常地工作。@contextmanager让我们通过编写generator来简化上下文管理。

from contextlib import contextmanager


class Query():
    def __init__(self, name):
        self.name = name

    def query(self):
        print('Query info about %s...' % self.name)


@contextmanager
def create_query(name):
    print('开始')
    q = Query(name)
    yield q
    print('结束')


with create_query('bob') as q:
    q.query()

'''
开始
Query info about bob...
结束
'''

在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现

@contextmanager
def tag(name):
    print('<%s>' % name)
    yield
    print('</%s>' % name)


with tag('h1'):
    print('hello')
    print('python')
'''
<h1>
hello
python
</h1>   
'''

执行顺序是:

  • with语句首先执行yield前的语句,因此打印出<h1>
  • yield调用会执行with语句内部的所有语句,因此打印出hello和world;
  • 最后执行yield之后的语句,打印出</h1>

如果一个对象没有实现上下文,就不能把它用于with语句。可以用closing()来把该对象变为上下文对象。例如,用with语句使用urlopen()

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('https://www.python.org')) as page:
    for line in page:
        print(line)

closing是一个经过@contextmanager装饰的generator,这个generator编写起来其实非常简单:

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

(九)、urllib

urllib提供了一系列用于操作URL的功能

  • get
    urllibrequest模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应:
    例如,对知乎的一个http://news-at.zhihu.com/api/4/news/latest进行抓取,并返回响应
from urllib import request

with request.urlopen('http://news-at.zhihu.com/api/4/news/latest') as f:
    datas = f.read()
    print('状态:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s:%s' % (k, v))
    print('数据:', datas.decode('utf-8'))

状态: 200 OK
Server:Tengine
Content-Type:application/json; charset=UTF-8
Content-Length:2682
Connection:close
Date:Tue, 10 Mar 2020 07:12:49 GMT
Set-Cookie:tgw_l7_route=116a747939468d99065d12a386ab1c5f; Expires=Tue, 10-Mar-2020 07:27:49 GMT; Path=/
Vary:Accept-Encoding
Etag:"1a79ee3d3f912b41a4e4b189da6c2e88edd0df1a"
X-Backend:zhihu-daily-web--1-e2c01739-645cb8d756-kpkh5
X-Backend-Response:0.014
X-SecNG-Response:0.026000022888184
Set-Cookie:_xsrf=BmhAxiCPgLLtxu8ltktSmOfGN9p8OsMX; path=/; domain=zhihu.com; expires=Sat, 27-Aug-22 07:12:49 GMT
x-lb-timing:0.025
x-idc-id:1
Via:cache33.l2cn1828[280,0], vcache11.cn2450[285,0]
Timing-Allow-Origin:*
EagleId:75bb159f15838243689427639e
数据: {"date":"20200310","stories":[{"image_hue":"0x4d3b36","title":"贪腐、失控、倒逼工作室,顶流团队真能管好粉圈吗?","url":"https:\/\/daily.zhihu.com\/story\/9721372","hint":"娱理工作室 · 3 分钟阅读","ga_prefix":"031011","images":["https:\/\/pic2.zhimg.com\/v2-5c7068cccefbbfdd00b27d2667f26e95.jpg"],"type":0,"id":9721372},{"image_hue":"0x5c928e","title":"如果你是天美的运营官,你会如何优化《王者荣耀》?","url":"https:\/\/daily.zhihu.com\/story\/9721362","hint":"Joker · 4 分钟阅读","ga_prefix":"031009","images":["https:\/\/pic4.zhimg.com\/v2-d49cfd2d89adef0cc587f6602c2f11b3.jpg"],"type":0,"id":9721362},....}]}

模拟浏览器发送GET请求,就需要使用Request对象,通过往Request对象添加HTTP头,我们就可以把请求伪装成浏览器。例如,模拟iPhone 6去请求baidu首页

from urllib import request

req = request.Request('https://www.baidu.com/')
req.add_header('User-Agent',
               'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:
    print('Status:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s:%s' % (k, v))
    print('Data:', f.read().decode('utf-8'))

  • Post
    如果要以POST发送一个请求,只需要把参数databytes形式传入。
    我们模拟一个微博登录,先读取登录的邮箱和口令,然后按照weibo.cn的登录页的格式以username=xxx&password=xxx的编码传入:
from urllib import request, parse

print('Login to weibo.cn...')
email = input('Email: ')
passwd = input('Password: ')
login_data = parse.urlencode([
    ('username', email),
    ('password', passwd),
    ('entry', 'mweibo'),
    ('client_id', ''),
    ('savestate', '1'),
    ('ec', ''),
    ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')
])

req = request.Request('https://passport.weibo.cn/sso/login')
req.add_header('Origin', 'https://passport.weibo.cn')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')

with request.urlopen(req, data=login_data.encode('utf-8')) as f:
    print('Status:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', f.read().decode('utf-8'))

登录成功:

Status: 200 OK
Server: nginx/1.2.0
...
Set-Cookie: SSOLoginState=1432620126; path=/; domain=weibo.cn
...
Data: {"retcode":20000000,"msg":"","data":{...,"uid":"1658384301"}}

登录失败:

...
Data: {"retcode":50011015,"msg":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef","data":{"username":"example@python.org","errline":536}}

(十)、HTMLParser

from html.parser import HTMLParser
# from html.entities import name2codepoint
from urllib import request
import re

class MyHTMLParser(HTMLParser):
    def __init__(self):
        super(MyHTMLParser,self).__init__()
        self.__parsedata='' # 设置一个空的标志位

    def handle_starttag(self, tag, attrs):
        if ('class', 'event-title') in attrs:
            self.__parsedata = 'name'  # 通过属性判断如果该标签是我们要找的标签,设置标志位
        if tag == 'time':
            self.__parsedata = 'time'
        if ('class', 'say-no-more') in attrs:
            self.__parsedata = 'year'
        if ('class', 'event-location') in attrs:
            self.__parsedata = 'location'

    def handle_endtag(self, tag):
        self.__parsedata = ''# 在HTML 标签结束时,把标志位清空

    def handle_data(self, data):
        if self.__parsedata == 'name': 
            print('会议名称:%s' % data) # 通过标志位判断,输出打印标签内容

        if self.__parsedata == 'time':
            print('会议时间:%s' % data)

        if self.__parsedata == 'year':
            if re.match(r'\s\d{4}', data): # 因为后面还有两组 say-no-more 后面的data却不是年份信息,所以用正则检测一下
                print('会议年份:%s' % data.strip())

        if self.__parsedata == 'location':
            print('会议地点:%s' % data)
            print('----------------------------------')

parser = MyHTMLParser()

URL = 'https://www.python.org/events/python-events/'

with request.urlopen(URL, timeout=15) as f:  # 打开网页并取到数据
    data = f.read()
parser.feed(data.decode('utf-8'))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值