Requests概述:让 HTTP 服务人类
Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
Requests库的优势
Requests 允许你发送纯天然,植物饲养的 HTTP/1.1 请求,无需手工劳动。你不需要手动为 URL 添加查询字串,也不需要对 POST 数据进行表单编码。Keep-alive 和 HTTP 连接池的功能是 100% 自动化的,一切动力都来自于根植在 Requests 内部的 urllib3。
为什么使用Requests库
- requests在python2 和python3中通用,方法完全一样,urllib2在python2和python3中的名称不同
- 工作中爬虫基本都使用requests
- 虽然Python的标准库中 urllib 模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests 自称 "HTTP for Humans",说明使用更简洁方便。requests 继承了urllib的所有特性,requests的底层实现就是urllib2。
- requests能够自动帮助我们解压(gzip压缩的等)网页内容,简单易用
- Requests支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。
-
Requests库使用非常广泛,Twitter、Spotify、Microsoft、Amazon、Lyft、BuzzFeed、Reddit、NSA、女王殿下的政府、Amazon、Google、Twilio、Mozilla、Heroku、PayPal、NPR、Obama for America、Transifex、Native Instruments、Washington Post、Twitter、SoundCloud、Kippt、Readability、以及若干不愿公开身份的联邦政府机构都在内部使用。
Warning:非专业使用其他 HTTP 库会导致危险的副作用,包括:安全缺陷症、冗余代码症、重新发明轮子症、啃文档症、抑郁、头疼、甚至死亡:)。
功能特性
Requests 完全满足今日 web 的需求:
- Keep-Alive & 连接池
- 国际化域名和 URL
- 带持久 Cookie 的会话
- 浏览器式的 SSL 认证
- 自动内容解码
- 基本/摘要式的身份认证
- 优雅的 key/value Cookie
- 自动解压
- Unicode 响应体
- HTTP(S) 代理支持
- 文件分块上传
- 流下载
- 连接超时
- 分块请求
- 支持 .netrc
- Requests 支持 Python 2.6—2.7以及3.3—3.7,而且能在 PyPy 下完美运行。
开源地址:https://github.com/kennethreitz/requests
中文文档 API: http://docs.python-requests.org/zh_CN/latest/index.html
安装requests
$ pip install requests
基本GET请求(headers参数 和 parmas参数)
最基本的GET请求可以直接用get方法
import requests
resp = requests.get('http://www.neuedu.com')
print(resp)
# <Response [200]>
# 也可以这么写
response = requests.request("get", "http://www.neuedu.com")
print(response)
# <Response [200]>
response.text 和response.content的区别
import requests
url = 'http://www.baidu.com/'
r = requests.get(url)
# 查看响应内容 str类型
#r.text
r.encoding
'ISO-8859-1'
# 手动修改相应编码格式
r.encoding = 'utf-8'
r.encoding
'utf-8'
# 直接把相应数据改成utf-8
# r.content.decode()
# 获取响应头
r.headers
{'Accept-Ranges': 'bytes', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Content-Length': '2381', 'Content-Type': 'text/html', 'Date': 'Mon, 17 Jun 2019 15:13:04 GMT', 'Etag': '"588604c4-94d"', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:32 GMT', 'Pragma': 'no-cache', 'Server': 'ATS/8.0.3', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Age': '0', 'Connection': 'keep-alive'}
添加 headers 和 查询参数
为什么请求需要带上header?
模拟浏览器,欺骗服务器,获取和浏览器一致的内容
headers的形式:字典
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"}
# 用法:
requests.get(url,headers=headers)
发送带参数的请求
什么叫做请求参数:
列1: http://www.webkaka.com/tutorial/server/2015/021013/ 不是
例2: https://www.baidu.com/s?wd=python&c=b 是
import requests
kw = {'wd':'东软'}
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"}
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("http://www.baidu.com/s?", params=kw, headers = headers)
# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)
# 查看响应内容,response.content返回的字节流数据
print(response.content)
# 查看完整url地址
print(response.url)
# 查看响应头部字符编码
print(response.encoding)
# 查看响应码
print(response.status_code)
没有添加请求头的知乎网站
import requests
url = 'https://www.zhihu.com'
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"}
r = requests.get(url)
r.status_code
400
r = requests.get(url,headers= headers)
r.status_code
200
案例:百度贴吧的爬取
- 获取指定页数
- 将贴吧保存成文件
参考代码:
import requests
"""
目标:
爬取百度贴吧页面:
1、构造url
2、构造请求头
3、发送请求
4、保存数据
"""
class Baidu(object):
def __init__(self,name,pn):
# 保存传入的贴吧名称
self.name = name
self.url = 'https://tieba.baidu.com/f?kw={}&ie=utf-8&pn='.format(name)
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
# 构造url列表,拼接的页数
self.url_list = [self.url + str(pn*50) for pn in range(pn)]
print(self.url_list)
# 发送请求获取数据,需要修改传入的url
def get_data(self,url):
response = requests.get(url,headers=self.headers)
return response.content
# 保存数据
def save_data(self,data,num):
# 构造保存数据的文件名称
file_name = self.name + '_' + str(num) + '.html'
with open(file_name,'wb') as f:
f.write(data)
# 执行方法
def run(self):
# 遍历url列表
for url in self.url_list:
# print (url)
# 调用发送请求的方法,获取数据
data = self.get_data(url)
# 获取self.url_list索引值
num = self.url_list.index(url)
# 调用保存数据的方法
self.save_data(data,num)
import sys
if __name__ == '__main__':
# 传入参数,贴吧名称,页数
name = sys.argv[1]
pn = int(sys.argv[2])
# print(type(name))
# print(type(pn))
# print(name,pn)
baidu = Baidu(name,pn)
baidu.run()
在终端运行如下命令:
python3 spider_tieba.py 李毅 3
本地会生成3个html文件哦
Requests高阶(POST)
发送POST请求
哪些地方我们会用到POST请求:
登录注册( POST 比 GET 更安全)绝大多数的登录会使用post请求,极少数网站仍然在使用get请求进行登录
向服务器传输的数据量比较多的时候,或者向服务器传输大文件
- 所以同样的,我们的爬虫也需要在这两个地方回去模拟浏览器发送post请求
用法:
response = requests.post(url, data = data, headers=headers)
# data 的形式:字典
案例:访问金山词霸在线翻译,获取翻译结果
网址:http://fy.iciba.com/
需求
1、构造url
2、请求头
3、构造请求体
4、发送请求,输出数据
如下如是浏览器的演示效果,接下来我们要用python代买去实现这一功能
注释:
1.XHR:筛选使用Ajax请求的network(ajax技术的核心是XMLHttpRequest对象(简称XHR),这是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。XHR为向服务器发送请求和解析服务器响应提供了流畅的接口,能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新数据)
2.当左侧输入框发生 变化就会触发Ajax请求事件
- Form Data就是post请求的表单数据
参考代码:
- Form Data就是post请求的表单数据
import requests
import json
class JinShan(object):
def __init__(self,word):
self.url = 'http://fy.iciba.com/ajax.php?a=fy'
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
# 构造请求的参数
self.post_data = {
"f": "auto",
"t": "auto",
"w": word
}
# 发送请求
def request_post(self):
response = requests.post(url=self.url,headers=self.headers,data=self.post_data)
# print(response.content.decode())
return response.content.decode()
# 解析数据
def parse_data(self,data):
# 把响应数据转成字典
dict_data = json.loads(data)
# 判断out键是否存在,不能判断一个字符串是否在字典的索引
# if 'out' in data:
# content = (dict_data['content']['out'])
# else:
# content = (dict_data['content']['word_mean'][0])
# 进行异常处理
try:
content = (dict_data['content']['out'])
except:
content = (dict_data['content']['word_mean'][0])
print (content)
def run(self):
# 调用发送请求,并获取数据
data = self.request_post()
# 调用解析数据方法
self.parse_data(data)
if __name__ == '__main__':
import sys
word = sys.argv[1]
jinshan = JinShan(word)
jinshan.run()
在终端运行命令之后会有下面的效果
使用IP代理爬虫
IP代理既代理服务器,其功能主要就是代理网络用户去获取网络信息,形象的说就是网络信息的中转站
为什么爬虫需要使用代理?
- 让目标服务器以为不是同一个客户端在请求,放置因为ip发送请求过多而被反爬
-
防止我们的真实地址被泄露,防止被追究
代理分类
使用免费代理网站,比如快代理https://www.kuaidaili.com/free/找到免费的ip
下面是使用代理的示例代码:
import requests
# 使用代理
url = 'http://www.baidu.com/'
# 免费代理
proxies = {
'http':'http://115.210.24.183:9000',
'https':'https://183.129.207.86:14002'
}
# # 付费代理,用户名:密码@ip和port
# proxies = {
# 'http':'http://user:pwd@115.223.200.85:9000'
# }
response = requests.get(url,proxies=proxies)
print(response.text)
cookie和session
本质上都是基于键值对的字符串
两者区别:
- cookie数据存放在客户的浏览器上,session数据放在服务器上
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗(使用用户的cookies获取相关信息
- session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
利弊:
- 带上cookie、session的好处:很多网站必须登录之后(或者获取某种权限之后)才能能够请求到相关数据
- 带上cookie、session的弊端:一套cookie和session往往和一个用户对应.请求太快,请求次数太多,容易被服务器识别为爬虫。从而是账号收到损害
- 使用建议
1.不需要cookie的时候尽量不去使用cookie
2.为了获取登录之后的页面,我们必须发送带有cookies的请求,此时为了确保账号安全应该尽量降低数据采集速度
案例:使用cookies来获取登录之后人人网的响应
首先浏览器登录人人网,然后打开调试模式,复制url和cookie
参考代码如下:
import requests
import re
"""
使用cookie进行模拟登录
1、首先使用浏览器登录网站
2、获取cookie信息
3、保存cookie信息放到请求头中
"""
# 登录后用户信息页
url = 'http://www.renren.com/971209342'
# 保存cookie信息
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
"Cookie": "anonymid=jx19bd0l-4r5q5y; JSESSIONID=abcsD77Gb0el7fJktQOTw; depovince=GW; _r01_=1; ick_login=19787144-5838-490f-b599-34a22b58aa7c; t=06700184d453813c5b594f40b29cd9412; societyguester=06700184d453813c5b594f40b29cd9412; id=971209342; xnsid=974c659d; jebecookies=7b441396-b2be-4c75-ac03-4466a9fc763f|||||; ver=7.0; loginfrom=null"
}
# 发送请
response = requests.get(url, headers=headers)
# 获取响应,并且解码成str,使用正则获取响应中的字符串
print(re.findall('关注内容', response.content.decode()))
print(response.status_code)
输出结果如下:
['关注内容']
200
Cookies 和 session
如果一个响应中包含了cookie,那么我们可以利用 cookies参数拿到
import requests
url = 'http://www.baidu.com'
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
# 设置请求的超时时间,单位是s
# response = requests.get(url,timeout=3)
response = requests.get(url, headers=headers)
# 获取cookie信息
print(response.cookies)
# 把cookie转成字典
content = requests.utils.dict_from_cookiejar(response.cookies)
print(content)
# 将cookie字典转成对象
c = requests.utils.cookiejar_from_dict(content)
print(c)
运行结果:
<RequestsCookieJar[<Cookie H_PS_PSSID=1443_21123_29135_29237_29098_29369_28832_29220_26350 for .baidu.com/>, <Cookie delPer=0 for .baidu.com/>, <Cookie BDSVRTM=0 for www.baidu.com/>, <Cookie BD_HOME=0 for www.baidu.com/>]>
{'H_PS_PSSID': '1443_21123_29135_29237_29098_29369_28832_29220_26350', 'delPer': '0', 'BDSVRTM': '0', 'BD_HOME': '0'}
<RequestsCookieJar[<Cookie BDSVRTM=0 for />, <Cookie BD_HOME=0 for />, <Cookie H_PS_PSSID=1443_21123_29135_29237_29098_29369_28832_29220_26350 for />, <Cookie delPer=0 for />]>
使用session实现人人网登录
在 requests 里,session对象是一个非常常用的对象,这个对象代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开。
会话能让我们在跨请求时候保持某些参数,比如在同一个 Session 实例发出的所有请求之间保持 cookie
import requests
import re
url = 'http://www.renren.com/PLogin.do'
# 保存cookie信息
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
}
# 构造session回话对象
session = requests.session()
# 构造post请求data数据
data = {
'email':'1900000000',
'password':'neuedu'
}
# 发送post请求
session.post(url,headers=headers,data=data)
# 验证登录是否成功
response = session.get('http://www.renren.com/963112933')#
# 打印响应内容
print(response.text)