使用Requests进行爬网
一 HTTP协议
1.1 http协议概述
HTTP
是Hyper Text Transfer Protocol
(超文本传输协议)的缩写。HTTP
是一个基于"请求与响应"模式的, 无状态的应用层协议。http协议在TCP/IP协议栈中的位置如下图:
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS, 默认HTTP的端口号为80,HTTPS的端口号为443.
1.2 http的请求响应模型
http协议永远都是客户端发起请求, 服务器回送响应. 如下图所示:
http协议时一个无状态协议, 也就是说同一个客户端的这次请求和上次请求没有对应关系.
1.3 工作流程
一次http操作一般包括如下几步:
-
首先客户机与服务器建立连接.
-
客户机向服务器发送一个资源请求
-
服务器接到请求后, 给予相应的响应
-
客户端接收返回信息后进行解析
1.4 http请求方法
HTTP/1.1
协议中共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源, 下面是常用的请求方法:
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URL位置资源的响应消息报告, 即获得该资源的头部信息 |
POST | 请求向URL位置的资源后附件新的数据 |
PUT | 请求向URL位置存储一个资源, 覆盖原URL位置的资源 |
PATCH | 请求局部更新URL位置的资源, 即改变该处资源的部分内容 |
DELETE | 请求删除URL位置存储的资源 |
1.5 URL
URL
(Uniform Resource Location) 统一资源定位符, 也就是网页地址. 是互联网上标准的资源的地址.
HTTP
协议采用URL
做为定位资源的标识.
1.5.1 URL
格式
http://host[:port][path]
-
host
: 合法的Internet主机域名或者IP地址. -
port
: 端口号, 缺省端口为80 -
path
: 请求资源的路径
1.5.2 URL
示例
https://www.cup.edu.cn/
指的是中国石油大学(北京)的校园网主页.
https://www.cup.edu.cn/cise
指的是中国石油大学(北京)这个主机域名下cise
目录下的资源, 也就是信息科学与工程学院的主页
URL
可以这样理解: 它是HTTP协议存取资源的Internet
路径, 一个URL
对应一个数据资源.
二 Requests库
Requests
是Python的一个优雅而简单的HTTP库,是为人类构建的。通过requests
可以非常容易的发送http/1.1
请求, 不需要将查询字符串添加到url
, 也不需要对post
数据进行表单编码.
requests
是一个第三方库, 因此要使用前必须进行安装. 建议大家使用anaconda
集成环境, 里面已经安装了requests
库及其依赖.
2.1 Request
和Response
对象
无论何时调用requests.get()
及其伙伴方法, 实际上都是正在做两件主要事情: 首先你正在构建一个Request
对象, 它将被发送到服务器以便请求或查询一些资源. 其次, 一旦请求从服务器获得响应, 就会生成Response
对象. 它包含服务器返回的所有信息, 还包含最初创建的Request
对象.
下面是一个请求示例, 用来从https://httpbin.org
获取一些信息:
>>> import requests
>>> r=requests.get("https://httpbin.org")
如果我们希望访问服务器返回的头部信息, 可以通过如下代码:
>>> r.headers
{'Date': 'Sun, 18 Apr 2021 11:15:57 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '9593', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}
然而, 如果我们想要获取发送给服务器的头部信息, 就要通过响应的request
对象来访问了, 比如:
>>> r.request.headers
{'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
2.2 主要接口
所有Requests
的功能都可以通过7中方法访问. 它们都返回Response
对象的一个实例. 其中requests.request()
方法最为重要, 是所有其它6个方法的基础.
2.2.1 requests.request(method, url, **kwargs)
该方法用于构造和发送一个Request
.
参数:
-
method: 也就是发送的
HTTP
请求, 可以是GET
,HEAD
,POST
,PUT
等 -
url
:HTTP
请求的地址. -
**kwargs
: 控制访问的参数, 均为可选项
返回值:requests.Response
2.2.1.1 request控制参数
**kwargs
一共有13个可选参数, 分别说明如下:
params
: 字典或字节序列, 做为参数增加到url
中.
>>> import requests
>>> r = requests.request('GET','https://httpbin.org', params={'key1':'val1', 'key2':'val2'})
>>> print(r.url)
https://httpbin.org/?key1=val1&key2=val2
data
: 字典, 字节序列或文件对象, 做为Request
的内容
>>> import requests
>>> r = requests.post('https://httpbin.org/post', data={'key':'value'})
>>> r = requests.post('https://httpbin.org/post', data='main content')
json
: JSON格式数据, 做为Request
的内容
>>> import requests
>>> kv = {'key':"value"}
>>> r = requests.request('POST', 'http://python123.io/ws', json=kv)
headers
: 字典, HTTP定制头
>>> hd={'user-agent': "Chrome/10"} # 浏览器伪装
>>> r = requests.request('POST', 'http://python123.io/ws', headers=hd)
>>> r.request.headers
{'user-agent': 'Chrome/10', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '0'}
-
cookies
: 字典或CookieJar
,Request
中的cookie
-
auth
: 元组, 支持HTTP认证功能. -
files
: 字典类型, 传输文件.
>>> fs = {'file': open('data.xls', 'rb')}
>>> r = requests.request('POST', 'http://python123.io/ws', files=fs)
timeout
: 设定的超时时间, 秒为单位
>>> r = requests.request('GET', 'http://www.baidu.com', timeout=10)
proxies
: 字典类型, 设定访问代理服务器, 可以增加登录认证, 使用代理可以增加反爬溯源的难度
>>> pxs = {'http': 'http://user:pass@10.10.10.1:1234', 'https':'https://10.10.10.1:4321'}
>>> r = requests.request('GET', 'http://www.baidu.com', proxies=pxs)
-
allow_redirects
: True/False, 默认为True, 重定向开关 -
stream
:True/False, 默认为True,获取内容立即下载开关 -
verify
:True/False, 默认为True, 认证SSL证书开关 -
cert
: 本地SSL证书路径
2.2.2 requests.get(url, params=None, **kwargs)
以HTTP的GET方式发起请求.
主要参数:
-
url
: 拟获取页面的url
链接 -
params
:url
中的额外参数, 字典或字节流格式, 可选 -
**kwargs
: 12个控制范围参数参数
返回值:requests.Response
2.2.3 requests.head(url, **kwargs)
以HTTP的HEAD
方式发起请求.
参数:
-
url
: 拟获取页面的url
链接 -
**kwargs
: 13个访问控制参数
返回值:requests.Response
2.2.4 requests.post(url, data=None, json=None, **kwargs)
以HTTP的POST
方式发起请求.
参数:
-
url
: 拟获取页面的url
链接 -
data
: 字典, 字节序列或文件对象, 做为Request
的内容 -
json
: JSON格式数据, 做为Request
的内容 -
**kwargs
: 11个访问控制参数
返回值:requests.Response
2.2.5 requests.put(url, data=None, **kwargs)
以HTTP的PUT
方式发起请求.
参数:
-
url
: 拟获取页面的url
链接 -
data
: 字典, 字节序列或文件对象, 做为Request
的内容 -
json
: JSON格式数据, 做为Request
的内容 -
**kwargs
: 11个访问控制参数
返回值:requests.Response
2.2.6 requests.patch(url, data=None, **kwargs)
以HTTP的PATCH
方式发起请求.
参数:
-
url
: 拟获取页面的url
链接 -
data
: 字典, 字节序列或文件对象, 做为Request
的内容 -
json
: JSON格式数据, 做为Request
的内容 -
**kwargs
: 11个访问控制参数
返回值:requests.Response
2.2.7 requests.delete(url, **kwargs)
以HTTP的delete
方式发起请求.
参数:
-
url
: 拟获取页面的url
链接 -
**kwargs
: 13个访问控制参数
返回值:requests.Response
三 案例: 爬取慕课网所有免费课程的封面图片
通过打开网址https://www.imooc.com/course/list
并且审查其中每一张图片地址的格式,然后确定图片地址的模式, 整个爬取代码如下:
import requests
import re
url = "https://www.imooc.com/course/list"
# 请求内容
try:
r = requests.get(url)
r.raise_for_status()
except:
print("发生错误了!")
else:
r.encoding = r.apparent_encoding
# 将内容保存在变量html中
html = r.text
# 使用正则表达式, 查找所有图片的地址
images=re.findall(r'//img\d\.mukewang\.com/\w+\.(?:png|jpg)', html)
# 构造完整的地址
images_url=['https:' + url for url in images]
i = 0
for url in images_url:
# 截取图片文件的后缀
prefix = re.search(r'\w{3}$',url).group()
# 构造下载后图片文件存储位置, images提前建立好的
filename = './images/{}.{}'.format(i,prefix)
# 针对每一张图片,爬取其二进制内容, 并存入本地文件
f = open(filename, 'wb')
r = requests.get(url)
f.write(r.content)
f.close()
i += 1
上述代码存在两个不尽人意的地方: 一是要提前建立本地存放图片的目录, 二是命名图片的时候引入了一个变量i
.下面是针对这两个问题的优化代码:
import requests
import re
import os
url = "https://www.imooc.com/course/list"
# 请求内容
try:
r = requests.get(url)
r.raise_for_status()
except:
print("发生错误了!")
else:
r.encoding = r.apparent_encoding
# 将内容保存在变量html中
html = r.text
# 使用正则表达式, 查找所有图片的地址
images=re.findall(r'//img\d\.mukewang\.com/\w+\.(?:png|jpg)', html)
# 构造完整的地址
images_url=['https:' + url for url in images]
# 构造存放图片的目录
if not os.path.isdir('imgs'):
os.mkdir('imgs')
else:
os.chdir('./imgs')
for f1 in os.listdir():
os.remove(f1)
os.chdir('..')
# 使用enumerate方法遍历列表
for i, url in enumerate(images_url):
prefix = re.search(r'\w{3}$',url).group()
filename = './imgs/{}.{}'.format(i,prefix)
f = open(filename, 'wb')
r = requests.get(url)
f.write(r.content)
f.close()
欢迎大家收看Python视频课程:https://www.bilibili.com/video/BV1sh411Q7mz