本文共 14156 字,大约阅读时间需要 47 分钟。
opener是 urllib.request.OpenerDirector 的实例,我们之前一直都在使用的urlopen,它是一个特殊的模块构建好的opener
但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:
(1)使用相关的 Handler处理器
来创建特定功能的处理器对象;
(2)然后通过 urllib.request.build_opener()
方法使用这些处理器对象,创建自定义opener对象;
(3)使用自定义的opener对象,调用open()
方法发送请求。
如果程序里所有的请求都使用自定义的opener,可以使用urllib.request.install_opener()
将自定义的 opener 对象 定义为 全局opener,表示之后凡是调用urlopen,都将使用这个opener来打开
在urllib库中,给我们提供了一些Handler:HTTPHandler,HTTPSHandler,ProxyHandler,BaseHandler,AbstractHTTPHandler,FileHandler,FTPHandler,分别用于处理HTTP,HTTPS,Proxy代理等。
import urllibfrom urllib import requesthanders = urllib.request.HTTPSHandler() #构建一个HTTPHandler 处理器对象,支持处理HTTPS请求hander = urllib.request.HTTPHandler() #构建一个HTTPHandler 处理器对象,支持处理HTTP请求# 方式1opener = urllib.request.build_opener(hander) #创建一个打开器urllib.request.install_opener(opener) #安装一个全局的打开器(这一步可选不安装采用默认)request = urllib.request.Request("http://www.baidu.com/")response = opener.open(request) #opener打开请求print(response.read().decode())#方式2response = urllib.request.urlopen('http://www.baidu.com') #urllib.request.urlopen() 特殊的openerprint(response.read().decode())
如果在 HTTPHandler()增加 debuglevel=1参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时可以省去抓包的工作。
# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0http_handler = urllib.request.HTTPHandler(debuglevel=1)# 构建一个HTTPHSandler 处理器对象,支持处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0https_handler = urllib.request.HTTPSHandler(debuglevel=1)
Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。
(1) Cookie原理
HTTP是无状态的面向连接的协议, 为保持连接状态, 引入Cookie机制 Cookie是http消息头中的一种属性,包括:Cookie名字(Name)Cookie的值(Value)Cookie的过期时间(Expires/Max-Age)Cookie作用路径(Path)Cookie所在域名(Domain),使用Cookie进行安全连接(Secure)。前两个参数是Cookie应用必要条件,还包括Cookie大小(Size,不同浏览器对Cookie个数及大小限制是有差异的)。
Cookie由变量名和值组成,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
(2) cookie应用
Cookies在爬虫方面最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续。
# 获取一个有登录信息的Cookie模拟登陆import urllibfrom urllib import request# 1. 构建一个已经登录过的用户的headers信息headers = { "Host":"www.renren.com", "Connection":"keep-alive", "Upgrade-Insecure-Requests":"1", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6", # Accept-Encoding: gzip, deflate, sdch # 便于终端阅读,表示不支持压缩文件 # 重点:这个Cookie里记录了用户名,密码(通常经过RAS加密) "Cookie": "anonymid=ixrna3fysufnwv; depovince=GW; _r01_=1; JSESSIONID=abcmaDhEdqIlM7riy5iMv; jebe_key=f6fb270b-d06d-42e6-8b53-e67c3156aa7e%7Cc13c37f53bca9e1e7132d4b58ce00fa3%7C1484060607478%7C1%7C1484060607173; jebecookies=26fb58d1-cbe7-4fc3-a4ad-592233d1b42e|||||; ick_login=1f2b895d-34c7-4a1d-afb7-d84666fad409; _de=BF09EE3A28DED52E6B65F6A4705D973F1383380866D39FF5; p=99e54330ba9f910b02e6b08058f780479; ap=327550029; first_login_flag=1; ln_uact=mr_mao_hacker@163.com; ln_hurl=http://hdn.xnimg.cn/photos/hdn521/20140529/1055/h_main_9A3Z_e0c300019f6a195a.jpg; t=214ca9a28f70ca6aa0801404dda4f6789; societyguester=214ca9a28f70ca6aa0801404dda4f6789; id=327550029; xnsid=745033c5; ver=7.0; loginfrom=syshome"}# 2. 通过headers里的报头信息(主要是Cookie信息),构建Request对象req = urllib.request.Request("http://www.renren.com/", headers = headers)# 3. 直接访问renren主页,服务器会根据headers报头信息(主要是Cookie信息),判断这是一个已经登录的用户response = urllib.request.urlopen(req)# 4. 打印响应内容print(response.read())# 或者构建openerhander = urllib.request.HTTPHandler()opener = urllib.request.build_opener(hander)req = urllib.request.Resquest("http://www.renren.com/", headers = headers)response = opener.open(req)print(response.read().decode())
但是这样做太过复杂,我们先需要在浏览器登录账户,并且设置保存密码,并且通过抓包才能获取这个Cookie,那有么有更简单方便的方法呢?
在Python处理Cookie,一般是通过cookielib
模块和 urllib模块的HTTPCookieProcessor
处理器类一起使用
cookielib
模块:主要作用是提供用于存储cookie的对象
HTTPCookieProcessor
处理器:主要作用是处理这些cookie对象,并构建handler对象。
(1)cookielib库
该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar
#CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。#FileCookieJar (filename,delayload=None,policy=None):从CookieJar派生而来,用来创建FileCookieJar实例,检索cookie信息并将cookie存储到文件中。filename是存储cookie的文件名。delayload为True时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。#MozillaCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与Mozilla浏览器 cookies.txt兼容的FileCookieJar实例。#LWPCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与libwww-perl标准的 Set-Cookie3 文件格式兼容的FileCookieJar实例。
其实大多数情况下,我们只用CookieJar(),如果需要和本地文件交互,就用 MozillaCookjar() 或 LWPCookieJar()
(2) 案例
HTTPCookieProcessor 用来配置cookie的处理器
ProxyHandler 用来配置代理
HTTPHander 用来配置http
HTTPSHander 用来配置https
cookie库的配置流程:
handler会自动的保存登录之后的cookie
import urllib.requestfrom http import cookiejar cookies = cookiejar.CookieJar() # 构建一个CookieJar对象实例来保存cookie# 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象cookie_handler = urllib.request.HTTPCookieProcessor(cookies) opener = urllib.request.build_opener(cookie_handler) # 构建打开器response = opener.open("http://www.baidu.com") #访问页面cookieDict = ''for cookie in cookies: print(cookie) #类型为cookieDict += cookie.name +'='+ cookie.value + '\n'print(cookieDict)#输出: BAIDUID=ACB161A0A42A0374A9D9C4F94DC7F563:FG=1BIDUPSID=ACB161A0A42A0374A9D9C4F94DC7F563H_PS_PSSID=1426_21083_26577_22073PSTM=1528979080BDSVRTM=0BD_HOME=0
import urllib.requestfrom http import cookiejarfilename = 'cookie.txt'#涉及本地文件交互采用LWPCCookieJar实例保存cookiecookies = cookiejar.LWPCCookieJar(filename=filename) cookie_handle = urllib.request.HTTPCookieProcessor(cookies) #构建cookie处理器headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36"}req = urllib.request.Request('http://www.baidu.com',headers=headers) #构建请求response = opener.open(req) #打开请求cookies.save(ignore_discard=True, ignore_expires=True) #忽略错误并保存cookie至本地
import urllib.requestfrom http import cookiejarfilePath = 'baiduCookie.txt' #本地已存在的文件cookies = cookiejar.LWPCookieJar() #实例化保存cookie对象cookies,不需传参cookies.load(filePath=filePath,ignore_discard=True, ignore_expires=True) #对cookies文件进行加载cookie_handler = urllib.request.HTTPCookieProcessor(cookies) #处理器opener = urllib.request.build_opener(cookie_handler)#构建打开器headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36"}req = urllib.request.Request('http://www.baidu.com',headers=headers) #构建请求response = opener.open(req) #打开请求
(1)Request部分解析
Headers —— 显示客户端发送到服务器的 HTTP 请求的 header,显示为一个分级视图,包含了 Web 客户端信息、Cookie、传输状态等。Textview —— 显示 POST 请求的 body 部分为文本。WebForms —— 显示请求的 GET 参数 和 POST body 内容。HexView —— 用十六进制数据显示请求。Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(授权) 信息.Raw —— 将整个请求显示为纯文本。JSON - 显示JSON格式文件。XML —— 如果请求的 body 是 XML 格式,就是用分级的 XML 树来显示它。
(2)Response部分详解
Transformer —— 显示响应的编码信息。Headers —— 用分级视图显示响应的 header。TextView —— 使用文本显示相应的 body。ImageVies —— 如果请求是图片资源,显示响应的图片。HexView —— 用十六进制数据显示响应。WebView —— 响应在 Web 浏览器中的预览效果。Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(授权) 信息。Caching —— 显示此请求的缓存信息。Privacy —— 显示此请求的私密 (P3P) 信息。Raw —— 将整个响应显示为纯文本。JSON - 显示JSON格式文件。XML —— 如果响应的 body 是 XML 格式,就是用分级的 XML 树来显示它
(3)模拟登陆人人网
import urllibfrom urllib import request, parsefrom http import cookiejarfilename = "cookie.txt"cookie = cookiejar.LWPCookieJar(filename=filename) #创建一个cookie对象,保存cookie# 构建cookie处理器hander_cookie = urllib.request.HTTPCookieProcessor(cookie)# 创建一个打开器opener = urllib.request.build_opener(hander_cookie)# 安装一个全局可用的openerurllib.request.install_opener(opener)header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36"}loginUrl = "http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018542123646"data = {"email": "用户名","password": "密码"}data = urllib.parse.urlencode(data).encode('utf-8') #将data转二进制req = urllib.request.Request(url=loginUrl, headers=header, data=data) #构建request请求response = opener.open(req) #打开请求cookie.save(ignore_discard=True, ignore_expires=True) # 保存cookie可重复使用print(response.read().decode()) #返回登陆后的响应print(cookie,type(cookie)) # cookie存放在列表里indexurl = "http://www.renren.com/SysHome.do" #登陆界面print("==================")response = opener.open(indexurl)print(response.read().decode()) #打印登陆后的网页
(4)重复使用cookie
import urllibfrom urllib import request, parsefrom http import cookiejar# 创建一个cookie对象 filename = "cookie.txt" #本地已存在的cookiecookie = cookiejar.LWPCookieJar()cookie.load(filename, ignore_expires=True, ignore_discard=True) # 加载cookiehander_cookie = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(hander_cookie)urllib.request.install_opener(opener)header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"}indexurl = "http://zhibo.renren.com/top"print("==================")print(urllib.request.urlopen(indexurl).read().decode())
使用代理IP,这是爬虫/反爬虫的第二大招,通常也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。urllib中通过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:
import urllib.request#构建了两个代理Handler,一个有代理IP,一个没有代理IPhttpproxy_handler = urllib.request.ProxyHandler({ "http":"120.76.55.49:8088"})nullproxy_handler = urllib.request.ProxyHandler({})proxyswitch = True #定义一个代理开关#通过urllib2.build_opener()方法使用这些代理Handler对象,创建自定义openerif proxyswitch: opener = urllib.request.build_opener(httpproxy_handler)else: opener = urllib.request.build_opener(nullproxy_handler)request = urllib.request.Request("http://www.baidu.com/")#1.如果这么写,只有使用opener.open()方法发送请才使用自定义的代理,而urlopen()使用自定义代理response = opener.open(request)#2.如果这么写,就是opener应用到全局,不管是opener.open()还是urlopen()发送请求,都将使用自定义代理# urllib.request.install_opener(opener)# response = urllib.request.urlopen(request)print(response.read())
免费短期代理网站举例:
#当有足够的代理ip与用户代理时,可以随机操作import urllib.requestimport randomproxy = { "http": "10.31.162.71:808"} # 代理ip, 无密码# 代理ip, 有密码{"协议":"用户名:密码@IP:端口"}# proxy = {"HTTP": "User1:123456@10.31.162.71:808"} # ip代理池列表proxyList = [{ "HTTPS": "14.118.252.111:6666"}, { "HTTPS": "122.72.18.34:80"}, { "HTTPS": "122.72.18.35:80"}]# user-agent代理列表(或者直接构建字典 {User-Agent:" "})UserAngentList=[ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36"]for _ in range(1): proxy = random.choice(proxyList) # proxy_handler = urllib.request.ProxyHandler(proxy) # 代理处理器 opener = urllib.request.build_opener(proxy_handler) # 创建打开器opener headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"} url = "http://blog.csdn.net/Gi1gamesh/article/details/80690415" req = urllib.request.Request(url, headers=headers) req.add_header("User-Agent", random.choice(UserAngentList)) #给用户代理添加User-Agent属性 response = urllib.request.urlopen(req) #进行异常处理 try: req = urllib.request.Request(url, headers=headers) #构建请求 response = urllib.request.urlopen(req) #打开请求 print(response.code) #状态码 except: pass
HTTPPasswordMgrWithDefaultRealm()
类创建一个密码管理对象,用来保存HTTP请求相关的用户名和密码,主要应用两个场景:
ProxyBasicAuthHandler(代理授权验证)
如果我们使用之前的代码来使用私密代理,会报HTTP 407错误,表示代理没有通过身份验证:
urllib2.HTTPError:HTTP Error 407:Proxy Authentication Required
所以我们需要改写代码,通过:
HTTPPasswordMgrWithDefaultRealm()
:来保存私密代理的用户密码ProxyBasicAuthHandler()
:来处理代理的身份。import urllib#私密代理授权的账户user = "mr_mao_hacker"#私密代理授权的密码passwd = "sffqry9r"#私密代理IPproxyserver = "61.23.123.43:16813"#1. 构建一个密码管理对象,用来保存需要处理的用户名和密码passwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() #2. 添加账户信息,第一个参数是realm是与远程服务器相关的域信息,一般没人管它都是写None,后面三个参数分别是 代理服务器,用户名,密码passwdmgr.add_password(None, proxyserver, user, passwd)#3. 构建一个基础用户名/密码验证的ProxyBasicAuthHandler处理器对象,参数是创建的密码管理对象#注意:这里不再使用普通的ProxyHandler累了。proxyauth_handler = urllib.request.ProxyBasicAuthHandler(passwdmgr)#4. 通过build_opener()方法使用代理handler对象,创建自定义opener对象,参数包括构建的proxyauth_handleropener = urllib.request.build_opener(proxyauth_handler)#5.构建request请求request = urllib.request.Request("http://www.baidu.com/")#6.使用自定义的opener发送请求response = opener.open(request)#7.打印响应内容print(response.read())
有些Web服务器(包括HTTP/FTP等)访问时,需要进行用户身份验证,爬虫直接访问会报HTTP 401错误,表示访问身份未经授权:
urllib2.HTTPError:HTTP Error 401:Unauthorized
如果我们有客户端的用户名和密码,我们可以通过下面的方法去访问爬取:
#用户名user = "test"#密码passwd = "123456"#web服务器IPwebserver = "18.123.123.1:16354"#1. 构建一个用户密码管理对象,用来保存需要处理的用户密码passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()#2. 添加账户信息,第一个参数是realm与远程服务器相关的域消息,一般没人管都是写None,后面三个参数分别是服务器,用户名,密码passmgr.add_password(None, webserver, user, passwd)#3.构建一个Http基础用户名/密码验证的HTTPBasicAuthHandler处理器对象,参数是创建的密码管理对象httpauth_handler = urllib.request.HTTPBasicAuthHandler(passmgr)#4.通过build_opener()方法使用这些代理handler对象,创建自定义的opener对象,参数是创建的httpauth_handleropener = urllib.request.build_opener(httpauth_handler)#5.可以选择通过install_opener()方法定义全局openerurllib.request.install_opener(opener)#6.构建request对象request = urllib.request.Request("http://www.baidu.com/")#7.定义opener为全局opener后,可直接使用urlopen()请求response = urllib.request.urlopen(request)#8.打印回应print(response.read())