爬虫请求库之requests库
一. 介绍#
# 介绍:使用requests可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3)
# 注意:requests库发送请求将网页内容下载下来以后,并不会执行js代码,这需要我们自己分析目标站点然后发起新的request请求
# 安装:pip3 install requests
# 各种请求方式:常用的就是requests.get()和requests.post()
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r = requests.post('http://httpbin.org/post', data = {'key':'value'})
>>> r = requests.put('http://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('http://httpbin.org/delete')
>>> r = requests.head('http://httpbin.org/get')
>>> r = requests.options('http://httpbin.org/get')
# 建议在正式学习requests前,先熟悉下HTTP协议
http://www.cnblogs.com/linhaifeng/p/6266327.html
二. 基于GET请求#
1. 发送get请求#
知识储备: 什么是referer?
import requests
headers = {
# 模拟浏览器
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
# 解决妹子图防盗链问题: referer
'referer': 'https://www.mzitu.com/taiwan/'
}
# response = requests.get('https://mzitu.com', headers=headers)
# print(response.text) # 文本内容
response1 = requests.get("https://i.mmzztt.com/thumb/2020/07/221376_236.jpg", headers=headers)
# print(response1.content) # 二进制内容
# 将爬取的图片写入本地
with open('221376_236.jpg', 'wb') as f:
for line in response1.iter_content():
f.write(line)
2. 请求地址中携带数据#
1) 请求地址中携带数据方式一: 直接携带 (中文一般不会进行url编码, 会出现编码问题)#
import requests
headers = {
# 模拟浏览器
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
}
response = requests.get('https://www.baidu.com/s?wd=wozhengshuai', headers=headers)
print(response.text)
2) 请求地址中携带数据方式二: 使用params (中文会自动进行url编码)#
import requests
headers = {
# 模拟浏览器
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
}
response = requests.get('https://www.baidu.com/s', params={'wd': '我真帅'}, headers=headers)
print(response.text)
3) 使用urllib解决方式一编码问题#
from urllib.parse import urlencode, unquote
# 编码
res = urlencode({'wd': '我真帅'}, encoding='utf-8')
print(res) # wd=%E6%88%91%E7%9C%9F%E5%B8%85
# 解码
res = unquote('%E6%88%91%E7%9C%9F%E5%B8%85', encoding='utf-8')
print(res) # 我真帅
3. 请求带cookie#
1) 方式一: 存放在header#
import requests
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
'cookie': 'Hm_lvt_b72418f3b1d81bbcf8f99e6eb5d4e0c3=1596202661; UM_distinctid=173a517b3192a1-07d51af695c35e-3a65420e-1fa400-173a517b31a70b;'
}
# 注意: url不要写错成了 'http://127.0.0.1:8050/index'
response = requests.get('http://127.0.0.1:8050/index/', headers=headers)
print(response.text)
# 服务端获取:
# 注意: 放在headers中, cookie对应的value如果有等于号, 那么等于号左边作为cookie的key, 右边作为cookie的value. 如果没有那么, key为空字符串. value为值.
"""
{
'Hm_lvt_b72418f3b1d81bbcf8f99e6eb5d4e0c3': '1596202661',
'UM_distinctid': '173a517b3192a1-07d51af695c35e-3a65420e-1fa400-173a517b31a70b'
}
"""
2) 方式二: 存放在指定的cookies参数中#
import requests
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
}
cookies = {
'cookie': 'Hm_lvt_b72418f3b1d81bbcf8f99e6eb5d4e0c3=1596202661; UM_distinctid=173a517b3192a1-07d51af695c35e-3a65420e-1fa400-173a517b31a70b;'
}
# 注意: url不要写错成了 'http://127.0.0.1:8050/index'
response = requests.get('http://127.0.0.1:8050/index/', headers=headers, cookies=cookies)
print(response.text)
# 服务端获取:
# 注意: cookies直接指定. 字典中的key对应的服务端cookie获取的key. 字典中value冒号分隔的等于号左边作为cookie的key, 右边作为value
'''
{
'cookie': 'Hm_lvt_b72418f3b1d81bbcf8f99e6eb5d4e0c3=1596202661',
'UM_distinctid': '173a517b3192a1-07d51af695c35e-3a65420e-1fa400-173a517b31a70b'
}
4. 总结#
# headers参数:
1. 模拟浏览器: user-agent
2. 解决防盗链: referer
# 解决url编码问题
1. params参数默认解决url编码问题
2. urllib模块
# 携带cookie
第一种方式: 存放在headers中
客户端发送: {'cookie': 'key=value;key1=value1'}
服务端获取: {key: value, key1: value1}
第二种方式: 指定cookie参数 (提示: 可以存放dict 和 CookieJar对象)
客户端发送: {key: value, key1: value1}
服务端获取: {key: value, key1: value1}
response.text 文本
response.content 二进制
response.iter_content() 迭代器
三. 基于POST请求#
1. 携带数据发送post请求#
1) 携带数据: urlencoded#
import requests
response = requests.post('http://127.0.0.1:8050/index/', data={'name': 'yang'})
print(response.text)
# 服务端获取
'''
request.body: b'name=yang'
request.POST: <QueryDict: {'name': ['yang']}>
'''
2) 携带数据: json#
import requests
response = requests.post('http://127.0.0.1:8050/index/', json={'name': 'yang'})
print(response.text)
# 服务端获取
'''
request.body: b'{"name": "yang"}'
request.POST: <QueryDict: {}>
'''
2. 自动携带cookie#
import requests
session = requests.session() # 注意: 是session()方法, 不是sessions()
session.post('http://127.0.0.1:8050/login/', json={'username': 'yang', 'password': '123'}) # 假设这个请求登录了
response = session.get('http://127.0.0.1:8050/index/') # 现在不需要手动带cookie, session会自动处理
print(response)
3. 自定义请求头#
requests.post(url='xxxxxxxx',
data={'xxx': 'yyy'}) # 没有指定请求头,# 默认的请求头:application/x-www-form-urlencoed
# 如果我们自定义请求头是application/json,并且用data传值, 则服务端取不到值
requests.post(url='',
data={'': 1, },
headers={
'content-type': 'application/json'
})
requests.post(url='',
json={'': 1, },
) # 默认的请求头:application/json
4. 总结#
# 携带数据:
携带json数据: json={}
携带urlencoded数据: data={}
# 自动携带cookie:
session = requests.session()
res = session.post(认证url)
res1 = session.get(访问url)
# 自定义请求头:
默认: application/x-www-form-urlencoed
headers={'content-type': 'application/json'}
四. 响应Response#
1. response对象方法#
import requests
response = requests.post('http://127.0.0.1:8050/index/?username=yang', data={'name': 'yang'})
# 响应的文本
res = response.text
print(res) # ok
# 响应体的二进制
res = response.content
print(res) # b'ok'
# 获取响应状态码
res = response.status_code
print(res) # 200
# 获取响应头
res = response.headers
print(res)
'''
{
'Date': 'Fri, 31 Jul 2020 14:32:06 GMT', 'Server': 'WSGIServer/0.2 CPython/3.6.3',
'Content-Type': 'application/json',
'X-Frame-Options': 'SAMEORIGIN',
'Content-Length': '16',
'Set-Cookie': 'cookie_key=cookie_value; Path=/'
}
'''
# 注意: 它是CookieJar对象, 获取后端设置的cookie
res = response.cookies
print(res) # <RequestsCookieJar[<Cookie cookie_key=cookie_value for 127.0.0.1/>]>
# 把cookie转成字典
res = response.cookies.get_dict()
print(res) # {'cookie_key': 'cookie_value'}
# 把cookie转成列表套元组
res = response.cookies.items()
print(res) # [('cookie_key', 'cookie_value')]
# 获取请求的url地址
res = response.url
print(res) # http://127.0.0.1:8050/index/?username=yang 如果重定向了那么就是重定向的地址 http://127.0.0.1:8050/home/
# 将重定向之前的地址存放到列表当中. 注意: 是response对象
res = response.history
print(res) # [<Response [302]>, <Response [302]>]
print(type(res[0])) # <class 'requests.models.Response'>
print(res[0].content) # b''
# 响应的编码方式
res = response.encoding
print(res) # ISO-8859-1
# iter_content主要是针对图片,视频,大文件. 可以迭代取值
res = response.iter_content()
print(res) # <generator object iter_slices at 0x0000021A72B5F360>
2. 编码问题#
import requests
response = requests.get('http://www.autohome.com/news')
# 方式一: 手动指定编码 (前提: 知道它的编码方式)
# response.encoding = 'gb2312'
# 方式二: 自动指定编码, 由方法内部获取.
response.encoding = response.apparent_encoding
print(response.text)
3. 解析json#
import requests
response = requests.post('http://127.0.0.1:8050/index/', data={'name': 'yang'})
response_text = response.text
print(type(response_text), response_text) # <class 'str'> {"name": "yang"}
# 解析方式一: 使用json模块解析 (繁琐)
import json
res = json.loads(response_text)
print(type(res), res) # <class 'dict'> {'name': 'yang'}
# 解析方式二: 使用requests提供的方法解析(提示: 内部本质还是调用了json)
res = response.json()
print(type(res), res) # <class 'dict'> {'name': 'yang'}
# 注意: 方式一是通过获取到文本内容再进行的json序列化, 而方式二是直接透过response对象调用json()方法.
4. 总结#
# response对象方法:
响应文本 response.text
响应二进制数据 response.content
响应状态码 response.status_code
响应头 response.headers
响应CookieJar对象 response.cookies
响应cookie字典 response.cookies.get_dict()
响应cookie列表套元组 response.cookies.items()
响应重定向之前的response对象 response.history
响应url地址 response.url
响应编码 response.encoding
响应数据的迭代器 response.iter_content()
# 解决响应内容编码:
手动: response.encoding = '你知道你获取url资源的编码'
自动: response.encoding = response.apparent_encoding
# 解析json:
1. json模块解析
json.loads(response.text)
2. requests提供的json()方法解析
response.json()
五. 高级用法#
1. SSL Cerf Verification#
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code) # 不验证证书,报警告,返回200
# 使用证书,需要手动携带
import requests
response = requests.get('https://www.12306.cn',
cert=('/path/server.crt',
'/path/key'))
print(response.status_code)
2. 使用代理#
知识储备: 什么是http代理,什么是socks5代理?两者有什么不同?
1) HTTP代理#
import requests
response = requests.get('https://www.baidu.com/', proxies={'http': '165.225.8.94:10605', })
print(response.status_code)
2) socks代理#
# 安装: pip install requests[socks]
import requests
proxies = {
# 'https': 'socks5://47.244.192.12:17518',
'http': 'socks5://47.244.192.12:17518',
}
response = requests.get('https://www.baidu.com',
proxies=proxies)
print(response.status_code)
3. 超时设置#
import requests
# 两种超时:float or tuple
# timeout = 0.001 # 代表接收数据的超时时间
timeout = (0.0001, 0.002) # 0.1代表链接超时 0.2代表接收数据的超时时间
response = requests.get('https://www.baidu.com',
timeout=timeout)
print(response.text)
print(response.status_code)
# 注意: 超时以后抛出异常.
'''
# 0.0001表示链接超时
requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='www.baidu.com', port=443): Read timed out. (read timeout=0.0001)
'''
4. 认证设置#
官网链接:http://docs.python-requests.org/en/master/user/authentication/
'''
认证设置:登陆网站是,弹出一个框,要求你输入用户名密码(与alter很类似),此时是无法获取html的
但本质原理是拼接成请求头发送
r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
一般的网站都不用默认的加密方式,都是自己写
那么我们就需要按照网站的加密方式,自己写一个类似于_basic_auth_str的方法
得到加密字符串后添加到请求头
r.headers['Authorization'] =func('.....')
'''
# 看一看默认的加密方式吧,通常网站都不会用默认的加密设置
import requests
from requests.auth import HTTPBasicAuth
r = requests.get('xxx', auth=HTTPBasicAuth('user', 'password'))
print(r.status_code)
# HTTPBasicAuth可以简写为如下格式
import requests
r = requests.get('xxx', auth=('user', 'password'))
print(r.status_code)
5. 异常处理#
import requests
from requests.exceptions import *
try:
response = requests.get('https://www.baidu.com', timeout=(0.001, 0.002))
print(response.status_code)
except ReadTimeout:
print('读取超时!')
except ConnectionError:
print('连接失败!')
except Timeout:
print('超时')
except RequestException:
print("请求异常")
except Exception as e:
print(e)
6. 上传文件#
import requests
import os
dir_path = os.path.join(os.path.dirname(__file__), '2')
dir_list = os.listdir(dir_path)
file1, file2 = dir_list[0], dir_list[1]
# print(file1, file2) # 220089_236.jpg 220304_236.jpg
file1_path = os.path.join(dir_path, file1)
file2_path = os.path.join(dir_path, file2)
files = {
'file1': open(file1_path, 'rb'),
'file2': open(file2_path, 'rb'),
}
response = requests.post('http://127.0.0.1:8050/index/', files=files)
print(response.status_code) # 200
# 后端数据获取:
'''
<MultiValueDict:
{
'file1': [<InMemoryUploadedFile: 220089_236.jpg ()>],
'file2': [<InMemoryUploadedFile: 220304_236.jpg ()>]
}
>
'''
7. 总结#
# SSL认证
verify=False不校验
verify=True校验. cert=(证书格式)
# 代理
# HTTP代理
proxies={'http': 'IP:PORT'}
# socks代理: 安装requests[socks]
proxies={'http': 'socks://IP:PORT'}
# 超时设置
timeout=(连接超时时间, 接受数据超时时间)
抛出异常: ReadTimeOut
# 认证设置
from requests.auth import HTTPBasicAuth
auth=HTTPBasicAuth('user', 'password')
# 异常处理
from requests.exceptions import *
ReadTimeOut 连接 或者 获取数据超时
TimeOut 超时
ConnectionError 连接错误
RequestException 请求异常
# 上传文件
files={key: file, key1: file1}