Python爬虫编程思想(11):用urllib请求基础验证页面
Posted 蒙娜丽宁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python爬虫编程思想(11):用urllib请求基础验证页面相关的知识,希望对你有一定的参考价值。
有一些页面并不是我们想访问就能访问的,这些页面都会将各种验证,例如,在第一次访问网页之前,会先弹出如图1所示的登录对话框,只有正确输入用户名和密码才能访问页面,否则会直接跳到错误页面。
这种验证被称为基础验证,是HTTP验证的一种。输入的用户名和密码会通过Authorization请求头字段发送给服务端,在访问给页面时,如果服务端检测到HTTP请求头中没有Authorization字段时,会设置名为WWW-Authenticate的HTTP响应头字段,同时返回401响应码。WWW-Authenticate字段值的格式如下:
'Basic realm=需要验证的范围'
其中Basic表示基础验证,也就是需要输入用户名和密码。realm部分表示需要验证的范围,是一个字符串,详细的用法在本节后面的部分介绍。
当浏览器遇到401状态码,并检测到是基础验证时就会弹出如图3-8所示的验证对话框,正确输入用户名和密码后,浏览器会向服务端发送Authorization请求头字段,该字段的值是Basic encode,其中encode是用户名和密码的Base64编码。由于基础验证的用户名和密码都是以明文发送(尽管被进行了Base64编码,但Base64编码是可逆的)的,所以建议该页面使用HTTPS,这样传输的数据就会使用SSL加密,以保证数据传输过程的安全。
下面的例子使用Flask编写了一个Web服务器,用于模拟基础验证页面。并使用普通设置HTTP请求头的方式和Handler方式请求这个基础验证页面。
Flask是基于Python的Web框架,可以用于编写Web应用程序,在使用之前需要使用如下的命令安装。
pip install flask
本例编写的第一个程序是这个支持基础验证的Web服务器,代码如下:
from flask import Flask
from flask import request
import base64
app = Flask(__name__)
# 判断客户端是否提交了用户名和密码,如果未提交,设置状态码为401,并设置WWW-Authenticate响应头
# auth:Authorization请求头字段的值, response:响应对象
def hasAuth(auth,response):
if auth == None or auth.strip() == "":
# 设置响应状态码为401
response.status_code = 401
# 设置响应头的WWW-Authenticate字段,其中localhost是需要验证的范围
response.headers["WWW-Authenticate"] = 'Basic realm="localhost"'
# 返回False,表示客户端未提交用户名和密码
return False
return True
# 根路由
@app.route("/")
def index():
# 创建响应对象,并指定未输入用户名和密码(单击”取消“按钮)或输入错误后的返回内容
response = app.make_response('username or password error')
# 输出所有的HTTP请求头
print(request.headers)
# 得到Authorization请求头的值
auth = request.headers.get('Authorization')
# 输出Authorization请求头的值
print('Authorization:',auth)
# 如果客户端提交了用户名和密码,进行验证
if hasAuth(auth, response):
# 将用户名和密码按Base64编码格式解码,这里按空格拆分成两个值,第一个是Basic,第二个是
# Base64编码后的用户名和密码
auth = str(base64.b64decode(auth.split(' ')[1]),'utf-8')
# 用户名和密码之间用冒号(:)分隔,所以需要将它们拆开
values = auth.split(':')
# 获取用户名
username = values[0]
# 获取密码
password = values[1]
print('username:',username)
print('password:',password)
# 判断用户名和密码是否正确,如果正确,返回success
if username == 'bill' and password == '1234':
return "success"
return response
if __name__ == '__main__':
app.run()
本例为了方便,将用户名和密码分别固定为bill和1234,现在运行AuthServer.py,然后在浏览器中输入http://127.0.0.1:5000,就会弹出如图3-8所示的验证对话框,正确输入用户名和密码后,浏览器会输出success。当再次访问http://127.0.0.1:5000时,浏览器会使用刚才输入的用户名和密码通过Authorization请求头字段发送给服务端,所以不管用户名和密码输入的是否正确,只要单击了“登录”按钮,下次再访问给页面时都不会再弹出如图3-8所示的登录验证对话框。由于用户名和密码都是通过临时Cookie保存的,所以一旦浏览器关闭,再次打开后,访问http://127.0.0.1:5000仍然会弹出如图3-8所示的登录验证对话框。
由于用户名和密码是通过HTTP请求头的Authorization字段发送给服务端的,所以在访问该页面时可以直接设置Authorization字段。
from urllib import request
import base64
url = 'http://localhost:5000'
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
'Host':'localhost:5000',
# 设置Authorization请求字段头,并指定Base64编码的用户名和密码
'Authorization': 'Basic ' +
str(base64.b64encode(bytes('bill:1234','utf-8')),'utf-8')
}
req = request.Request(url = url,headers=headers,method="GET")
response=request.urlopen(req)
print(response.read().decode('utf-8'))
执行这段代码后,会在Console中输出success。AuthServer在Console中也会输出如图2所示的信息。
urllib中提供了一些Handler类,这些类可以用来处理各种类型的页面请求,这些类通常都是BaseHandler的子类,例如,HTTPBasicAuthHandler用于处理管理认证,HTTPCookieProcessor用于处理Cookie。本例会使用HTTPBasicAuthHandler处理页面的基础验证。
使用HTTPBasicAuthHandler的关键是build_opener函数,该函数与urlopen类似,只是可以完成更复杂的工作。实际上,urlopen函数相当于类库为你封装好了及其常用的请求方法,利用它们可以完成基本的请求,但如果要完成更复杂的请求,就需要使用build_opener函数。除此之外,还需要使用HTTPPasswordMgrWithDefaultRealm对象封装请求字段数据,也就是用户名、密码和realm。
from urllib.request import
HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
from urllib.error import URLError
username = 'bill'
password = '1234'
url = 'http://localhost:5000'
p = HTTPPasswordMgrWithDefaultRealm()
# 封装realm,url、用户名和密码
p.add_password('localhost',url,username,password)
auth_handler = HTTPBasicAuthHandler(p)
# 发送HTTP请求
opener = build_opener(auth_handler)
result = opener.open(url)
# 获取服务端响应数据
html = result.read().decode('utf-8')
print(html)
运行程序,在Console中会输出success。在AuthServer的Console中会输出如图3所示的信息。
从图3的输出内容可以看出,客户端发送了两次HTTP请求,第1次没有Authorization字段,这是服务端会返回401错误,如果客户端是浏览器,那么就会弹出如图1所示的验证对话框,但现在客户端是爬虫,所以干脆直接提供现成的用户名和密码,这就是第2次HTTP请求的目的。
add_password函数的第1个参数是realm,如果指定这个参数,那么必须与服务端设置WWW-Authenticate字段时指定的realm相同,本例是localhost。如果add_password函数的第1个参数是None,那么服务端会忽略realm。如果指定的realm不一致,就会抛出如图4所示的异常。
所以为了安全起见,在发送HTTP请求时可以用try...except语句,以避免程序崩溃。
try:
result = opener.open(url)
html = result.read().decode('utf-8')
print(html)
except URLError as e:
print(e.reason)
以上是关于Python爬虫编程思想(11):用urllib请求基础验证页面的主要内容,如果未能解决你的问题,请参考以下文章
Python爬虫编程思想:使用urllib库发送HTTP请求和接收响应
Python爬虫编程思想(19):使用urllib3上传文件与处理超时
Python爬虫编程思想(17):使用urllib3发送HTTP Get和HTTP POST请求
Python爬虫编程思想(10):通过urllib设置HTTP请求头