文章目录
1、概述
问题:为什么要学习requests,而不是urllib?
requests的底层实现就是urllib
requests简单易用
requests在python2 和python3中通用,方法完全一样
requests能够自动帮助我们解压(gzip压缩的等)网页内容
作用:发送网络请求,返回响应数据
中文文档 API:
http://docs.python-requests.org/zh_CN/latest/index.html
如何使用requests来发送网络请求?
2、request的基本使用
安装requests模块: pip install requests
使用步骤(三部曲):
导入模块: import reqeusts
- 发送get请求,获取响应: response = requests.get(url)
- 从响应中获取数据
- 核心代码: 发送get请求
request的常用属性:
response.text 响应文本
respones.content 二进制形式的响应数据
response.status_code 响应状态吗
response.headers 响应头
response.request.headers 请求头
response.text 和response.content的区别
response.text
类型:str
解码类型: 根据HTTP头部对响应的编码作出有根据的推测,推测的文本编码
如何修改编码方式:response.encoding=”gbk”
response.content
类型:bytes
解码类型: 没有指定
如何修改编码方式:response.content.deocde(“utf8”)
更推荐使用response.content.deocde()的方式获取响应的html页面
实例1:爬取百度首页
# 1. 导入这个模块; 如果没有就安装一下: pip install resquests
import requests
# 2. 发送请求(get)
response = requests.get("http://www.baidu.com/")
# 3. 读取数据
# response.encoding = "utf8"
# #print(response.encoding) #ISO-8859-1 拉丁文编码表; 希腊等一些欧洲国家
# text 属性: 返回 自动识别编码的字符穿, 如果要指定编码,可以使用encoding的属性来指定编码
# content = response.text
# print(content)
# content 属性: 获取的是二进制数据,使用decode进行解码
data = response.content.decode()
print(data)
# response.status_code 响应状态吗
print(response.status_code)
# response.request.headers 请求头
print(response.request.headers)
# response.headers 响应头
print(response.headers)
解决乱码问题
先使用decode()来尝试解码, 现在国内80%网站都是用utf-8进行编码的
如果上面面没有解决乱码问题,就指定GBK的编码方式, 个别网站使用的是GBK
如果指定GBK还不行,就request的自动推断功能了,通过text 属性获取
# 1. 现在国内80%网站都是用utf-8进行编码的
data = response.content.decode()
print(data)
# 2. 如果上面面没有解决乱码问题,就指定GBK的编码方式
# data = response.content.decode("GBK")
# 3. 如果指定GBK还不行,就request的自动推断功能了,通过text 属性获取
# data = response.text
练习2:下载图片
# 1. 导入requests模块
import requests
# 2. 发送请求获取二进制数据(bytes)
respone = requests.get("http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=098c3f828cd6277ffd1f3a7841407a5c/3c6d55fbb2fb4316e3afd1432ba4462309f7d353.jpg")
# 获取二进制数据; 注意: 这里不要解码, 因为图片,视频等文件都是二进制的不是文本,不需要解码
# data = respone.content
# 3. 把数据写入文件
with open("壁纸.jpg", "wb") as f:
f.write(respone.content)
requests带有请求头
我们直接发送请求获取到的baidu首页与真实不一样,该怎么办?
原因是百度的服务器认为,不是人给他发送请求.
默认使用requests模块发送请求: ‘User-Agent’: ‘python-requests/2.18.1’, 百度服务器就认为这是一个爬虫,就不给你真正的页面.
在requests发送请求指定headers的格式 requests.get(url, headers={})
为什么要发送带有请求头的请求呢? 就让服务器认为,这个是正常客户端请求. 从而给我真正的内容.
代码:
import requests
# 定义请求头字典
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"
}
# 发送请求,获取百度首页
response = requests.get("http://www.baidu.com", headers=headers)
# 那内容之前先判断一下状态
print(response.request.headers)
# print(response.status_code)
if response.status_code == 200:
print(response.content.decode())
使用requests模块发送带有参数的请求
练习3 如何模拟搜狗搜索内容 ?
发送带有参数请求,在URL后面加上query=要搜索的内容
URL编码:
https://www.sogou.com/web?query=%E9%9F%B3%E4%B9%90
在requests发送get请求指定参数的格式 ?
方式1: 自己拼接一个带有参数的URL
方式2: 在发送请求的时候,使用params进行指定格式:
requests.get(“url”, params={})
import requests
# 发送带有参数的请求
# 方式1: 拼接URL
# 键盘录入要搜索的内容获取搜索结果
# url = "https://www.sogou.com/web?query=%s"
url = "https://www.sogou.com/web?query={}"
key = input("请录入你要搜索的内容: ")
# url = url % key
url = url.format(key)
# print(url)
# 发送请求
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"
}
response = requests.get(url,headers=headers)
if response.status_code == 200:
print(response.content.decode())
方式2: 使用字典指定请求参数
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"
}
# 准备参数
key = input("请录入你要搜索的内容: ")
params = {"query":key}
basic_url = "https://www.sogou.com/web"
# 发送带有参数的请求
response = requests.get(basic_url, params=params, headers=headers)
# 获取请求结果打印
content = response.content.decode()
print(content)
实例:爬取百度贴吧
实现任意贴吧的爬虫,保存网页到本地;
要求: 可以指定贴吧名称, 起始页与结束页
思路:
找出访问贴吧内容的URL的规律
http://tieba.baidu.com/f?kw=吧名&ie=utf-8&pn=(页码-1)*50
代码实现
根据规律生成访问贴吧内容页面的URL,放到列表中
遍历URL,发送请求,获取内容
把内容页面写到文件中
import requests
# 定义一个贴吧爬虫类
class TiebaCrawler(object):
# 基础URL
basic_url = "http://tieba.baidu.com/f?kw={}&ie=utf-8&pn={}"
# 初始化方法
def __init__(self, name, start, end):
self.name = name
self.start = start
self.end = end
# 2.1 根据规律生成访问贴吧内容页面的URL,放到列表中
def get_url_list(self):
# 定义一个list
# url_list = []
# # 生成URL放入list中
# for i in range(self.start, self.end+1):
# url = self.basic_url.format(self.name, (i-1)*50)
# url_list.append(url)
# #返回list的列表
# return url_list
# 扁平化写法: 推荐,简洁
return [self.basic_url.format(self.name, (i-1)*50) for i in range(self.start, self.end+1)]
# 写一个方法下载URL中内容
def download_from_url(self, url):
response = requests.get(url)
return response.content
# 写一个方法把内容写到文件中
def write_to_file(self, filename, content):
with open(filename, "wb") as f:
f.write(content)
# 下载方法,存储主逻辑
def run(self):
# 2.1 根据规律生成访问贴吧内容页面的URL,放到列表中
url_list = self.get_url_list()
# print(url_list)
# 遍历URL,发送请求,获取内容
for url in url_list:
content = self.download_from_url(url)
# 文件名称: 贴吧名称_页码
file_name = "{}_第{}页.html".format(self.name, url_list.index(url)+1)
# 把内容页面写到文件中
self.write_to_file(file_name, content)
print("写入完成")
if __name__ == '__main__':
# 思路: 从用户角度去考虑代码写法
# 你要下载那个贴吧内容创建一个对象,指定贴吧名称,起始页和结束页,调用run方法就可以了
ts = TiebaCrawler("电影", 1, 5)
ts.run()
3、使用requests模块发送post请求
请问我们注册和登录是发送get请求还是post请求为什么?
POST:
原因: 登陆是把账号和密码信息提交给服务器, POST更加安全
当我们发送的内容比较多的时候,是使用get请求还是post请求为什么
POST
原因: POST可以提交"大""数据
我们写爬虫的时候, 不可避免要发送POST请求, 使用requests模块如何发送POST请求呢?
使用requests模块发送POST请求格式
requests.get(url, params={}, headers={}) # get请求
requests.post(url, data={}, headers={}) # POST请求
演示: 向http://httpbin.org/post 发送一个POST请求, 测试数据是否能传递成功
说明: http://httpbin.org 是一个专门用于测试http请求网站
代码
import requests
# 表单数据
data = {
'name': 'lao wang'
}
# 发送POST请求
response = requests.post("http://httpbin.org/post", data=data)
print(response.content.decode())
实例:模拟百度翻译
import json
import requests
url = 'https://fanyi.baidu.com/sug'
headers = {
'Accept': 'application/json, ext/javascript, */*; q=0.01',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36 FS',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest'
}
key = input("请输去你要翻译的文字")
data = {
'kw':key
}
resp = requests.post(url=url,headers=headers,data=data)
a = json.loads(resp.content.decode('utf-8'))
print(a)
4、在requests模块中使用代理发送请求
什么是代理
代理概述
- 玩爬虫为什么我们不能使用一个固定IP发送请求
你使用一个固定IP发送每秒向对方服务器发送10几个请求,对方认为这样操作不是人干的, 就把你IP给封了
服务器端的人可以根据你IP很快锁定你, 要求你对这种窃取行为赔偿.
- 代理
正向代理与反向代理
正向代理与反向代理的区别
- 反向代理:
- 服务器端知道代理的存在,反向代理是为了保护服务器或负责负载均衡
但是客户端不知道代理的存在的
- 服务器端知道代理的存在,反向代理是为了保护服务器或负责负载均衡
- 正向代理:
- 客户端知道代理的存在,正向代理是为保护客户端,防止追究责任.
但是服务端不知道真实的客户端
- 客户端知道代理的存在,正向代理是为保护客户端,防止追究责任.
代理IP的分类
根据代理ip的匿名程度,代理IP可以分为下面四类:
透明代理(Transparent Proxy):透明代理虽然可以直接“隐藏”你的IP地址,但是还是可以查到你是谁。
匿名代理(Anonymous Proxy):使用匿名代理,别人只能知道你用了代理,无法知道你是谁。
高匿代理(Elite proxy或High Anonymity Proxy):高匿代理让别人根本无法发现你是在用代理,所以是最好的选择。
在使用的使用,毫无疑问使用高匿代理效果最好
从请求使用的协议可以分为:
http代理
https代理
socket代理等
不同分类的代理,在使用的时候需要根据抓取网站的协议来选择
在requests模块中使用代理(重点)
在requests模块中使用代理格式(重点)
proxies = {
"http":"http://ip:端口号"
"https":"https://ip:端口号"
}
request.get(url, proxies=proxies)
使用需要账号和密码的代理
proxies = {
"http":"http://username:password@ip:端口号"
"https": "https://username:password@ip:端口号"
}
request.get(url, proxies=proxies)
实例
import requests
# 使用代理获取百度首页
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"
}
# 定义代理的字典
proxies = {
# "http":"http://14.118.252.64:6666"
# "https":"https://114.99.30.126:18118"
"http": "http://121.228.8.93:8118"
}
# 使用代理给服务器发送请求
response = requests.get("https://www.baidu.com", proxies=proxies, headers=headers)
# 获取状态
print(response.status_code)
print(response.content.decode())
代理IP使用的注意点
反反爬
使用代理ip是非常必要的一种反反爬的方式
但是即使使用了代理ip,对方服务器任然会有很多的方式来检测我们是否是一个爬虫,比如:
一段时间内,检测IP访问的频率,访问太多频繁会屏蔽
检查Cookie,User-Agent,Referer等header参数,若没有则屏蔽
服务方购买所有代理提供商,加入到反爬虫数据库里,若检测是代理则屏蔽
所以更好的方式在使用代理ip的时候使用随机的方式进行选择使用,不要每次都用一个代理ip
代理ip池的更新
购买的代理ip很多时候大部分(超过60%)可能都没办法使用,这个时候就需要通过程序去检测哪些可用,把不能用的删除掉。
使用代理的好处:
让目标服务器以为不是同一个客户端在请求,防止因为同一个ip发送请求过多而被反爬
防止我们的真实地址被泄露,防止被追究,保护自己
随机代理案例
我们说使用代理的目标是为了避免同一个IP发送请求频率过高而被识别为爬虫对吧,我们上面演示中是不是还是使用一个IP; 那么我们在实际开发中是怎么玩的呢?
准备一个代理池, 使用的时候随机获取一个来用
思路:
准备一个代理的列表
从列表中随机取出一个代理
使用这个代理发送请求
import random
import requests
# 1. 准备代理列表
proxies = [
{'http': '121.8.98.198:80'},
{'http': '39.108.234.144:80'},
{'http': '125.120.201.68:808'},
{'http': '120.24.216.39:60443'},
{'http': '121.8.98.198:80'},
{'http': '121.8.98.198:80'}
]
# 2. 随机选出一个代理
for i in range(0, 10):
proxy = random.choice(proxies)
print(proxy)
try:
response = requests.get("http://www.baidu.com", proxies=proxy, timeout=3)
print(response.status_code)
except Exception as ex:
print("代理有问题: %s" % proxy)