博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
爬虫入门之handler与opener(三)
阅读量:6296 次
发布时间:2019-06-22

本文共 14156 字,大约阅读时间需要 47 分钟。

1 自定义opener

  • 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)

2 Cookie概述

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,那有么有更简单方便的方法呢?

3 cookielib库和HTTPCookieProcessor处理器

在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库的配置流程:

  1. 创建一个CookieJar对象
  2. 使用cookiejar对象,创建一个handler对象
  3. 使用handler创建一个opener
  4. 通过opener登录
  5. handler会自动的保存登录之后的cookie

    • 获取Cookie,并保存到CookieJar()对象中
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
  • 访问网站获取cookie值,并把获得的cookie保存在cookie文件中
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至本地
  • ##### 从文件中获取cookies,做为请求的一部分去访问
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)   #打开请求

4 HTTP代理Fidder

(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())

5 ProxyHandler处理器

(1) 代理 IP

使用代理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
(2)HTTPPasswordMgrWithDefaultRealm()

HTTPPasswordMgrWithDefaultRealm()类创建一个密码管理对象,用来保存HTTP请求相关的用户名和密码,主要应用两个场景:

  1. 验证代理授权的用户名和密码(ProxyBasicAuthHandler())
  2. 验证web客户端的用户名和密码(HTTPBasicAuthHandler())

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())
你可能感兴趣的文章
Python系语言发展综述
查看>>
新手 开博
查看>>
借助开源工具高效完成Java应用的运行分析
查看>>
163 yum
查看>>
第三章:Shiro的配置——深入浅出学Shiro细粒度权限开发框架
查看>>
80后创业的经验谈(转,朴实但实用!推荐)
查看>>
让Windows图片查看器和windows资源管理器显示WebP格式
查看>>
我的友情链接
查看>>
vim使用点滴
查看>>
embedded linux学习中几个需要明确的概念
查看>>
mysql常用语法
查看>>
Morris ajax
查看>>
【Docker学习笔记(四)】通过Nginx镜像快速搭建静态网站
查看>>
ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
查看>>
<转>云主机配置OpenStack使用spice的方法
查看>>
java jvm GC 各个区内存参数设置
查看>>
[使用帮助] PHPCMS V9内容模块PC标签调用说明
查看>>
基于RBAC权限管理
查看>>
数学公式的英语读法
查看>>
留德十年
查看>>