2、urllib库的使用详解

1、urllib2库的基本使用

所谓网页抓取,就是把URL地址中指定的网络资源从网络流中读取出来,保存到本地。 在Python中有不少库能够用来抓取网页,咱们先学习urllib2javascript

urllib2 是 Python2.7 自带的模块(不须要下载,导入便可使用)html

urllib2 官方文档:https://docs.python.org/2/library/urllib2.htmljava

urllib2 源码:https://hg.python.org/cpython/file/2.7/Lib/urllib2.pynode

urllib2 在 python3.x 中被改成urllib.requestpython

urlopen

咱们先来段代码:web

# urllib2_urlopen.py # 导入urllib2 库 import urllib2 # 向指定的url发送请求,并返回服务器响应的类文件对象 response = urllib2.urlopen("http://www.baidu.com") # 类文件对象支持 文件对象的操做方法,如read()方法读取文件所有内容,返回字符串 html = response.read() # 打印字符串 print html 

执行写的python代码,将打印结果ajax

Power@PowerMac ~$: python urllib2_urlopen.py

实际上,若是咱们在浏览器上打开百度主页, 右键选择“查看源代码”,你会发现,跟咱们刚才打印出来的是如出一辙。也就是说,上面的4行代码就已经帮咱们把百度的首页的所有代码爬了下来。算法

一个基本的url请求对应的python代码真的很是简单。json

Request

在咱们第一个例子里,urlopen()的参数就是一个url地址;python3.x

可是若是须要执行更复杂的操做,好比增长HTTP报头,必须建立一个 Request 实例来做为urlopen()的参数;而须要访问的url地址则做为 Request 实例的参数。

咱们编辑urllib2_request.py

# urllib2_request.py import urllib2 # url 做为Request()方法的参数,构造并返回一个Request对象 request = urllib2.Request("http://www.baidu.com") # Request对象做为urlopen()方法的参数,发送给服务器并接收响应 response = urllib2.urlopen(request) html = response.read() print html 
运行结果是彻底同样的:

新建Request实例,除了必需要有 url 参数以外,还能够设置另外两个参数:

  1. data(默认空):是伴随 url 提交的数据(好比要post的数据),同时 HTTP 请求将从 "GET"方式 改成 "POST"方式。

  2. headers(默认空):是一个字典,包含了须要发送的HTTP报头的键值对。

这两个参数下面会说到。

User-Agent

可是这样直接用urllib2给一个网站发送请求的话,确实略有些唐突了,就比如,人家每家都有门,你以一个路人的身份直接闯进去显然不是很礼貌。并且有一些站点不喜欢被程序(非人为访问)访问,有可能会拒绝你的访问请求。

可是若是咱们用一个合法的身份去请求别人网站,显然人家就是欢迎的,因此咱们就应该给咱们的这个代码加上一个身份,就是所谓的User-Agent头。

  • 浏览器 就是互联网世界上公认被容许的身份,若是咱们但愿咱们的爬虫程序更像一个真实用户,那咱们第一步,就是须要假装成一个被公认的浏览器。用不一样的浏览器在发送请求的时候,会有不一样的User-Agent头。 urllib2默认的User-Agent头为:Python-urllib/x.y(x和y是Python主版本和次版本号,例如 Python-urllib/2.7)
#urllib2_useragent.py import urllib2 url = "http://www.itcast.cn" #IE 9.0 的 User-Agent,包含在 ua_header里 ua_header = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} # url 连同 headers,一块儿构造Request请求,这个请求将附带 IE9.0 浏览器的User-Agent request = urllib2.Request(url, headers = ua_header) # 向服务器发送这个请求 response = urllib2.urlopen(request) html = response.read() print html 

添加更多的Header信息

在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。

能够经过调用Request.add_header() 添加/修改一个特定的header 也能够经过调用Request.get_header()来查看已有的header。

  • 添加一个特定的header
# urllib2_headers.py import urllib2 url = "http://www.itcast.cn" #IE 9.0 的 User-Agent header = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} request = urllib2.Request(url, headers = header) #也能够经过调用Request.add_header() 添加/修改一个特定的header request.add_header("Connection", "keep-alive") # 也能够经过调用Request.get_header()来查看header信息 # request.get_header(header_name="Connection") response = urllib2.urlopen(req) print response.code #能够查看响应状态码 html = response.read() print html 
  • 随机添加/修改User-Agent
# urllib2_add_headers.py import urllib2 import random url = "http://www.itcast.cn" ua_list = [ "Mozilla/5.0 (Windows NT 6.1; ) Apple.... ", "Mozilla/5.0 (X11; CrOS i686 2268.111.0)... ", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X.... ", "Mozilla/5.0 (Macintosh; Intel Mac OS... " ] user_agent = random.choice(ua_list) request = urllib2.Request(url) #也能够经过调用Request.add_header() 添加/修改一个特定的header request.add_header("User-Agent", user_agent) # 第一个字母大写,后面的所有小写 request.get_header("User-agent") response = urllib2.urlopen(req) html = response.read() print html


2、urllib2默认只支持HTTP/HTTPS的GETPOST方法

urllib.urlencode()

urllib 和 urllib2 都是接受URL请求的相关模块,可是提供了不一样的功能。两个最显著的不一样以下:
  • urllib 仅能够接受URL,不能建立 设置了headers 的Request 类实例;

  • 可是 urllib 提供 urlencode 方法用来GET查询字符串的产生,而 urllib2 则没有。(这是 urllib 和 urllib2 常常一块儿使用的主要缘由)

  • 编码工做使用urllib的urlencode()函数,帮咱们将key:value这样的键值对转换成"key=value"这样的字符串,解码工做可使用urllib的unquote()函数。(注意,不是urllib2.urlencode() )

# IPython2 中的测试结果 In [1]: import urllib In [2]: word = {"wd" : "传智播客"} # 经过urllib.urlencode()方法,将字典键值对按URL编码转换,从而能被web服务器接受。 In [3]: urllib.urlencode(word) Out[3]: "wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2" # 经过urllib.unquote()方法,把 URL编码字符串,转换回原先字符串。 In [4]: print urllib.unquote("wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2") wd=传智播客 
通常HTTP请求提交数据,须要编码成 URL编码格式,而后作为url的一部分,或者做为参数传到Request对象中。

Get方式

GET请求通常用于咱们向服务器获取数据,好比说,咱们用百度搜索传智播客https://www.baidu.com/s?wd=传智播客

浏览器的url会跳转成如图所示:

https://www.baidu.com/s?wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2

在其中咱们能够看到在请求部分里,http://www.baidu.com/s? 以后出现一个长长的字符串,其中就包含咱们要查询的关键词传智播客,因而咱们能够尝试用默认的Get方式来发送请求。

# urllib2_get.py import urllib #负责url编码处理 import urllib2 url = "http://www.baidu.com/s" word = {"wd":"传智播客"} word = urllib.urlencode(word) #转换成url编码格式(字符串) newurl = url + "?" + word # url首个分隔符就是 ? headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"} request = urllib2.Request(newurl, headers=headers) response = urllib2.urlopen(request) print response.read() 

批量爬取贴吧页面数据

首先咱们建立一个python文件, tiebaSpider.py,咱们要完成的是,输入一个百度贴吧的地址,好比:

百度贴吧LOL吧第一页:http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=0

第二页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=50

第三页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=100

发现规律了吧,贴吧中每一个页面不一样之处,就是url最后的pn的值,其他的都是同样的,咱们能够抓住这个规律。

简单写一个小爬虫程序,来爬取百度LOL吧的全部网页。
  • 先写一个main,提示用户输入要爬取的贴吧名,并用urllib.urlencode()进行转码,而后组合url,假设是lol吧,那么组合后的url就是:http://tieba.baidu.com/f?kw=lol
# 模拟 main 函数 if __name__ == "__main__": kw = raw_input("请输入须要爬取的贴吧:") # 输入起始页和终止页,str转成int类型 beginPage = int(raw_input("请输入起始页:")) endPage = int(raw_input("请输入终止页:")) url = "http://tieba.baidu.com/f?" key = urllib.urlencode({"kw" : kw}) # 组合后的url示例:http://tieba.baidu.com/f?kw=lol url = url + key tiebaSpider(url, beginPage, endPage) 
  • 接下来,咱们写一个百度贴吧爬虫接口,咱们须要传递3个参数给这个接口, 一个是main里组合的url地址,以及起始页码和终止页码,表示要爬取页码的范围。
def tiebaSpider(url, beginPage, endPage): """ 做用:负责处理url,分配每一个url去发送请求 url:须要处理的第一个url beginPage: 爬虫执行的起始页面 endPage: 爬虫执行的截止页面 """ for page in range(beginPage, endPage + 1): pn = (page - 1) * 50 filename = "第" + str(page) + "页.html" # 组合为完整的 url,而且pn值每次增长50 fullurl = url + "&pn=" + str(pn) #print fullurl # 调用loadPage()发送请求获取HTML页面 html = loadPage(fullurl, filename) # 将获取到的HTML页面写入本地磁盘文件 writeFile(html, filename) 
  • 咱们已经以前写出一个爬取一个网页的代码。如今,咱们能够将它封装成一个小函数loadPage,供咱们使用。
def loadPage(url, filename): ''' 做用:根据url发送请求,获取服务器响应文件 url:须要爬取的url地址 filename: 文件名 ''' print "正在下载" + filename headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} request = urllib2.Request(url, headers = headers) response = urllib2.urlopen(request) return response.read() 
  • 最后若是咱们但愿将爬取到了每页的信息存储在本地磁盘上,咱们能够简单写一个存储文件的接口。
def writeFile(html, filename): """ 做用:保存服务器响应文件到本地磁盘文件里 html: 服务器响应文件 filename: 本地磁盘文件名 """ print "正在存储" + filename with open(filename, 'w') as f: f.write(html) print "-" * 20 

其实不少网站都是这样的,同类网站下的html页面编号,分别对应网址后的网页序号,只要发现规律就能够批量爬取页面了。


POST方式:

上面咱们说了Request请求对象的里有data参数,它就是用在POST里的,咱们要传送的数据就是这个参数data,data是一个字典,里面要匹配键值对。

有道词典翻译网站:

输入测试数据,再经过使用Fiddler观察,其中有一条是POST请求,而向服务器发送的请求数据并非在url里,那么咱们能够试着模拟这个POST请求。

因而,咱们能够尝试用POST方式发送请求。

import urllib import urllib2 # POST请求的目标URL url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" headers={"User-Agent": "Mozilla...."} formdata = { "type":"AUTO", "i":"i love python", "doctype":"json", "xmlVersion":"1.8", "keyfrom":"fanyi.web", "ue":"UTF-8", "action":"FY_BY_ENTER", "typoResult":"true" } data = urllib.urlencode(formdata) request = urllib2.Request(url, data = data, headers = headers) response = urllib2.urlopen(request) print response.read() 
发送POST请求时,须要特别注意headers的一些属性:

Content-Length: 144: 是指发送的表单数据长度为144,也就是字符个数是144个。

X-Requested-With: XMLHttpRequest :表示Ajax异步请求。

Content-Type: application/x-www-form-urlencoded : 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。

获取AJAX加载的内容

有些网页内容使用AJAX加载,只要记得,AJAX通常返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了。

"做为一名爬虫工程师,你最须要关注的,是数据的来源"

import urllib import urllib2 # demo1 url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action" headers={"User-Agent": "Mozilla...."} # 变更的是这两个参数,从start开始日后显示limit个 formdata = { 'start':'0', 'limit':'10' } data = urllib.urlencode(formdata) request = urllib2.Request(url, data = data, headers = headers) response = urllib2.urlopen(request) print response.read() # demo2 url = "https://movie.douban.com/j/chart/top_list?" headers={"User-Agent": "Mozilla...."} # 处理全部参数 formdata = { 'type':'11', 'interval_id':'100:90', 'action':'', 'start':'0', 'limit':'10' } data = urllib.urlencode(formdata) request = urllib2.Request(url, data = data, headers = headers) response = urllib2.urlopen(request) print response.read() 

问题:为何有时候POST也能在URL内看到数据?

  • GET方式是直接以连接形式访问,连接中包含了全部的参数,服务器端用Request.QueryString获取变量的值。若是包含了密码的话是一种不安全的选择,不过你能够直观地看到本身提交了什么内容。

  • POST则不会在网址上显示全部的参数,服务器端用Request.Form获取提交的数据,在Form提交的时候。可是HTML代码里若是不指定 method 属性,则默认为GET请求,Form中提交的数据将会附加在url以后,以?分开与url分开。

  • 表单数据能够做为 URL 字段(method="get")或者 HTTP POST (method="post")的方式来发送。好比在下面的HTML代码中,表单数据将由于 (method="get") 而附加到 URL 上:

<form action="form_action.asp" method="get"> <p>First name: <input type="text" name="fname" /></p> <p>Last name: <input type="text" name="lname" /></p> <input type="submit" value="Submit" /> </form>

处理HTTPS请求 SSL证书验证

如今随处可见 https 开头的网站,urllib2能够为 HTTPS 请求验证SSL证书,就像web浏览器同样,若是网站的SSL证书是通过CA认证的,则可以正常访问,如:https://www.baidu.com/等...

若是SSL证书验证不经过,或者操做系统不信任服务器的安全证书,好比浏览器在访问12306网站如:https://www.12306.cn/mormhweb/的时候,会警告用户证书不受信任。(听说 12306 网站证书是本身作的,没有经过CA认证)

 

urllib2在访问的时候则会报出SSLError:

import urllib2 url = "https://www.12306.cn/mormhweb/" headers = {"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"} request = urllib2.Request(url, headers = headers) response = urllib2.urlopen(request) print response.read() 

运行结果:

urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>

因此,若是之后遇到这种网站,咱们须要单独处理SSL证书,让程序忽略SSL证书验证错误,便可正常访问。

import urllib import urllib2 # 1. 导入Python SSL处理模块 import ssl # 2. 表示忽略未经核实的SSL证书认证 context = ssl._create_unverified_context() url = "https://www.12306.cn/mormhweb/" headers = {"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"} request = urllib2.Request(url, headers = headers) # 3. 在urlopen()方法里 指明添加 context 参数 response = urllib2.urlopen(request, context = context) print response.read() 

关于CA

CA(Certificate Authority)是数字证书认证中心的简称,是指发放、管理、废除数字证书的受信任的第三方机构,如北京数字认证股份有限公司上海市数字证书认证中心有限公司等...

CA的做用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理。

现实生活中能够用身份证来证实身份, 那么在网络世界里,数字证书就是身份证。和现实生活不一样的是,并非每一个上网的用户都有数字证书的,每每只有当一我的须要证实本身的身份的时候才须要用到数字证书。

普通用户通常是不须要,由于网站并不关心是谁访问了网站,如今的网站只关心流量。可是反过来,网站就须要证实本身的身份了。

好比说如今钓鱼网站不少的,好比你想访问的是www.baidu.com,但其实你访问的是www.daibu.com”,因此在提交本身的隐私信息以前须要验证一下网站的身份,要求网站出示数字证书。

通常正常的网站都会主动出示本身的数字证书,来确保客户端和网站服务器之间的通讯数据是加密安全的。

 

3、Handler处理器 和 自定义Opener

  • opener是 urllib2.OpenerDirector 的实例,咱们以前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮咱们构建好的)。

  • 可是基本的urlopen()方法不支持代理、cookie等其余的HTTP/HTTPS高级功能。因此要支持这些功能:

    1. 使用相关的 Handler处理器 来建立特定功能的处理器对象;
    2. 而后经过 urllib2.build_opener()方法使用这些处理器对象,建立自定义opener对象;
    3. 使用自定义的opener对象,调用open()方法发送请求。
  • 若是程序里全部的请求都使用自定义的opener,可使用urllib2.install_opener() 将自定义的 opener 对象 定义为 全局opener,表示若是以后凡是调用urlopen,都将使用这个opener(根据本身的需求来选择)

简单的自定义opener()

import urllib2 # 构建一个HTTPHandler 处理器对象,支持处理HTTP请求 http_handler = urllib2.HTTPHandler() # 构建一个HTTPHandler 处理器对象,支持处理HTTPS请求 # http_handler = urllib2.HTTPSHandler() # 调用urllib2.build_opener()方法,建立支持处理HTTP请求的opener对象 opener = urllib2.build_opener(http_handler) # 构建 Request请求 request = urllib2.Request("http://www.baidu.com/") # 调用自定义opener对象的open()方法,发送request请求 response = opener.open(request) # 获取服务器响应内容 print response.read() 

这种方式发送请求获得的结果,和使用urllib2.urlopen()发送HTTP/HTTPS请求获得的结果是同样的。

若是在 HTTPHandler()增长 debuglevel=1参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时能够省去抓包的工做。

# 仅须要修改的代码部分: # 构建一个HTTPHandler 处理器对象,支持处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0 http_handler = urllib2.HTTPHandler(debuglevel=1) # 构建一个HTTPHSandler 处理器对象,支持处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0 https_handler = urllib2.HTTPSHandler(debuglevel=1) 

ProxyHandler处理器(代理设置)

使用代理IP,这是爬虫/反爬虫的第二大招,一般也是最好用的。

不少网站会检测某一段时间某个IP的访问次数(经过流量统计,系统日志等),若是访问次数多的不像正常人,它会禁止这个IP的访问。

因此咱们能够设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然能够换个IP继续爬取。

urllib2中经过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:

#urllib2_proxy1.py import urllib2 # 构建了两个代理Handler,一个有代理IP,一个没有代理IP httpproxy_handler = urllib2.ProxyHandler({"http" : "124.88.67.81:80"}) nullproxy_handler = urllib2.ProxyHandler({}) proxySwitch = True #定义一个代理开关 # 经过 urllib2.build_opener()方法使用这些代理Handler对象,建立自定义opener对象 # 根据代理开关是否打开,使用不一样的代理模式 if proxySwitch: opener = urllib2.build_opener(httpproxy_handler) else: opener = urllib2.build_opener(nullproxy_handler) request = urllib2.Request("http://www.baidu.com/") # 1. 若是这么写,只有使用opener.open()方法发送请求才使用自定义的代理,而urlopen()则不使用自定义代理。 response = opener.open(request) # 2. 若是这么写,就是将opener应用到全局,以后全部的,无论是opener.open()仍是urlopen() 发送请求,都将使用自定义代理。 # urllib2.install_opener(opener) # response = urlopen(request) print response.read() 

免费的开放代理获取基本没有成本,咱们能够在一些代理网站上收集这些免费代理,测试后若是能够用,就把它收集起来用在爬虫上面。

免费短时间代理网站举例:

若是代理IP足够多,就能够像随机获取User-Agent同样,随机选择一个代理去访问网站。

import urllib2 import random proxy_list = [ {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"}, {"http" : "124.88.67.81:80"} ] # 随机选择一个代理 proxy = random.choice(proxy_list) # 使用选择的代理构建代理处理器对象 httpproxy_handler = urllib2.ProxyHandler(proxy) opener = urllib2.build_opener(httpproxy_handler) request = urllib2.Request("http://www.baidu.com/") response = opener.open(request) print response.read() 

可是,这些免费开放代理通常会有不少人都在使用,并且代理有寿命短,速度慢,匿名度不高,HTTP/HTTPS支持不稳定等缺点(免费没好货)。

因此,专业爬虫工程师或爬虫公司会使用高品质的私密代理,这些代理一般须要找专门的代理供应商购买,再经过用户名/密码受权使用(舍不得孩子套不到狼)。

HTTPPasswordMgrWithDefaultRealm()

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

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

ProxyBasicAuthHandler(代理受权验证)

若是咱们使用以前的代码来使用私密代理,会报 HTTP 407 错误,表示代理没有经过身份验证:

urllib2.HTTPError: HTTP Error 407: Proxy Authentication Required

因此咱们须要改写代码,经过:

  • HTTPPasswordMgrWithDefaultRealm():来保存私密代理的用户密码
  • ProxyBasicAuthHandler():来处理代理的身份验证。
#urllib2_proxy2.py import urllib2 import urllib # 私密代理受权的帐户 user = "mr_mao_hacker" # 私密代理受权的密码 passwd = "sffqry9r" # 私密代理 IP proxyserver = "61.158.163.130:16816" # 1. 构建一个密码管理对象,用来保存须要处理的用户名和密码 passwdmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() # 2. 添加帐户信息,第一个参数realm是与远程服务器相关的域信息,通常没人管它都是写None,后面三个参数分别是 代理服务器、用户名、密码 passwdmgr.add_password(None, proxyserver, user, passwd) # 3. 构建一个代理基础用户名/密码验证的ProxyBasicAuthHandler处理器对象,参数是建立的密码管理对象 # 注意,这里再也不使用普通ProxyHandler类了 proxyauth_handler = urllib2.ProxyBasicAuthHandler(passwdmgr) # 4. 经过 build_opener()方法使用这些代理Handler对象,建立自定义opener对象,参数包括构建的 proxy_handler 和 proxyauth_handler opener = urllib2.build_opener(proxyauth_handler) # 5. 构造Request 请求 request = urllib2.Request("http://www.baidu.com/") # 6. 使用自定义opener发送请求 response = opener.open(request) # 7. 打印响应内容 print response.read() 

HTTPBasicAuthHandler处理器(Web客户端受权验证)

有些Web服务器(包括HTTP/FTP等)访问时,须要进行用户身份验证,爬虫直接访问会报HTTP 401 错误,表示访问身份未经受权:

urllib2.HTTPError: HTTP Error 401: Unauthorized

若是咱们有客户端的用户名和密码,咱们能够经过下面的方法去访问爬取:

import urllib import urllib2 # 用户名 user = "test" # 密码 passwd = "123456" # Web服务器 IP webserver = "http://192.168.199.107" # 1. 构建一个密码管理对象,用来保存须要处理的用户名和密码 passwdmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() # 2. 添加帐户信息,第一个参数realm是与远程服务器相关的域信息,通常没人管它都是写None,后面三个参数分别是 Web服务器、用户名、密码 passwdmgr.add_password(None, webserver, user, passwd) # 3. 构建一个HTTP基础用户名/密码验证的HTTPBasicAuthHandler处理器对象,参数是建立的密码管理对象 httpauth_handler = urllib2.HTTPBasicAuthHandler(passwdmgr) # 4. 经过 build_opener()方法使用这些代理Handler对象,建立自定义opener对象,参数包括构建的 proxy_handler opener = urllib2.build_opener(httpauth_handler) # 5. 能够选择经过install_opener()方法定义opener为全局opener urllib2.install_opener(opener) # 6. 构建 Request对象 request = urllib2.Request("http://192.168.199.107") # 7. 定义opener为全局opener后,可直接使用urlopen()发送请求 response = urllib2.urlopen(request) # 8. 打印响应内容 print response.read() 

Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie能够保持登陆信息到用户下次与服务器的会话。

Cookie原理

HTTP是无状态的面向链接的协议, 为了保持链接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性,包括:

Cookie名字(Name)
Cookie的值(Value)
Cookie的过时时间(Expires/Max-Age)
Cookie做用路径(Path)
Cookie所在域名(Domain),
使用Cookie进行安全链接(Secure)。

前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不一样浏览器对Cookie个数及大小限制是有差别的)。

Cookie由变量名和值组成,根据 Netscape公司的规定,Cookie格式以下:

Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE

Cookie应用

Cookies在爬虫方面最典型的应用是断定注册用户是否已经登陆网站,用户可能会获得提示,是否在下一次进入此网站时保留用户信息以便简化登陆手续。

# 获取一个有登陆信息的Cookie模拟登录 import urllib2 # 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是保存了密码无需重复登陆的用户的Cookie,这个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对象 urllib2.Request("http://www.renren.com/", headers = headers) # 3. 直接访问renren主页,服务器会根据headers报头信息(主要是Cookie信息),判断这是一个已经登陆的用户,并返回相应的页面 response = urllib2.urlopen(request) # 4. 打印响应内容 print response.read()

可是这样作太过复杂,咱们先须要在浏览器登陆帐户,而且设置保存密码,而且经过抓包才能获取这个Cookie,那有么有更简单方便的方法呢?

cookielib库 和 HTTPCookieProcessor处理器

在Python处理Cookie,通常是经过cookielib模块和 urllib2模块的HTTPCookieProcessor处理器类一块儿使用。

cookielib模块:主要做用是提供用于存储cookie的对象

HTTPCookieProcessor处理器:主要做用是处理这些cookie对象,并构建handler对象。

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

咱们来作几个案例:

1)获取Cookie,并保存到CookieJar()对象中
# urllib2_cookielibtest1.py import urllib2 import cookielib # 构建一个CookieJar对象实例来保存cookie cookiejar = cookielib.CookieJar() # 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象 handler=urllib2.HTTPCookieProcessor(cookiejar) # 经过 build_opener() 来构建opener opener = urllib2.build_opener(handler) # 4. 以get方法访问页面,访问以后会自动保存cookie到cookiejar中 opener.open("http://www.baidu.com") ## 能够按标准格式将保存的Cookie打印出来 cookieStr = "" for item in cookiejar: cookieStr = cookieStr + item.name + "=" + item.value + ";" ## 舍去最后一位的分号 print cookieStr[:-1] 

咱们使用以上方法将Cookie保存到cookiejar对象中,而后打印出了cookie中的值,也就是访问百度首页的Cookie值。

运行结果以下:

BAIDUID=4327A58E63A92B73FF7A297FB3B2B4D0:FG=1;BIDUPSID=4327A58E63A92B73FF7A297FB3B2B4D0;H_PS_PSSID=1429_21115_17001_21454_21409_21554_21398;PSTM=1480815736;BDSVRTM=0;BD_HOME=0
2. 访问网站得到cookie,并把得到的cookie保存在cookie文件中
# urllib2_cookielibtest2.py import cookielib import urllib2 # 保存cookie的本地磁盘文件名 filename = 'cookie.txt' # 声明一个MozillaCookieJar(有save实现)对象实例来保存cookie,以后写入文件 cookiejar = cookielib.MozillaCookieJar(filename) # 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象 handler = urllib2.HTTPCookieProcessor(cookiejar) # 经过 build_opener() 来构建opener opener = urllib2.build_opener(handler) # 建立一个请求,原理同urllib2的urlopen response = opener.open("http://www.baidu.com") # 保存cookie到本地文件 cookiejar.save() 
3. 从文件中获取cookies,作为请求的一部分去访问
# urllib2_cookielibtest2.py import cookielib import urllib2 # 建立MozillaCookieJar(有load实现)实例对象 cookiejar = cookielib.MozillaCookieJar() # 从文件中读取cookie内容到变量 cookie.load('cookie.txt') # 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象 handler = urllib2.HTTPCookieProcessor(cookiejar) # 经过 build_opener() 来构建opener opener = urllib2.build_opener(handler) response = opener.open("http://www.baidu.com") 

利用cookielib和post登陆人人网

import urllib import urllib2 import cookielib # 1. 构建一个CookieJar对象实例来保存cookie cookie = cookielib.CookieJar() # 2. 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象 cookie_handler = urllib2.HTTPCookieProcessor(cookie) # 3. 经过 build_opener() 来构建opener opener = urllib2.build_opener(cookie_handler) # 4. addheaders 接受一个列表,里面每一个元素都是一个headers信息的元祖, opener将附带headers信息 opener.addheaders = [("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")] # 5. 须要登陆的帐户和密码 data = {"email":"mr_mao_hacker@163.com", "password":"alaxxxxxime"} # 6. 经过urlencode()转码 postdata = urllib.urlencode(data) # 7. 构建Request请求对象,包含须要发送的用户名和密码 request = urllib2.Request("http://www.renren.com/PLogin.do", data = postdata) # 8. 经过opener发送这个请求,并获取登陆后的Cookie值, opener.open(request) # 9. opener包含用户登陆后的Cookie值,能够直接访问那些登陆后才能够访问的页面 response = opener.open("http://www.renren.com/410043129/profile") # 10. 打印响应内容 print response.read() 

模拟登陆要注意几点:

  1. 登陆通常都会先有一个HTTP GET,用于拉取一些信息及得到Cookie,而后再HTTP POST登陆。
  2. HTTP POST登陆的连接有多是动态的,从GET返回的信息中获取。
  3. password 有些是明文发送,有些是加密后发送。有些网站甚至采用动态加密的,同时包括了不少其余数据的加密信息,只能经过查看JS源码得到加密算法,再去破解加密,很是困难。
  4. 大多数网站的登陆总体流程是相似的,可能有些细节不同,因此不能保证其余网站登陆成功。
这个测试案例中,为了想让你们快速理解知识点,咱们使用的人人网登陆接口是人人网改版前的隐藏接口(嘘....),登陆比较方便。
固然,咱们也能够直接发送帐号密码到登陆界面模拟登陆,可是当网页采用JavaScript动态技术之后,想封锁基于 HttpClient 的模拟登陆就太容易了,甚至能够根据你的鼠标活动的特征准确地判断出是否是真人在操做。
因此,想作通用的模拟登陆还得选别的技术,好比用内置浏览器引擎的爬虫(关键词:Selenium ,PhantomJS),这个咱们将在之后会学习到。
 

3、urllib2 的异常错误处理

在咱们用urlopen或opener.open方法发出一个请求时,若是urlopen或opener.open不能处理这个response,就产生错误。

这里主要说的是URLError和HTTPError,以及对它们的错误处理。

URLError

URLError 产生的缘由主要有:

  1. 没有网络链接
  2. 服务器链接失败
  3. 找不到指定的服务器

咱们能够用try except语句来捕获相应的异常。下面的例子里咱们访问了一个不存在的域名:

# urllib2_urlerror.py import urllib2 requset = urllib2.Request('http://www.ajkfhafwjqh.com') try: urllib2.urlopen(request, timeout=5) except urllib2.URLError, err: print err 

运行结果以下:

<urlopen error [Errno 8] nodename nor servname provided, or not known> 

urlopen error,错误代码8,错误缘由是没有找到指定的服务器。

HTTPError

HTTPError是URLError的子类,咱们发出一个请求时,服务器上都会对应一个response应答对象,其中它包含一个数字"响应状态码"。

若是urlopen或opener.open不能处理的,会产生一个HTTPError,对应相应的状态码,HTTP状态码表示HTTP协议所返回的响应的状态。

注意,urllib2能够为咱们处理重定向的页面(也就是3开头的响应码),100-299范围的号码表示成功,因此咱们只能看到400-599的错误号码。

# urllib2_httperror.py import urllib2 requset = urllib2.Request('http://blog.baidu.com/itcast') try: urllib2.urlopen(requset) except urllib2.HTTPError, err: print err.code print err 

运行结果以下:

404 HTTP Error 404: Not Found 

HTTP Error,错误代号是404,错误缘由是Not Found,说明服务器没法找到被请求的页面。

一般产生这种错误的,要么url不对,要么ip被封。

改进版

因为HTTPError的父类是URLError,因此父类的异常应当写到子类异常的后面,因此上述的代码能够这么改写:

# urllib2_botherror.py import urllib2 requset = urllib2.Request('http://blog.baidu.com/itcast') try: urllib2.urlopen(requset) except urllib2.HTTPError, err: print err.code except urllib2.URLError, err: print err else: print "Good Job" 

运行结果以下:

404 
这样咱们就能够作到,首先捕获子类的异常,若是子类捕获不到,那么能够捕获父类的异常。

HTTP响应状态码参考:

1xx:信息 100 Continue 服务器仅接收到部分请求,可是一旦服务器并无拒绝该请求,客户端应该继续发送其他的请求。 101 Switching Protocols 服务器转换协议:服务器将听从客户的请求转换到另一种协议。 2xx:成功 200 OK 请求成功(其后是对GET和POST请求的应答文档) 201 Created 请求被建立完成,同时新的资源被建立。 202 Accepted 供处理的请求已被接受,可是处理未完成。 203 Non-authoritative Information 文档已经正常地返回,但一些应答头可能不正确,由于使用的是文档的拷贝。 204 No Content 没有新文档。浏览器应该继续显示原来的文档。若是用户按期地刷新页面,而Servlet能够肯定用户文档足够新,这个状态代码是颇有用的。 205 Reset Content 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它。 3xx:重定向 300 Multiple Choices 多重选择。连接列表。用户能够选择某连接到达目的地。最多容许五个地址。 301 Moved Permanently 所请求的页面已经转移至新的url。 302 Moved Temporarily 所请求的页面已经临时转移至新的url。 303 See Other 所请求的页面可在别的url下被找到。 304 Not Modified 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(通常是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还能够继续使用。 305 Use Proxy 客户请求的文档应该经过Location头所指明的代理服务器提取。 306 Unused 此代码被用于前一版本。目前已再也不使用,可是代码依然被保留。 307 Temporary Redirect 被请求的页面已经临时移至新的url。 4xx:客户端错误 400 Bad Request 服务器未能理解请求。 401 Unauthorized 被请求的页面须要用户名和密码。 401.1 登陆失败。 401.2 服务器配置致使登陆失败。 401.3 因为 ACL 对资源的限制而未得到受权。 401.4 筛选器受权失败。 401.5 ISAPI/CGI 应用程序受权失败。 401.7 访问被 Web 服务器上的 URL 受权策略拒绝。这个错误代码为 IIS 6.0 所专用。 402 Payment Required 此代码尚没法使用。 403 Forbidden 对被请求页面的访问被禁止。 403.1 执行访问被禁止。 403.2 读访问被禁止。 403.3 写访问被禁止。 403.4 要求 SSL。 403.5 要求 SSL 128。 403.6 IP 地址被拒绝。 403.7 要求客户端证书。 403.8 站点访问被拒绝。 403.9 用户数过多。 403.10 配置无效。 403.11 密码更改。 403.12 拒绝访问映射表。 403.13 客户端证书被吊销。 403.14 拒绝目录列表。 403.15 超出客户端访问许可。 403.16 客户端证书不受信任或无效。 403.17 客户端证书已过时或还没有生效。 403.18 在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。 403.19 不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。 403.20 Passport 登陆失败。这个错误代码为 IIS 6.0 所专用。 404 Not Found 服务器没法找到被请求的页面。 404.0 没有找到文件或目录。 404.1 没法在所请求的端口上访问 Web 站点。 404.2 Web 服务扩展锁定策略阻止本请求。 404.3 MIME 映射策略阻止本请求。 405 Method Not Allowed 请求中指定的方法不被容许。 406 Not Acceptable 服务器生成的响应没法被客户端所接受。 407 Proxy Authentication Required 用户必须首先使用代理服务器进行验证,这样请求才会被处理。 408 Request Timeout 请求超出了服务器的等待时间。 409 Conflict 因为冲突,请求没法被完成。 410 Gone 被请求的页面不可用。 411 Length Required "Content-Length" 未被定义。若是无此内容,服务器不会接受请求。 412 Precondition Failed 请求中的前提条件被服务器评估为失败。 413 Request Entity Too Large 因为所请求的实体的太大,服务器不会接受请求。 414 Request-url Too Long 因为url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种状况。 415 Unsupported Media Type 因为媒介类型不被支持,服务器不会接受请求。 416 Requested Range Not Satisfiable 服务器不能知足客户在请求中指定的Range头。 417 Expectation Failed 执行失败。 423 锁定的错误。 5xx:服务器错误 500 Internal Server Error 请求未完成。服务器遇到不可预知的状况。 500.12 应用程序正忙于在 Web 服务器上从新启动。 500.13 Web 服务器太忙。 500.15 不容许直接请求 Global.asa。 500.16 UNC 受权凭据不正确。这个错误代码为 IIS 6.0 所专用。 500.18 URL 受权存储不能打开。这个错误代码为 IIS 6.0 所专用。 500.100 内部 ASP 错误。 501 Not Implemented 请求未完成。服务器不支持所请求的功能。 502 Bad Gateway 请求未完成。服务器从上游服务器收到一个无效的响应。 502.1 CGI 应用程序超时。 · 502.2 CGI 应用程序出错。 503 Service Unavailable 请求未完成。服务器临时过载或当机。 504 Gateway Timeout 网关超时。 505 HTTP Version Not Supported 服务器不支持请求中指明的HTTP协议版本
相关文章
相关标签/搜索