Python进阶四

常用的内置模块

datetime模块

获取当前日期和时间

注意:在datetime这个模块里有个datetime类,两者容易搞混。下面这个获取当前直接的now()方法用的是datetime模块里的datetime类。使用datetime类的最后都是datetime对象,就比如下面的now和dt都是datetime类的实例对象

from datetime import datetime    
now = datetime.now()

如果没有第一行引入这个类,那就是需要这样写
now = datetime.datetime.now()

获取指定日期和时间

还是用的datetime模块里的datetime类

from datetime import datetime    
dt = datetime(2022,9,2)

至少需要传入三个参数,年月日,可以多传入几个时间,时分秒。

datetime转换为timestamp(时间戳)

timestamp就是从10970年一月一日零点开始的到现在时间的秒数。
把一个datetime类型转换为timestamp只需要简单调用timestamp()方法。
dt.timestamp()上面的dt对象。最后打印的就是一个浮点数。

timestamp转换为datetime

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

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

这样打印的就是一个datetime类的对象了。

字符串转换datetime

转换方法是通过datetime.strptime()实现,需要一个日期和时间的格式化字符串:
字符串'%Y-%m-%d %H:%M:%S'规定了日期和时间部分的格式

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

datetime转换为str

转换方法是通过strftime()实现:

from datetime import datetime
now = datetime.now()
print(now.strftime('%a, %b %d %H:%M'))
>>>Fri, Sep 02 21:11

datetime加减

直接使用加减号就行了,不过需要导入timedelta这个类。

collections模块

集合模块,提供了许多有用的集合类。

nametuple

nametuple是一个函数,可以自定义一个数据类型,但是跟元组一样,不能更改,里面可以指定元素个数和属性,比如这个就是指定两个元素,x和y,属性就是x,y可以直接通过属性来找到对应的数字,就不需要索引就可以。

deque

跟list一样,deque除了实现list的append()和pop()外,还支持appendleft()和popleft()。就多了这俩left方法。

defaultdict

跟字典一样,但是不同的是:要是字典里没有这个键,就会报错,在defaultdict可以设置一个默认值,当没有某个键的时候返回这个默认值。

from collections import defaultdict
d = defaultdict(lambda:"没有这个键")
d[k1] = 1
print(d[k1])
print(d[k2])

Orderdict

一般创建一个字典是无序的,但用Orderict创建的字典会按着键自动排序。

Count(这个感觉比较有用)

Count是一个计数器,可以通过这个可以查看字符串里每个字符出现的次数,返回的是一个字典。

base4

base64是用64个字符来对二进制数据表示的一种方法,规定的有一个64个字符的数组。对二进制数据进行处理:首先将这个二进制的数据的每三个字节分为一组(二进制里由于一个字节里面是8位bit),所以这样一组里面就有24位bit;然后对这24位bit再重新分一下,划分成6位bit为一个小组,分成了4个小组;然后每一个小组去查表,替换一个字符。这样就把三个字符(大多数计算机中一个字节存储一个字符或者一个数字)的数据变成四个字符的数据了。

比较搞不懂的就是\x00这个补字节的怎么理解:首先base64编码的规则上面已经详细说明了,然后这里说一下,bit是二进制存储的最小单位,只能说明0或者1,所以base64里6个bit为一小组就是因为2的6次方刚还等于64,刚好可以base64数组里面的某一个字符。
在base64编码里面还有一层就是,每一个小组有6个bit,然后会为前面添加00,这样就构成了8位,然后刚好一个字节,但是因为前面补充的是00,不影响在base64数组里面找到对应的字符。(这里面的补充不是外面的补充的意思,只是多一层理解编码的过程)
还是搞不懂在\x00后面补充的意思,以后遇到再来补充。

练习题

补充base64

base64主要就是将图片转换成json的格式。



struct

转字节类型的模块好像,感觉现在用不到,等到以后需要再来学。

hashlib

哈希算法:它通过一个函数,将任意数据转成长度固定的数据串,通常用长度固定的十六进制的字符串表示
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest。可以用来发现原始数据是否被修改,因为摘要算法是一个单向函数,f(data)的出来的digest,从digset返回得出data这个原数据就很困难,而且对原数据做一个很细微的改动,甚至1bit的修改得出来的digest也会不同。
hashlib常见的算法MD5和SHA1的用法:

MD5的用法


如果要update的数据过长,也可以分开编码

这样发现分不分开update跟合在一起没有区别,其实就是无论你update多少次,最终还是按着hashlib的编码格式,然后由算法MD5来计算,最后合并到一起再md5.hexdigest()输出。
还有个了解的点是,MD5这个算法,生产的结果是16个字节(有128b,因为一个字节有8b,但是最后得出的结果看不出来),因为最后输出的是固定的32个16进制的字符。

SHA1的用法


跟MD5的用法完全一样,不同的就是最后输出的结果是占有(一共160个bit)20个字节的40个16进制的字符。
所以我们的密码,都是通过这样的算法得出摘要,然后存入数据库中,不会以密码的明文保存在数据库里,当我们登录输入密码的时候,就会将我们输入的密码经计算转成摘要然后去跟数据库里的摘要进行对比,最终确定你输入的是正确的密码。

还有一个补充的就是hexdigest和digest,hexdigest是返回十六进制的,digest是返回二进制的结果,就这点不同。

import hashlib as h

db = {
    'michael': 'e10adc3949ba59abbe56e057f20f883e',
    'bob': '878ef96e86145580c38c87f0410ad153',
    'alice': '99b1c2188db85afee403b1536010c2c9'
}
def login(user, password):
    MD5 = h.md5()
    MD5.update(password.encode('utf-8'))
    digest = MD5.hexdigest()
    db[user]=digest
    print("注册成功")
    calc_md5()
def calc_md5():
    password = input("请输入密码:")
    MD5 = h.md5()
    MD5.update(password.encode('utf-8'))
    digest = MD5.hexdigest()
    if digest == db[user]:
        print("登录成功")
    else :
        print("密码错误")
if __name__ == '__main__':
    user = input("请输入账号:")
    if user in db.keys():
        calc_md5()
    else :
        print("该用户未注册")
        password = input("请输入注册密码:")
        login(user,password)

还需要的一点就是怎么把db这样的文件给导出,这样就可以保存,就实现了注册信息的保存了。

hmac

就是上一节里提到的加盐,就是为了密码能更安全,加盐对MD5或者SHA1这种哈希算法都适用。hmac主要就是在自己输入的密码后面随机加一个数据key,这样就加大被破解的难度,而且还能解决当用户名不同的时候,密码相同被存储进数据库的问题,因为key是随机不知道的。
错误解释:使用hmac需要得到的密码和key都必须是bytes的,不能是字符串,所以需要转一下格式

跟hashlib里面updat用法改成new了,其他差不多都一样。

除了给password后面加个编码格式,还能直接在写的时候加个b表示是byte类型的,就可以直接用了。

itertools

用于操作迭代对象的函数,itertool提供了几个迭代器。

count()

不断给出自然数,传入的参数是起始值和步长:

import itertools
n = itertools.count(2)#2是起始值,步长默认是一
#必须创建对象之后才能调用
for i in n:
    print(i)

一直循环就会一直调用这个参数不会停止,所以最好不要这样用。

cycle()

里面传入一个序列,然后会把序列里面的元素一个一个重复打印,同样停不下来

repeat()

里面传入参数(不断重复这个参数),和控制重复次数的参数

这个可以控制输出次数

takewhile()

用来截取循环里面的片段:传入一个函数,用来作为判断条件,然后传入循环对象,也可以写一个匿名函数:

chain()

可以将传入的多个序列合并成一个序列

这里可以发现n用完就为空了,同样把循环和列表位置换一下,会输出列表,但是不会输出for循环里面的了,因为里面的n已经是空的了。

gruopby()

contextlib

首先先来了解一下with语句的用法,在前面看文件打开关闭的时候,学过直接open,然后还得close,这样得写个try-finally,但是这样写比较麻烦,很容易忘记写close。所以有介绍了另一个语句with,就不需要再写close了,直接with open() as a:就可以打开一个文件,而且会自动帮我们关闭close,这是第一次了解到with语句。这里再详细介绍一下with里面是怎么运行的。

先来看一下这段代码,with语句处理的对象(就是类的实例化对象)里面必须得有__enter__()和__exit__()这两个方法或者说函数,一个进入,一个出来,其中__enter__()方法必须返回一个值,这个值可以是类的属性参数也可以是类本身,上面这串代码就是返回的是随便一个字符串,然后重点,使用with语句后面加这个类对象,里面运行顺序是这样的,with会首先运行enter方法里面的代码,然后将获得的return赋给as后面的一个变量,上面这段代码就是赋给这个sample的变量,然后with就开始执行下面的代码就是with:冒号后面的代码,执行完之后,又会接着调用类对象里面的exit方法表示结束,所以这就是with语句的执行过程。
其中在exit方法里面有三个参数,应该就是用来处理错误的,具体等用到再来学。


这里with语句获得的是这个类的对象(跟上面的一样),但是在enter方法里面返回的还是这个对象本身,然后赋值的时候还是赋的这个对象,然后在with语句执行的代码就是类调用自身里面的quary方法。

然后就是contextmanager这个语法糖,具体用法,等需要的时候再来着重看一下


最后一句话通过迭代器没看懂,可能以后会需要和迭代器一块使用。
总结,使用@contextmanager这个语法糖,里面有yield一块使用,然后另写with,记得,yeild语句开始执行with语句里面的东西。

@closing

这个没看,需要看再来补。

urllib模块

用于对url操作:

Get

urllib模块里的request模块能够发送一个get请求,然后返回一个HTTP的响应。使用urlopen来发送请求。例子里用with语句写的,(用with应该就是为了释放内存的吧)。
之所以出现乱码,就是因为你想显示的文字并没有对应的二进制编码。

from urllib import request

#返回响应HTTP获取一个客户端类对象,需要read()方法才能显示数据。
with request.urlopen("https://www.bilibili.com/") as r:
    data = r.read()
#r对象里面还有头数据,可以通过字典获取
    for k,v in r.getheaders():
        print("%s : %s"%(k,v))
    print(data.decode("utf-8"))#是**de**code,"utf-8"加引号。    

如果需要像post那样给服务器发送请求,可以使用Request对象
注意:在使用Request传入头信息的时候,是需要俩参数的,所以要分开写。

from urllib import request

r = request.Request("https://www.bilibili.com/")
#模拟浏览器发送请求,给服务器发送自己的表头信息
r.add_header("user-agent" ,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53")
with request.urlopen(r) as r:
    data = r.read()
    for k, v in r.getheaders():
        print("%s : %s" % (k, v))
    print("*"*200)
    print(data.decode("utf-8"))

Post

上面的使用Request传入头信息就是Post的方式,这里详细介绍一下Post来获取网页信息。
要以Post方式获取网页信息,不同于get方式的就是可以自己传入信息,但是传入的信息都得是Bytes形式的,就是字节的意思。
使用Post方式可以获取一些需要登陆的网页的网页信息。比如微博登录。

上面是两种获取网页信息的方式,下面介绍一下常用获取网页信息的代码:

  1. urlopen():有三个参数
    1. url就是要获取的网址
    2. data用来指明发往服务器请求中的额外的参数信息(如:在线翻译,在线答题,账号密码等提交的内容),data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
    3. timeout:访问超时时间,就是超过这个就是就会停止发送请求了。
  2. response,就是urlopen返回的对象。这里介绍这个对象的几个常用的方法和属性:
    1. read()返回获取到的网页数据,注意编码格式得是decode("utf-8"),也可以把这个使用utf-8的这个写一块,像这样data = r.read().decode("utf-8")。
    2. response.status,返回状态码,正常的是200,被禁止就是404啥的
    3. response.reason,返回状态码状态,正常就是ok。

XML

这个就先略过,暂时用不到,而且需要学的东西比较多。
xml主要是用来传输数据的,html是属于xml的一种,但html是主要显示数据的。对于xml一般解析有两种方法DOM和SAX,就先了解这些,等到知识储备够了再补充。

HTMLParser

网络爬虫第一步是获取网页信息,得到由服务器发来的html网页信息;第二步就是解析该网页,获取到我们想要的信息。
首先从自己定义一个类,这个类继承模块html.parser中一个类:HTMLParser

from html.parser import HTMLParser

class MyHtmlParse(HTMLParser):
    def handle_starttag(self, tag: str, attrs):#有时候开始的标签也会加由属性和值
        print("返回开始的标签: "+tag)

    def handle_endtag(self, tag):
        print("返回结束的标签: "+tag)

    def handle_comment(self, data):
        print("返回注释里面的东西: "+data)

    def handle_data(self, data):
        print("返回两个<><>中间的任何东西,不管是不是开始还是结束标签: "+data)

    def handle_startendtag(self, tag,attrs):
        print("返回特殊标签里面的标签和列表值: "+tag+attrs)

p = MyHtmlParse()
p.feed('''<a >ddd</a>
<b>ddd</b>ddd<img age=20/>''')

#最后写的数据有些错误,可能解析不到,用法都是这样的,而且一般都是获取网上的html,所以获取的数据不会出错。


然后根据自己的需求在自己定义的类(MyHtmlParser)里面重新定义继承来的类HTMLParser里面的这四个函数:(一般用也是只用这四个函数)
handle_starttag( tag, attrs)这个函数是用来处理html开始的标签
handle_endtag( tag)这个跟上面一对,处理结束的标签。
handle_startendtag( tag, attrs)这个就是处理在HTML里面一些特殊的结构的标签,就比如这样的(前面img是标签,注意右边必须得由/),没有<></>的结构。
handle_data(data)这个就是重要的获取标签中间的数据,每一个标签,无论<> 还是</>,均会调用handle_data()。
handle_comment(data) ,处理注释,之间的文本,这个简单就是注释。

还要介绍一下这些函数里面的参数:
tag很简单就是html的标签;
attrs是由(属性,值)构成的元组的列表:
比如一个标签是这样的,得到的attrs就是一个列表,列表的元素是元组,元组里面是属性和值:[(‘type’, ‘hidden’), (‘name’, ‘NXX’), (‘id’, ‘IDXX’), (‘value’, ‘VXX’)]这样的。


说一下理解:
首先是四个函数,当碰到<>,自动调用handle_starttag();碰到</>,自动调用handle_endtag(),当然会主动返回获得的tag和attrs,然后传入函数里,你自己根据传入的参数定义函数。还有一个是直接返回注释的,这个就直接返回--中间的值了。
最不容易理解的就是handle_data(data),开始看网页上面写的是返回<></>中间的值,后来才发现不是,它就是自动返回所有<><>中间的值,如果没有,就不会被调用,有的话就会调用,而且换行,空格都会被调用。
差不多重点内容是这些,使用的时候就是把自己定义的类给实例化,然后调用feed()函数,传入html格式的字符串(需要三引号)。

教程里内置的模块就这些,其实还有更多,以后慢慢学。

posted @ 2023-05-29 19:12  楠栀南寒  阅读(38)  评论(0)    收藏  举报