人生苦短, 我用 Python
前文传送门:
小白学 Python 爬虫(1): 开篇 https://www.geekdigging.com/2019/11/13/3303836941/
小白学 Python 爬虫 (2): 前置准备(一) 基本类库的安装 https://www.geekdigging.com/2019/11/20/2586166930/
小白学 Python 爬虫(3): 前置准备(二)Linux 基础入门 https://www.geekdigging.com/2019/11/21/1005563697/
小白学 Python 爬虫(4): 前置准备(三)Docker 基础入门 https://www.geekdigging.com/2019/11/22/3679472340/
小白学 Python 爬虫 (5): 前置准备(四) 数据库基础 https://www.geekdigging.com/2019/11/24/334078215/
小白学 Python 爬虫 (6): 前置准备(五) 爬虫框架的安装 https://www.geekdigging.com/2019/11/25/1881661601/
小白学 Python 爬虫(7):HTTP 基础 https://www.geekdigging.com/2019/11/26/1197821400/
小白学 Python 爬虫(8): 网页基础 https://www.geekdigging.com/2019/11/27/101847406/
小白学 Python 爬虫(9): 爬虫基础 https://www.geekdigging.com/2019/11/28/1668465912/
小白学 Python 爬虫(10):Session 和 Cookies https://www.geekdigging.com/2019/12/01/2475257648/
小白学 Python 爬虫(11):urllib 基础使用(一) https://www.geekdigging.com/2019/12/02/2333822325/
上一篇我们聊了 urlopen 的基本使用姿势, 但这几个简单的参数并不足以构建一个完整的请求. 对于复杂的请求, 例如需要添加请求头就显得无能为力, 这时我们可以选择使用 Request .
Request
官方文档:
首先来看一下 Request 的使用语法:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
url: 请求的地址链接, 只有这个是必传参数, 其余都是可选参数.
data: 如果这个参数需要传递, 则必须传 bytes(字节流)类型的.
headers: 请求头信息, 它是一个字典, 可以在构造请求的时候通过 headers 之间构造, 也可以调用 add_header() 添加.
origin_req_host: 发起请求一方的 host 名称或者也可以是 ip 地址.
unverifiable: 指的是这个请求是否是无法验证的, 默认是 False . 意思就是说用户没有足够权限来选择接收这个请求的结果. 例如我们请求一个 html 文档中的图片, 但是我们没有自动抓取图像的权限, 这时 unverifiable 的值就是 True .
method: 请求方法, 如 GET , POST , PUT , DELETE 等等.
还是先来看一个简单的示例, 使用 Request 爬取博客网站:
- import urllib.request
- request = urllib.request.Request('https://www.geekdigging.com/')
- response = urllib.request.urlopen(request)
- print(response.read().decode('utf-8'))
可以看到, 这里还是使用 urlopen() 来发起请求, 只是参数不再是之前的 URL , Data , timeout 等等信息, 而是变成了 Request 类型的对象.
我们来构建一个稍微复杂一点的请求.
- import urllib.request, urllib.parse
- import JSON
- url = 'https://httpbin.org/post'
- headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ApplewebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
- 'Content-Type': 'application/json;encoding=utf-8',
- 'Host': 'geekdigging.com'
- }
- data = {
- 'name': 'geekdigging',
- 'hello':'world'
- }
- data = bytes(JSON.dumps(data), encoding='utf8')
- req = urllib.request.Request(url=url, data=data, headers=headers, method='POST')
- resp = urllib.request.urlopen(req)
- print(resp.read().decode('utf-8'))
结果如下:
- {
- "args": {},
- "data": "{\"name\": \"geekdigging\", \"hello\": \"world\"}",
- "files": {},
- "form": {},
- "headers": {
- "Accept-Encoding": "identity",
- "Content-Length": "41",
- "Content-Type": "application/json;encoding=utf-8",
- "Host": "geekdigging.com",
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
- },
- "json": {
- "hello": "world",
- "name": "geekdigging"
- },
- "origin": "116.234.254.11, 116.234.254.11",
- "url": "https://geekdigging.com/post"
- }
这里我们通过 4 个参数构建了一个 Request 对象.
通过 url 指定了访问的链接, 还是前面一片文章中提到的测试链接.
在 headers 中指定了 User-Agent , Content-Type 和 Host 3 个参数.
在 data 中使用 JSON.dumps() 将一个 dict 转换成 JSON 格式, 并通过 bytes() 最终转换为字节流.
最后, 指定了访问方式为 POST .
从最终的结果中, 可以看到我们前面的设定全都成功.
进阶操作
前面我们使用 Request 完成了请求头的添加, 如果我们想处理 Cookies 和使用代理访问, 就需要使用到更加强大的 Handler 了. Handler 可以简单理解为各种功能的处理器, 使用它, 几乎可以为我们做到所有有关 HTTP 请求的事情.
urllib.request 为我们提供了 BaseHandler 类, 它是所有其他 Handler 的父类, 它提供了直接使用使用的方法如下:
add_parent(): 添加 director 作为父类.
close(): 关闭它的父类.
parent(): 打开使用不同的协议或处理错误.
default_open(): 捕获所有的 URL 及子类, 在协议打开之前调用.
接下来, 就有各种 Handler 子类集成这个 BaseHandler 类:
HTTPDefaultErrorHandler: 用来处理 http 响应错误, 错误会抛出 HTTPError 类的异常.
HTTPRedirectHandler: 用于处理重定向.
ProxyHandler: 用于设置代理, 默认代理为空.
HTTPPasswordMgr: 用于管理密码, 它维护用户名和密码表.
AbstractBasicAuthHandler: 用于获取用户 / 密码对, 然后重试请求来处理身份验证请求.
HTTPBasicAuthHandler: 用于重试带有身份认证信息的请求.
HTTPCookieProcessor: 用于处理 cookies.
等等, urllib 为我们提供的 BaseHandler 子类非常的多, 小编这里就不一一列举, 各位同学可以通过访问官方文档来查看.
官方文档地址:
在介绍如何使用 Handler 之前, 先介绍一个高级类: OpenerDirector .
OpenerDirector 是用来处理 URL 的高级类, 它分三个阶段来打开 URL:
在每个阶段中调用这些方法的顺序是通过对处理程序实例进行排序来确定的; 每个使用此类方法的程序都会调用 protocol_request() 方法来预处理请求, 然后调用 protocol_open() 来处理请求; 最后调用 protocol_response() 方法来处理响应.
我们可以称 OpenerDirector 为 Opener . 我们之前用过 urlopen() 这个方法, 实际上它就是 urllib 为我们提供的一个 Opener .
Opener 的方法包括:
add_handler(handler): 添加处理程序到链接中
open(url,data=None[,timeout]): 打开给定的 URL 与 urlopen()方法相同
error(proto,*args): 处理给定协议的错误
下面我们来演示一下如何获取网站的 Cookies :
- import http.cookiejar, urllib.request
- # 实例化 cookiejar 对象
- cookie = http.cookiejar.CookieJar()
- # 使用 HTTPCookieProcessor 构建一个 handler
- handler = urllib.request.HTTPCookieProcessor(cookie)
- # 构建 Opener
- opener = urllib.request.build_opener(handler)
- # 发起请求
- response = opener.open('https://www.baidu.com/')
- print(cookie)
- for item in cookie:
- print(item.name + "=" + item.value)
代码中具体的含义小编就不再解释了, 注释已经写得比较完善. 最后得到的打印结果如下:
- <CookieJar[<Cookie BAIDUID=48EA1A60922D7A30F711A420D3C5BA22:FG=1 for .baidu.com/>
- ,
- <Cookie BIDUPSID=48EA1A60922D7A30DA2E4CBE7B81D738 for .baidu.com/>
- ,
- <Cookie PSTM=1575167484 for .baidu.com/>
- ,
- <Cookie BD_NOT_HTTPS=1 for www.baidu.com/>
- ]> BAIDUID = 48EA1A60922D7A30F711A420D3C5BA22:FG=1 BIDUPSID = 48EA1A60922D7A30DA2E4CBE7B81D738
- PSTM = 1575167484 BD_NOT_HTTPS = 1
这里产生一个问题, cookie 既然可以打印, 那么我们能不能将 cookie 的输出保存到文件中呢?
答案当然是可以的, 因为我们知道, cookie 本身就是保存在文件中的.
- # cookies 保存 Mozilla 型文件示例
- filename = 'cookies_mozilla.txt'
- cookie = http.cookiejar.MozillaCookieJar(filename)
- handler = urllib.request.HTTPCookieProcessor(cookie)
- opener = urllib.request.build_opener(handler)
- response = opener.open('http://www.baidu.com')
- cookie.save(ignore_discard=True, ignore_expires=True)
- print('cookies_mozilla 保存成功')
这里我们需修改之前的 CookieJar 为 MozillaCookieJar , 它在生成文件时会用到, 是 CookieJar 的子类, 可以用来处理 Cookies 和文件相关的事件, 比如读取和保存 Cookies , 可以将 Cookies 保存成 Mozilla 型浏览器的 Cookies 格式.
- # Netscape HTTP Cookie File
- # http://curl.haxx.se/rfc/cookie_spec.html
- # This is a generated file! Do not edit.
- .baidu.com TRUE / FALSE 1606703804 BAIDUID 0A7A76A3705A730B35A559B601425953:FG=1
- .baidu.com TRUE / FALSE 3722651451 BIDUPSID 0A7A76A3705A730BE64A1F6D826869B5
- .baidu.com TRUE / FALSE H_PS_PSSID 1461_21102_30211_30125_26350_30239
- .baidu.com TRUE / FALSE 3722651451 PSTM 1575167805
- .baidu.com TRUE / FALSE delPer 0
- www.baidu.com FALSE / FALSE BDSVRTM 0
- www.baidu.com FALSE / FALSE BD_HOME 0
- # cookies 保存 LWP 型文件示例
- filename = 'cookies_lwp.txt'
- cookie = http.cookiejar.LWPCookieJar(filename)
- handler = urllib.request.HTTPCookieProcessor(cookie)
- opener = urllib.request.build_opener(handler)
- response = opener.open('http://www.baidu.com')
- cookie.save(ignore_discard=True, ignore_expires=True)
- print('cookies_lwp 保存成功')
- #LWP-Cookies-2.0
- Set-Cookie3: BAIDUID="D634D45523004545C6E23691E7CE3894:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2020-11-30 02:45:24Z"; comment=bd; version=0
- Set-Cookie3: BIDUPSID=D634D455230045458E6056651566B7E3; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-12-19 05:59:31Z"; version=0
- Set-Cookie3: H_PS_PSSID=1427_21095_30210_18560_30125; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
- Set-Cookie3: PSTM=1575168325; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-12-19 05:59:31Z"; version=0
- Set-Cookie3: delPer=0; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
- Set-Cookie3: BDSVRTM=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
- Set-Cookie3: BD_HOME=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
- # 请求是使用 Mozilla 型文件
- cookie = http.cookiejar.MozillaCookieJar()
- cookie.load('cookies_mozilla.txt', ignore_discard=True, ignore_expires=True)
- handler = urllib.request.HTTPCookieProcessor(cookie)
- opener = urllib.request.build_opener(handler)
- response = opener.open('http://www.baidu.com')
- print(response.read().decode('utf-8'))
- https://www.cnblogs.com/zhangxinqi/p/9170312.html
- https://cuiqingcai.com/5500.html
来源: https://www.cnblogs.com/babycomeon/p/11993172.html