经过一个多星期的学习和尝试,相信大家都已经熟悉python的语法,结构,以及一些基本的包和用途了,现在我们正式准备开始采集,但是有一个大问题,就像之前采集那个美国案例站的时候,每次采集,我们都要写上好几行的指令,而且,还没继承一些类似cookie、refer之类的信息,所以,简单的使用python的urllib是不能满足我们的实际需要的,那么,我们就来分析一下,一个可以日常使用的采集类,应该包含哪些东西。
1、实例化采集类后,自带一些header信息,类似user-agent、accept之类的,能不手动添加就不手动添加
2、在执行了采集后,获取采集到的响应头,解析其中的数据,该记录的记录该执行的执行,在下次调用采集方法时继承获取到的信息
3、可以采集纯文本内容,也可以采集二进制流,方便采集页面和下载相关文档
4、支持不同的字符编码,响应编码,比如gbk、utf8等,比如gzip、deflate等
5、支持不同的请求方法,比如get、put、post、delete、head等
6、不管是否采集异常,都能返回状态码
7、可以伪造、添加各种头信息,伪造、添加cookie之类的信息,类似 Oauth:xxxx,Signture:xxx等
8、支持301、302之类的自动跳转采集,支持meta自动跳转采集
9、自动完成网址补全,不需要我们在根据采集目标,提取到链接后自己再计算
10、如果可以,尽量支持异步采集
11、如果可以,尽量支持事件委托
12、如果可以,尽量支持proxy
13、如果可以,尽量支持断点续传下载和上传
以上这些,是老顾在c#里搞了几年采集总结下来的需求,当以上这些都满足的时候,采集就是一件很简单的事情了
来,让我们一步一步的尽量实现上述需求
------------------------------------------------------------------------------------------------
我们继续把类放到之前提到的custom文件夹下,如果不了解的,请参考文盲的Python入门日记:第五天,搭建一个python调试环境,以及初步探索pymssql的使用,文中在安装完spyder后,有建立自定义类的方法
嗯,为了不与其他包混淆,我们给这个类的文件夹起名叫spider,采集类嘛,这个方便理解
嗯,开始开工,先来个基本定义,类名就用Ajax吧,反正包名和类名不一致是常态
class Ajax: def __init__(self): self.version = '0.1'
from spider import Ajax # Reloaded modules: spider
嗯,类定义成功了,然后,开始一步一步的实现我们的需求吧
第一步,先定义method枚举,因为这个数量有限,具体实现先不管,我们先把method限制掉
那么,来定义枚举吧
from enum import Enum class Ajax: def __init__(self): self.version = '0.1' self.agent = 'Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko/20100101 Firefox/11.0' self.refer = '' self.current_url = '' self.method = self.Method.GET class Method(Enum): GET = 1 POST = 2 HEAD = 3 PUT = 4 DELETE = 5
因为这个方法就只在发起http请求的时候使用,所以,我们把Metho作为Ajax的子类型定义,顺便定义了几个变量,相信大家看到变量名就知道这些是干嘛用的了,就不解释了
然后,我们就封装请求头,请求头是个词典,但是,是多个现有项的组合,所以我们把他定义为一个属性
@property def Header(self): return {'refer':self.refer ,'user-agent':self.agent ,'accept':self.accept ,'accept-encoding':self.encoding ,'accept-language':self.lang ,'cache-control':self.cache}
from spider import Ajax ajax = Ajax() print(ajax.Header) # {'refer': '', 'user-agent': 'Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko/20100101 Firefox/11.0', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'cache-control': 'no-cache'}
很好,请求头信息先这样,之后后边需要修改的地方,比如添加请求头,添加cookie,我们稍后再做,先来做请求方法吧,尝试我们用自己封装的类进行第一次采集
def Http(self,url,method=Method.GET,post=None): self.current_url = url req = request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) req.encoding = self.charset res = request.urlopen(req) data = res.read() self.html = data.decode(encoding=(re.sub('[-]','',self.charset))) return self.html
本来,印象中看到过有人使用 request.urlopen(req,headers)这样的格式,但是我在使用时居然报错,仔细百度了一下,原来还需要 urllib2 这个包支持,使用pip install urllib2,结果发现还不是官方包,那就算了,反正我们是封装到类里,只要能实现header信息携带,那就没必要开那么多包,使用自带的 add_header 就挺好
另外需要注意的是,decode里,encoding不能使用 utf-8,只能使用utf8,没有哪个减号,所以用正则处理了一下
from spider import Ajax ajax = Ajax() b = ajax.Http('https://www.cctv.com') print(b)
轻松搞定第一次采集,那么,我们的第一个需求,自带一些header信息就算完成了,当然后边肯定还要调整,追加很多内容,但现在先这样了
同时,我们第一次采集已经成功了,但我们并没有把相应头信息拿下来,所以,现在我们去调整下代码,看看响应头信息
def Http(self,url,method=Method.GET,post=None): self.current_url = url req = request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) req.encoding = self.charset res = request.urlopen(req,timeout=3) print(res.headers) data = res.read() self.html = data.decode(encoding=re.sub(r'[-]','',self.charset)) return self.html
Date: Wed, 23 Jun 2021 14:35:30 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close Expires: Wed, 23 Jun 2021 14:35:39 GMT Server: CCTVQCLOUD Cache-Control: max-age=180 X-UA-Compatible: IE=Edge Age: 171 X-Via: 1.1 PSbjwjBGP2sa180:8 (Cdn Cache Server V2.0), 1.1 PSgddgzk5hm168:0 (Cdn Cache Server V2.0), 1.1 chk67:1 (Cdn Cache Server V2.0) X-Ws-Request-Id: 60d346b2_PS-PEK-01fXq68_10786-48330
相应头回来了,但现在出大麻烦了,开始报错
self.html = data.decode(encoding=re.sub(r'[-]','',self.charset)) UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
嗯。。。。这个错误还是一阵一阵的,针对错误的采集,我们直接返回data,先不解码,发现前三个字符时\x1f\x8b\x08,哦吼,这明显就不是正常的编码格式了,百度一下,果然如此 https://blog.csdn.net/weixin_36842174/article/details/88924660,看看,我们还没准备好,就遇到了第四点需求里的情况了,页面内容是gzip格式编码的,来调整下代码
嗯。。。回过头来看响应头,果然发现 gzip 消息了,来开刚才我们得到的响应头
Date: Thu, 24 Jun 2021 00:52:04 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close Expires: Thu, 24 Jun 2021 00:54:41 GMT Cache-Control: max-age=180 X-UA-Compatible: IE=Edge Content-Encoding: gzip Age: 23 X-Via: 1.1 PS-000-017po25:9 (Cdn Cache Server V2.0), 1.1 PS-TSN-01hDc143:15 (Cdn Cache Server V2.0) X-Ws-Request-Id: 60d3d734_PS-TSN-01hOH49_13803-39259
content-encoding: gzip,好吧,我们先不管编码问题,我们先处理响应头吧,然后根据响应头来处理后续内容,还是回归到第二步了~~~~
那么,开始对响应头进行解析
import re import gzip from enum import Enum from urllib import request class Ajax: def __init__(self): self.version = '0.1' self.agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400' self.refer = '' self.cache = 'no-cache' self.lang = 'zh-CN,zh;q=0.9' self.encoding = 'gzip, deflate, br' self.accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' self.current_url = '' self.method = self.Method.GET self.charset = 'utf-8' self.__content_encoding = '' self.html = '' class Method(Enum): GET = 1 POST = 2 HEAD = 3 PUT = 4 DELETE = 5 DEBUG = 6 @property def Header(self): return {'refer':self.refer ,'user-agent':self.agent ,'accept':self.accept ,'accept-encoding':self.encoding ,'accept-language':self.lang ,'cache-control':self.cache} def Http(self,url,method=Method.GET,post=None): self.current_url = url req = request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) req.encoding = self.charset res = request.urlopen(req,timeout=3) self.__headers = str(res.headers) self.__parseHeaders() data = res.read() enc = re.sub(r'[-]','',self.charset) if 'gzip' in self.__content_encoding: self.html = gzip.decompress(data).decode(enc) else: self.html = data.decode(encoding=enc) return self.html def __parseHeaders(self): dict = {n.group(1).lower():n.group(2).strip() for n in re.finditer('([^\r\n:]+):([^\r\n]+)',self.__headers)} if 'content-encoding' in dict: self.__content_encoding = dict['content-encoding'] else: self.__content_encoding = ''
来一次到目前为止的所有代码,嗯,gzip已经支持起来了,deflate的碰到再说,至于这次的响应头里,没有set-cookie,所以暂时也不对cookie操作,其他方式的cookie后边再说
同时,这次的响应头里,也没什么可用的信息,那就继续进行第三点,可以采集纯文本内容,也可以采集二进制流,方便采集页面和下载相关文档
事实上,不管是访问页面也好,下载也好,都是正常的http请求,区别在于,页面的,我们可以通过浏览器打开,其他的都以二进制流直接保存到文件了,也就是说,我们的Http方法其实是可以访问可下载资源的,不过他没有相关的保存设置
所以,我们从新建立一个方法,单独用来进行下载
def Download(self,url,filename,method=Method.GET,post=None): req = request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) res = request.urlopen(req,timeout=3) data = res.read() f = open(filename,'wb+') f.write(data) f.close()
来尝试一下第一个下载,还是用上次的那个 xslt 文件测试,直接下载 xlst 到 d盘看看
from spider import Ajax ajax = Ajax() ajax.Download('https://www.govinfo.gov/content/pkg/BILLS-117hr3237ih/xml/BILLS-117hr3237ih.xml', r'd:\\test.xml')
很好,D盘多了一个 test.xml 文件,下载功能实现,具体完善以后再说,嘿嘿嘿嘿
支持不同的字符编码,响应编码,比如gbk、utf8等,比如gzip、deflate等,嗯这个需求基本上算是已经搭好架子了,gzip已经支持了,页面编码,也可以通过实例.charset设置,嗯继续下一个了
支持不同的请求方法,比如get、put、post、delete、head等,这个也是很重要的哦,有一些第三方提供的接口,他们会要求请求方法各种各样,除了常见的get、post,put和delete也很常用,比如第三方云存储,老办法,看别人的文章 https://blog.csdn.net/qq_35959613/article/details/81068042
哦哦,又引入了一个 requests 包。。嗯嗯,我们也引入,稍微变动变动,我们就可以支持各种方法了,再参考另一个文章 https://www.cnblogs.com/jingdenghuakai/p/11805128.html
嗯嗯嗯,这些文章都是挺好的,我们先建立一个测试环境吧,要不采集现成的网站,不会返回我们需要的测试详情,做一个测试页,提交的所有内容都反馈出来,这样才好继续编写
protected void Page_Load(object sender, EventArgs e) { Response.Write("<div><b>Request.HttpMethod</b>:" + Request.HttpMethod + "</div>"); Response.Write("<div><b>Request.InputStream.Length</b>:" + Request.InputStream.Length.ToString() + "</div>"); Response.Write("<div><b>Request.Files.Count</b>:" + Request.Files.Count.ToString() + "</div>"); Response.Write("<div><b>Request.Headers</b></div>"); foreach (string key in Request.Headers.Keys) { Response.Write("<div>" + key + " : " + Request.Headers[key] + "</div>"); } Response.Write("<div><b>Request.QueryString</b></div>"); foreach (string key in Request.QueryString.Keys) { Response.Write("<div>" + key + " : " + Request.QueryString[key] + "</div>"); } Response.Write("<div><b>Request.Form</b></div>"); foreach (string key in Request.Form.Keys) { Response.Write("<div>" + key + " : " + Request.Form[key] + "</div>"); } }
老顾自己是用的IIS,用c#写了个webform直接用来测试,大家可以根据自己的情况建立测试文件,基本就需要这么写内容,就可以测试出绝大部分功能了
这是老顾在浏览器里测试的结果,用来做个参考
然后,刚才的文章虽然都很好,但又引入了一个包。。。。先不引入,试试看 urllib 能不能做到,仅仅追加一个 req.method 设置
def Http(self,url,method=Method.GET,postdata=None): self.current_url = url req = request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) req.encoding = self.charset req.method = method.name res = request.urlopen(req,timeout=3) self.__headers = str(res.headers) self.__parseHeaders() data = res.read() enc = re.sub(r'[-]','',self.charset) if 'gzip' in self.__content_encoding: self.html = gzip.decompress(data).decode(enc) else: self.html = data.decode(encoding=enc) return self.html
import re from spider import Ajax ajax = Ajax() b = ajax.Http('https://localhost/test.aspx') b = re.sub('<div[^<>]*>','\\n',b) b = re.sub('<[^<>]+>','',b) print(b)
Request.HttpMethod:GET Request.InputStream.Length:0 Request.Files.Count:0 Request.Headers Cache-Control : no-cache Connection : close Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding : gzip, deflate, br Accept-Language : zh-CN,zh;q=0.9 Host : www2018.caigou.com.cn User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400 Refer : Request.QueryString Request.Form
import re from spider import Ajax ajax = Ajax() b = ajax.Http('https://localhost/test.aspx?x=1&y=2',Ajax.Method.POST,'a=1&b=2') b = re.sub('<div[^<>]*>','\\n',b) b = re.sub('<[^<>]+>','',b) print(b)
Request.HttpMethod:POST Request.InputStream.Length:0 Request.Files.Count:0 Request.Headers Cache-Control : no-cache Connection : close Content-Length : 0 Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding : gzip, deflate, br Accept-Language : zh-CN,zh;q=0.9 Host : www2018.caigou.com.cn User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400 Refer : Request.QueryString x : 1 y : 2 Request.Form
哦吼,httpmethod方法的确变了哦,那么就是说,使用urllib本身,就可以完成这些,不需要再引入其他包了,那就继续完善,先把post支持起来
说到post,那提交的信息也分两种,一种是表单方式,一种就是纯文字流方式。。。这也是无奈了,都支持起来吧,先支持一下表单方式
def Http(self,url,method=Method.GET,postdata=None): self.current_url = url enc = re.sub(r'[-]','',self.charset) req = urllib.request.Request(url) for k in self.Header: req.add_header(k,self.Header[k]) req.encoding = self.charset req.method = method.name if postdata!=None: if isinstance(postdata,dict): postdata = urllib.parse.urlencode(postdata) postdata = postdata.encode(enc) res = urllib.request.urlopen(req,timeout=3,data=postdata) self.__headers = str(res.headers) self.__parseHeaders() data = res.read() if 'gzip' in self.__content_encoding: self.html = gzip.decompress(data).decode(enc) else: self.html = data.decode(encoding=enc) return self.html
仅仅追加了一个 postdata 判断,并判断是否是字典,开始看到别人用urllib.parse.urlencode还不知道这个方法干嘛的,自己实际测试后发现,这就是将dict组成querystring字符串的方法。。。
得,我自己传递的值就是a=1&b=2了,不用转换格式了
Request.HttpMethod:POST Request.InputStream.Length:7 Request.Files.Count:0 Request.Headers Cache-Control : no-cache Connection : close Content-Length : 7 Content-Type : application/x-www-form-urlencoded Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding : gzip, deflate, br Accept-Language : zh-CN,zh;q=0.9 Host : www2018.caigou.com.cn User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400 Refer : Request.QueryString x : 1 y : 2 Request.Form a : 1 b : 2 python请求测试 div {padding:10px;font-size:14px;margin-bottom:10px;}
很好,post成功了,在 Request.Form 下有两个参数了,同样Request.InputStream也获取到信息了,那么这个是form提交方式
然后,有些api需要提交一个json字符串,那我们就来尝试一下
很好,可以直接用类型转换得到词典对应的json字符串,很方便
那么,就提交这个字符串试一下
Request.HttpMethod:POST Request.InputStream.Length:16 Request.Files.Count:0 Request.Headers Cache-Control : no-cache Connection : close Content-Length : 16 Content-Type : application/x-www-form-urlencoded Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding : gzip, deflate, br Accept-Language : zh-CN,zh;q=0.9 Host : www2018.caigou.com.cn User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400 Refer : Request.QueryString x : 1 y : 2 Request.Form : {'a': 1, 'b': 2} python请求测试 div {padding:10px;font-size:14px;margin-bottom:10px;}
哦吼,InputStream得到了16个字符,在Request.Form里,也体现出来这个信息了,连花括号在内,正好是16个字符,所以说,post支持完成
那么再看看put和delete,直接修改method参数
ajax.Http('https://www2018.caigou.com.cn/test.aspx?x=1&y=2',Ajax.Method.PUT)
HTTPError: Method Not Allowed
意料之外的错误,方法不被支持?PUT和DELETE都是这个错误,HEAD到是可以用,嗯,就是页面响应内容没了,只有返回头了
现在来调试PUT和DELETE吧,在网上搜索了半天,发现有用 httplib2实现的,有用urllib2实现的,在本地sitepackages文件夹里,还发现了个urllib3包。。。。总之,就是urllib本身是无法使用delete和put方法的,毕竟这两个不算安全方法啊
看来,不得不引入urllib2包了。。。。。。。嗯???。。。。。。TNND,python3里根本没有urllib2这个包!!!python3把 urllib 和 urllib2 整合了,但不支持了 PUT 和 DELETE!!!天啊。。。一个大坑。。。算了,我换个别的包吧,requests 就不错,经过一番整改,整个代码变成了这样
import gzip import re import requests import zlib from enum import Enum class Ajax: def __init__(self): self.version = '0.1' self.agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400' self.refer = '' self.cache = 'no-cache' self.lang = 'zh-CN,zh;q=0.9' self.encoding = 'gzip, deflate, br' self.accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' self.current_url = '' self.method = self.Method.GET self.charset = 'utf-8' self.__content_encoding = '' self.html = '' class Method(Enum): GET = 1 POST = 2 HEAD = 3 PUT = 4 DELETE = 5 OPTIONS = 6 TRACE = 7 PATCH = 8 @property def Header(self): return {'refer':self.refer ,'user-agent':self.agent ,'accept':self.accept ,'accept-encoding':self.encoding ,'accept-language':self.lang ,'cache-control':self.cache} def Http(self,url,method=Method.GET,postdata=None): self.current_url = url if postdata!=None: if isinstance(postdata,str): postdata = {n.group(1):n.group(2) for n in re.finditer('([^&=]+)=([^&]*)',postdata)} if method == self.Method.GET: res = requests.get(url=url,headers=self.Header,data=postdata) elif method == self.Method.POST: res = requests.post(url=url,headers=self.Header,data=postdata) elif method == self.Method.HEAD: res = requests.head(url=url,headers=self.Header,data=postdata) elif method == self.Method.PUT: res = requests.put(url=url,headers=self.Header,data=postdata) elif method == self.Method.DELETE: res = requests.delete(url=url,headers=self.Header,data=postdata) enc = re.sub(r'[-]','',res.encoding) if enc == 'ISO88591': enc = 'gbk' self.status = res.status_code self.__headers = str(res.headers) self.__parseHeaders() if method == self.Method.HEAD: return self.__headers data = res.content if 'gzip' in self.__content_encoding: self.html = gzip.decompress(data).decode(enc) elif 'deflate' in self.__content_encoding: try: self.html = zlib.decompress(data, -zlib.MAX_WBITS).decode(enc) except zlib.error: self.html = zlib.decompress(data).decode(enc) else: self.html = data.decode(encoding=enc) return self.html def __parseHeaders(self): dict = {n.group(1).lower():n.group(2).strip() for n in re.finditer('([^\r\n:]+):([^\r\n]+)',self.__headers)} if 'content-encoding' in dict: self.__content_encoding = dict['content-encoding'] else: self.__content_encoding = ''
不想记那么多方法,一个方法能调用所有method才是我所期望的,可惜 requests.Request 不知道该怎么用,没有找到相关手册
根据帮助,他得到个 PreparedRequest 带 method 的对象,问题是,后续文档没找到,怎么让这个对象发生实际请求,并获取数据呢,经过一番搜索,还是找到了一个文章https://blog.csdn.net/weixin_44523387/article/details/90732389
原来,这个Request方法, 需要用到一个Session对象,由这个对象保持会话,并发送请求,so。。。。貌似后续的cookie保持也可以用他来实现?那么再次调整我们的方法
import gzip import re import requests import zlib from enum import Enum class Ajax: def __init__(self): self.version = '0.1' self.agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400' self.refer = '' self.cache = 'no-cache' self.lang = 'zh-CN,zh;q=0.9' self.encoding = 'gzip, deflate, br' self.accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' self.current_url = '' self.method = self.Method.GET self.charset = 'utf-8' self.__content_encoding = '' self.__session = requests.Session() self.html = '' class Method(Enum): GET = 1 POST = 2 HEAD = 3 PUT = 4 DELETE = 5 OPTIONS = 6 TRACE = 7 PATCH = 8 @property def Header(self): return {'refer':self.refer ,'user-agent':self.agent ,'accept':self.accept ,'accept-encoding':self.encoding ,'accept-language':self.lang ,'cache-control':self.cache} def Http(self,url,method=Method.GET,postdata=None): self.current_url = url if postdata!=None: if isinstance(postdata,str): postdata = {n.group(1):n.group(2) for n in re.finditer('([^&=]+)=([^&]*)',postdata)} req = requests.Request(method=method.name,url=url,headers=self.Header,data=postdata) pre = self.__session.prepare_request(req) res = self.__session.send(pre) enc = re.sub(r'[-]','',res.encoding) if enc == 'ISO88591': charset = re.findall('''<meta[^<>]*?charset=['"]?([^'""]+)['"\\s]?''',res.text,re.I) if len(charset) == 0: enc = re.sub(r'[-]','',self.charset) else: enc = re.sub(r'[-]','',charset[0]) self.status = res.status_code self.__headers = str(res.headers) self.__parseHeaders() if method == self.Method.HEAD: return self.__headers data = res.content if 'gzip' in self.__content_encoding: self.html = gzip.decompress(data).decode(enc) elif 'deflate' in self.__content_encoding: try: self.html = zlib.decompress(data, -zlib.MAX_WBITS).decode(enc) except zlib.error: self.html = zlib.decompress(data).decode(enc) else: self.html = data.decode(encoding=enc) return self.html def __parseHeaders(self): dict = {n.group(1).lower():n.group(2).strip() for n in re.finditer('([^\r\n:]+):([^\r\n]+)',self.__headers)} if 'content-encoding' in dict: self.__content_encoding = dict['content-encoding'] else: self.__content_encoding = ''
在初始化这个实例的时候,建立一个私有变量__session,用来发送这个实例中的请求,然后,就不再需要判断我的请求方法method到底是什么了,这个方法都支持了,那一大堆的if elif就扔掉吧,然后把download方法也调整一下
def Download(self,url,filename,method=Method.GET,postdata=None): if postdata!=None: if isinstance(postdata,str): postdata = {n.group(1):n.group(2) for n in re.finditer('([^&=]+)=([^&]*)',postdata)} req = requests.Request(method=method.name,url=url,headers=self.Header,data=postdata) pre = self.__session.prepare_request(req) res = self.__session.send(pre) data = res.content f = open(filename,'wb+') f.write(data) f.close()
至此,我们列出的需求的1、2、3、4、5、6点都已经满足了,后边的需求,我们下次再继续实现
再次声明,老顾的python也是刚刚开始学习,从2021年6月6日才开始的哦,如有错漏,还请指正