python3 HTTP请求urllib与requests
- 一:urllib模块
-
- 1.1 urlopen()
- 1.2 User-Agent
- 1.3 Request类
- 1.4 urllib.parse 模块
- 1.5 提交方法method
- 1.6 处理json数据
- 1.7 HTTPS证书忽略
- 二:urllib3 库
- 三:requests库
-
- 3.1 发送get请求
- 3.2 发送post请求
- 3.3 使用代理IP
- 3.4 session与cookie
- 3.5 简单封装
一:urllib模块
urllib是Python中内置的发送网络请求的一个库(包),在Python2中由urllib和urllib2两个库来实现请求的发送,但是在Python3中已经不存在urllib2这个库了,已经将urllib和urllib2合并为urllib。
urllib 是标准库,它一个工具包模块,包含下面的模块处理 url:
urllib.request 用于打开和读写url
urllib.error 包含了有urllib.request引起的异常。
urllib.parse 用于解析url
urllib.robotparser 分析robots.txt 文件
1.1 urlopen()
url参数,可以是一个string,或者一个Request对象。
data一定是bytes对象,传递给服务器的数据,或者为None。
目前只有HTTP requests会使用data,提供data时会是一个post请求,如若没有data,那就是get请求。data在使用前需要使用urllib.parse.urlencode()函数转换成流数据。
from urllib.request import urlopen
url = 'https://www.bing.com'
response = urlopen(url, timeout = 5)
print(response.closed)
with response:
print(type(response)) # from http.client import HTTPResponse
print(response.status, response.reason)
print(response._method)
print(response.read()) # 返回网页内容
print(response.info()) # 获取响应头信息
print(response.geturl()) # 请求的真正url(有的url会被301,302重定向)
print(response.closed)
通过urllib.requset.urlopen 方法,发起一个HTTP的GET请求,web 服务器返回了网页内容,响应的数据被封装到类文件对象中,可以通过read方法,readline方法,readlines方法,获取数据,status,和reason 表示状态码, info方法表示返回header信息等
1.2 User-Agent
urlopen方法通过url 字符串和data发起HTTP请求,如果想修改HTTP头,例如:useragent 就得借助其他方式
urllib.request源码中构造的默认的useragent 如下:
# from urllib.request import OpenerDirector
class OpenerDirector:
def __init__(self):
client_version = "Python-urllib/%s" % __version__
self.addheaders = [('User-agent', client_version)]
自定义构造请求头:
from urllib.request import urlopen, Request
url = 'https://www.bing.com'
user_agent = {"User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}
req = Request(url, headers = user_agent) # 也可以通过下面行的代码添加user_agent到请求头
# req.add_header('User-Agent', user_agent)
response = urlopen(req, timeout = 5) # url参数为一个Request对象
print(response.closed)
with response:
print(type(response)) # from http.client import HTTPResponse
print(response.status, response.reason)
print(response._method)
print(response.read()) # 返回网页内容
print(response.info()) # 获取响应头信息
print(response.geturl()) # 请求的真正url(有的url会被301,302重定向)
print(response.closed)
1.3 Request类
Request(url, data=None, headers={} )
# 初始化方法,构造一个请求对象,可添加一个header的字典
# data 参数决定是GET 还是POST 请求(data 为None是GET,有数据,就是POST)
# add_header(key, val) 为header中增加一个键值对。
import random
from urllib.request import urlopen, Request
url = 'http://www.bing.com'
user_agent_list = [
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)"
]
user_agent = random.choice(user_agent_list) # 随机选择user_agent
request = Request(url)
request.add_header('User-Agent', user_agent)
response = urlopen(request, timeout = 20)
print(type(response))
with response:
print(1, response.status, response.getcode(), response.reason) # 状态,getcode本质上就是返回status
print(2, response.geturl()) # 返回真正请求的url
print(3, response.info()) # 返回响应头headers
print(4, response.read()) # 读取返回的内容
print(5, request.get_header('User-agent')) # 获取请求头中的User-agent信息
print(6, request.header_items()) # 获取请求头中的信息
1.4 urllib.parse 模块
该模块可以完成对url的编解码
from urllib import parse
d = dict(
id = 1,
name = '张三',
hobby = 'football'
)
u = parse.urlencode(d)
print(u)
# id=1&name=%E5%BC%A0%E4%B8%89&hobby=football
从运行结果来看冒号。斜杆 & 等号,问号都被编码,%之后实际上是单字节十六进制表示的值
query_param = parse.urlencode({'wd': '中'})
url = 'https://www.baidu.com?{}'.format(query_param)
print(url) # https://www.baidu.com?wd=%E4%B8%AD
print('中'.encode('utf-8')) # b'\xe4\xb8\xad'
print(parse.unquote(query_param)) # 解码:wd=中
print(parse.unquote(url)) # https://www.baidu.com?wd=中
一般来说,url中的地址部分,一般不需要使用中文路径,但是参数部分,不管 GET 还是post 方法,提交的数据中,可能有斜杆等符号,这样的字符表示数据,不表示元字符,如果直接发送给服务器端,就会导致接收方无法判断谁是元字符,谁是数据,为了安全,一般会将数据部分的字符串做url 编码,这样就不会有歧义了。
后来可以传送中文,同样会做编码,一般先按照字符集的encoding要求转化成字节序列,每一个字节对应的十六进制字符串前加上百分号即可。
1.5 提交方法method
最常用的HTTP交互数据的方法是GET ,POST
GET 方法,数据是通过URL 传递的,也就是说数据时候在http 报文的header部分
POST方法,数据是放在http报文的body 部分提交的数据都是键值对形式,多个参数之间使用&符号链接
GET方法:
连接 bing 搜索引擎官网,获取一个搜索的URL: http://cn.bing.com/search?q=如何学好Python
请写程序需完成对关键字的bing 搜索, 将返回的结果保存到一个网页文件中。
query_param = dict(q = '如何学好Python')
base_url = 'https://cn.bing.com/search'
url = '{}?{}'.format(base_url, parse.urlencode(query_param))
user_agent = "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)"
req = Request(url, headers = {'User-agent': user_agent})
result = urlopen(req)
print(type(result))
with result:
with open('bing.html', 'wb+') as f:
f.write(result.read())
f.flush()
POST方法:
url = 'http://httpbin.org/post'
data = urlencode({'name': '张三,@=/$*', 'age': 22})
request = Request(url, headers = {'User-agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)'})
with urlopen(request, data = data.encode()) as result:
text = result.read()
d = json.loads(text)
print(d)
print(type(d))
1.6 处理json数据
重要的不是代码,而是在网页上能找到json请求的数据,这里以豆瓣电影为例:
上面箭头指向的就是该页面的json请求,得到json请求数据的url后,代码与上面的写法是一样的
jurl = 'https://movie.douban.com/j/search_subjects'
data = dict(
type= 'movie',
tag = '热门',
page_limit=10,
page_start=10
)
user_agent = {'User-agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)'}
req = Request('{}?{}'.format(jurl, urlencode(data)), headers = user_agent)
with urlopen(req) as result:
subjects = json.loads(result.read())
print(len(subjects['subjects']))
print(subjects)
1.7 HTTPS证书忽略
HTTPS使用SSL 安全套接层协议,在传输层对网路数据进行加密,HTTPS 使用的时候,需要证书,而证书需要cA认证
CA(Certificate Authority)是数字证书认证中心的简称,是指发放,管理,废除数据证书的机构。
CA是受信任的第三方,有CA签发的证书具有可信性。如果用户由于信任了CA签发的证书导致的损失可以追究CA的法律责任。
CA是层级结构,下级CA信任上级CA,且有上级CA颁发给下级CA证书并认证。
from urllib.request import urlopen, Request
import ssl
request = Request('https://www.12306.cn/mormhweb/')
print(request)
ua = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 Safari/537.36'
request.add_header('User-agent', ua)
context = ssl._create_unverified_context() # 忽略不信任的证书(不用校验的上下文)
res = urlopen(request, context=context)
with res:
print(res._method)
print(res.geturl())
print(res.read().decode())
二:urllib3 库
标准库urllib缺少了一些关键的功能,非标准库的第三方库 urlib3 提供了,比如说连接池管理
官方文档:https://urllib3.readthedocs.io/en/latest/
from urllib.parse import urlencode
from urllib3.response import HTTPResponse
import urllib3
jurl = 'https://movie.douban.com/j/search_subjects'
data = dict(
type= 'movie',
tag = '热门',
page_limit=10,
page_start=10
)
user_agent = {'User-agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)'}
# 连接池管理器
with urllib3.PoolManager() as http:
response = http.request('GET', '{}?{}'.format(jurl, urlencode(data)), headers = user_agent)
print(type(response))
# response: HTTPResponse = HTTPResponse()
print(response.status)
print(response.data)
三:requests库
requests 使用了 urllib3, 但是 API 更加友好,推荐使用。requests的底层实现就是urllib模块。
# 官方文档见
https://requests.readthedocs.io/en/master/
# 安装requests模块
pip install requests
3.1 发送get请求
import requests
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
data = {'wd': 'python文档'}
response = requests.get(url = 'https://www.baidu.com/s', params = data, headers = headers)
# url后面可加?也可不加?,requests会自动把url与params进行拼接;如果有中文也会自动进行编码
# https://www.baidu.com/s?wd=python%E6%96%87%E6%A1%A3
print(response.status_code) # 状态码
print(response.request.headers) # 请求头
print(response.headers) # 响应头
print(response.request.url) # 请求的URL
print(response.url) # 响应的URL(如URL发生了重定向,该方法返回实际返回数据的URL)
print(response.text) # 响应内容(字符串)
print(response.content.decode()) # 响应内容(bytes)
response.text与response.content的区别:
url = 'http://www.baidu.com'
response = requests.get(url)
print(response)
print(response.encoding) # 根据HTTP头部对响应的编码作出有根据的推测,推测的文本编码
# response.encoding = 'utf-8' # 修改编码方式,将编码修改为utf-8后,输出的中文正常显示
print(response.text) # 直接输出结果会发现中文乱码
url = 'http://www.baidu.com'
response = requests.get(url)
print(response.content.decode()) # 返回bytes类型且不指定解码类型,使用decode()进行解码,默认为utf-8
请求并保存图片:
response = requests.get('https://www.baidu.com/img/dong_f6764cd1911fae7d460b25e31c7e342c.gif')
with open('demo.gif', 'wb') as f:
f.write(response.content)
3.2 发送post请求
requests默认使用application/x-www-form-urlencoded对POST数据编码:
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
data={'name':"tian"}
r=requests.post('http://www.httpbin.org/post',data=data,headers=headers)
print(r.text)
如果要传递JSON数据,可以直接传入json参数:
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
data={'name':"tian"}
r=requests.post('http://www.httpbin.org/post',json=data,headers=headers)
# 通过json=data传入一个字典,内部会自动序列化为json
print(r.text)
上传文件需要更复杂的编码格式,但是requests把它简化成files参数:
>>> upload_files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=upload_files)
# 在读取文件时,注意务必使用'rb'即二进制模式读取,这样获取的bytes长度才是文件的长度
3.3 使用代理IP
proxies = {
'http': 'http://140.255.145.83:25330'
}
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
r = requests.get(url = 'http://www.baidu.com', proxies = proxies, headers = headers)
print(r.status_code)
3.4 session与cookie
cookie和session的区别:
- cookie数据存放在客户的浏览器上,session数据存放在服务器上
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗
- session会在一定时间内保存在服务器上。当访问增多,会占用服务器的资源
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
带上cookie, session的好处:能够请求到登录之后的页面
带上cookie, session的弊端:一套cookie和session往往和一个用户对应,请求太快,次数太多,容易被服务器识别为爬虫
session = requests.session()
url = 'http://www.renren.com/PLogin.do'
data = {'email': '[email protected]', 'password': 'xxxxxx'}
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
# 使用session发送post请求,cookie保存在其中
session.post(url = url, data = data, headers = headers)
# 再使用session进行请求登陆后才能访问的地址
r = session.get('http://www.renren.com/875198389/profile', headers = headers)
print(r.status_code)
with open('renren.html', 'w', encoding='utf-8') as f:
f.write(r.content.decode())
或者手动登录网站,然后得到cookie信息,请求时直接携带cookie信息发送请求:
session = requests.session()
url = 'http://www.renren.com/PLogin.do'
data = {'email': '[email protected]', 'password': 'xxxxxx'}
headers = {
'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6',
'Cookie': '这里是登录网站后的cookie信息'
}
# 使用session发送post请求,cookie保存在其中
# session.post(url = url, data = data, headers = headers)
# 再使用session进行请求登陆后才能访问的地址
r = session.get('http://www.renren.com/875198389/profile', headers = headers)
print(r.status_code)
with open('renren.html', 'w', encoding='utf-8') as f:
f.write(r.content.decode())
cookies也可以作为一个单独的参数传递,但要求是cookies为一个字典
session = requests.session()
url = 'http://www.renren.com/PLogin.do'
data = {'email': '[email protected]', 'password': 'xxxxxx'}
headers = {
'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'
}
cookies = 'anonymid=xxxxxx-lhsd6u; depovince=GW; jebecookies=192dd350-389f-4002-9ad4-955822ef9e78|||||; _r01_=1; taihe_bi_sdk_uid=xxxxxxxx; taihe_bi_sdk_session=xxxxxx; ick_login=a49c573b-532d-4898-914e-e9ecdf1fd003; _de=F0FA10CCF09C5140CA6F896A1DF2C9CE; p=269e670c6edffcdf98110263a89728d79; first_login_flag=1; [email protected]; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=d600e408c953ce49e3a8f886e6ee3fc29; societyguester=d600e408c953ce49e3a8f886e6ee3fc29; id=875198389; xnsid=21554bf8; ver=7.0; loginfrom=null; JSESSIONID=abcP8p0hNRpNcdxsRQEhx; wp_fold=0'
cookies = {i.split('=')[0]:i.split('=')[1] for i in cookie.split('; ')} # 将上面的coockie转换为字典
r = session.get('http://www.renren.com/875198389/profile', headers = headers, cookies = cookies)
print(r.status_code)
with open('renren.html', 'w', encoding='utf-8') as f:
f.write(r.content.decode())
request默认使用Session 对象,是为了在多次和服务器交互中保留会话的信息,例如cookie,否则,每次都要重新发起请求
urls = ['https://www.baidu.com/s?wd=Python', 'https://www.baidu.com/s?wd=Java']
headers = {
'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'
}
session = requests.Session()
with session:
for url in urls:
response = session.get(url, headers = headers)
print(type(response))
with response:
print(response.text[:50]) # Html内容
print(response.headers) # 响应头
print(response.request.headers) # 请求头
print('-'*30)
print(response.cookies) # 输入cookie信息
print('-'*30)
print('=' * 100)
第一次发起请求是不带cookie的,第二次请求会带上cookie信息去请求网页
3.5 简单封装
class HTTP:
@staticmethod
def get(url, return_json = True):
r = requests.get(url)
if r.status_code != 200:
return {} if return_json else ''
return r.json() if return_json else r.text