请求 urllib.request
urlopen
- 默认发起
get
请求,当请求中传入data
参数时,则会发起post
请求
from urllib import request
resp = request.urlopen(url,
data=None,
[timeout, ]*,
cafile=None,
capath=None,
cadefault=False,
context=None)
(1)url:要请求的网址
www.baidu.com
- 在url中,是只能使用ASCII中包含的字符的,也就是说,ASCII不包含的
特殊字符
,以及中文
等字符都是不可以在url中使用的。而我们有时候又有将中文字符加入到url中的需求,例如百度的搜索地址:https://www.baidu.com/s?wd=机械舞
>>> from urllib import parse
>>> keyword = "机械舞"
>>> data = parse.quote(keyword)
>>> data
'%E6%9C%BA%E6%A2%B0%E8%88%9E'
>>> parse.unquote(data)
'机械舞'
>>>
from urllib import request
from urllib import parse
url = 'https://www.baidu.com/s?'
keywords = input("请输入要查询的内容:")
data = parse.quote(keywords)
fullurl = url + "wd="+data
- 在访问url时,我们常常需要传递
很多
的url参数
,而如果用字符串的方法去拼接url的话,会比较麻烦,所以urllib中提供了urlencode
这个方法来拼接url参数
>>> keyword = {"wd": "机械舞"}
>>> data = parse.urlencode(keyword)
>>> data
'wd=%E6%9C%BA%E6%A2%B0%E8%88%9E'
from urllib import request
from urllib import parse
url = 'https://www.baidu.com/s?'
keywords = input("请输入要查询的内容:")
kw = {"wd": keywords}
kw = parse.urlencode(kw)
# 将查询关键字进行url编码
fullurl = url + kw
resp = request.urlopen(fullurl)
resp.read().decode()
(2)data:请求需要参数
- urllib库中orlopen方法中请求参数data要求为
bytes
格式
from urllib import parse
from urllib import request
url = 'wwww.taobao.com/login'
formdata = {
"username": "admin",
"password": "admin123"
}
data = parse.urlencode(formdata).encode('utf-8')
或
data = bytes(parse.urlencode(formdata), encoding='utf-8')
resp = request.urlopen(url, data=data)
resp.read().decode()
(3)timeout:超时时间(单位:秒)
- 在某些网络情况不好或者服务器端异常的情况会出现请求慢的情况,或者请求异常,所以这个时候我们需要给请求设置一个超时时间,而不是让程序一直在等待结果,如果请求时间超出,那么就会抛出异常。
resp = request.urlopen(url, data=data, timeout=0.2)
resp.read().decode()
- 此时我们需要对异常进行捕获
import socket
from urllib import request
from urllib import error
try:
response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
print('TIME OUT')
(4)context:CA认证忽略
- HTTPS请求和HTTP请求,HTTPS = HTTP+SSL(安全套接层:RSA算法加密传输)
- HTTPS协议网站一般是经过权威机构CA认证机构认证过的安全性很高的网站
- 而有些网站并未经过CA安全认证(比如12306),因此当我们访问时,浏览器会提示我们存在安全风险,当我们很确信该网站值得信赖时,我们可以手动忽略该提示,那么在使用爬虫访问时我们就不能再手动处理提示了,此时就可以通过设置进行忽略该风险。
import ssl
# 创建一个不需要认证的ssl上下文管理器
context = ssl._create_unverified_context()
resp = request.urlopen(url, data=data, timeout=0.2, context=context)
Request
- urlopen只能用于一些简单的请求,因为它无法添加一些header信息,如果后面写爬虫我们可以知道,很多情况下我们是需要添加头部信息去访问目标站的,这个时候就用到了Request
(1)设置Headers
- 有很多网站为了防止程序爬虫爬网站造成网站瘫痪,会需要携带一些headers头部信息才能访问,最长见的有user-agent参数
from urllib import request
url = 'http://www.jd.com'
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Host': 'httpbin.org'
}
formdata = {
'name': 'zhaofan'
}
data = bytes(parse.urlencode(formdata), encoding='utf8')
# 通过Request设置请求头信息
req = request.Request(url=url, data=data, headers=headers)
response = request.urlopen(req)
print(response.read().decode('utf-8'))
- 第二种设置headers的方法
req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
- 一个自动生成请求头的库
from fake_useragent import UserAgent
ua = UserAgent()
headers = {"User-Agent": ua}
req = request.Request(url=url, data=data, headers=headers)
(2)设置cookie
- cookie中保存中我们常见的
登录
信息,有时候爬取网站需要携带cookie信息访问,这里用到了http.cookijar
,用于获取cookie以及存储cookie
from http import cookiejar
from urllib import request
from urllib import parse
from fake_useragent import UserAgent
ua = UserAgent()
headers = {"User-Agent": ua}
# 创建cookiejar对象,用来获取cookie并保存
cj = cookiejar.CookieJar()
# 通过HTTPCookieProcess对象来管理cj对象
cookie_handler = request.HTTPCookieProcess(cj)
- 为了在请求时能带上Cookie信息,我们需要重新构造一个
opener
。 - 使用
request.build_opener
方法来进行构造opener,将我们想要传递的cookie配置到opener中,然后使用这个opener的open
方法来发起请求。
# 构建一个opener,一个新的可以处理cookie的handler,取代原来默认的http handler,从而加强http handler的功能,实现其可以处理cookie
opener = request.build_opener(cookie_handler)
# 可以通过addheaders来添加请求头信息
# opener.addheaders = [('User-Agent': 'Mozilla/5.0....')]
# 构建请求所需数据
url = 'http://www.weixin.com'
formdata = {"username": "admin",
"password": "admin123"}
data = pares.urlencode(formdata).encode('utf-8')
req = request.Request(url=url, data=data, headers=headers)
# 通过open方法发起请求
resp = opener.open(req)
# 根据响应判断是否登录成功
# 假如现在登录成功,我们要去向登陆成功后的其他页面抓取数据,再次发送请求
# 通过Request对象构建数据,open方法发起请求
response = opener.open("http://www....")
# 写入文件
with open("xxxx", 'wb') as f:
f.write(response.read())
- 或者也可以把这个生成的opener使用
install_opener
方法来设置为全局的。则之后使用urlopen方法发起请求时,都会带上这个cookie。
# 将这个opener设置为全局的opener
request.install_opener(opener)
resp = request.urlopen(url)
(2)启用代理ProxyHandler
import urllib.request
proxy_handler = urllib.request.ProxyHandler({
'http': 'http://127.0.0.1:9743',
'https': 'https://127.0.0.1:9743'
})
opener = urllib.request.build_opener(proxy_handler)
response = opener.open('http://httpbin.org/get')
print(response.read())
(3)下载数据到本地
- 在我们进行网络请求时常常需要保存图片或音频等数据到本地,一种方法是使用python的文件操作,将read()获取的数据保存到文件中。
- 而urllib提供了一个
urlretrieve()
方法,可以简单的直接
将请求获取的数据保存
成文件
。
from urllib import request
url = 'http://python.org/'
request.urlretrieve(url, 'python.html')
响应 urllib.response
(1)read()
- 获取响应返回的数据,只能使用一次
(2)getcode()
- 获取服务器返回的状态码。
(3)getheaders()
- 获取返回响应的响应报头。
(4)geturl()
- 获取访问的url
异常处理
(1)urllib.error
- 在很多时候我们通过程序访问页面的时候,有的页面可能会出现错误,类似404,500等错误
这个时候就需要我们捕捉异常,下面先写一个简单的例子
from urllib import request, error
try:
response = request.urlopen("http://jdsogf.com/index")
except error.URLError as e:
print(e.reason)
- 上述代码访问的是一个不存在的页面,通过捕捉异常,我们可以打印异常错误
(2)这里我们需要知道的是在urllb异常这里有两个个异常错误:
- URLError
- HTTPError
- HTTPError是URLError的子类
- URLError里只有一个属性:
reason
,即抓异常的时候只能打印错误信息
,类似上面的例子 - HTTPError里有三个属性:
code
,reason
,headers
,即抓异常的时候可以获得code,reson,headers三个信息,例子如下
from urllib import request, error
try:
response = request.urlopen("http://pythonsite.com/1111.html")
except error.HTTPError as e:
print(e.reason)
print(e.code)
print(e.headers)
except error.URLError as e:
print(e.reason)
else:
print("reqeust successfully")
- 同时,e.reason其实也可以在做深入的判断,例子如下:
import socket
from urllib import request, error
try:
response = request.urlopen("http://www.pythonsite.com/",timeout=0.001)
except error.URLError as e:
print(type(e.reason))
if isinstance(e.reason, socket.timeout):
print("time out")