Python 爬虫网络请求模块下——requests

requests

上一节我们了解了 urllib 的基本用法,但是其中确实有不方便的地方,比如处理网页验证,需要写Opener 和 Handler 来处理。为了更加方便的实现这些操作,就有了更为强大的库requests 。

基本用法

安装模块

我们可以使用pip这个包管理工具进行安装
在命令行界面中运行如下命令,即可完成requests的安装:pip install requests

GET 请求

HTTP 中最常见的请求之一就是GET 请求,下面首先来详细了解一下利用 requests 构建 GET 请求的方法。

基本案例

首先,构建一个最简单的GET请求,请求的链接为 http://httpbin.org/get ,如果客户端发起的是GET请求,它会返回相应的请求信息.

import requests

r = requests.get('http://httpbin.org/get')
print(r.text)

运行结果如下:

{
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0",
    "X-Amzn-Trace-Id": "Root=1-6035f9e5-032660f21bf786da00a9d1b8"
  },
  "origin": "122.4.214.253",
  "url": "http://httpbin.org/get"
}

可以发现,我们成功发起了GET请求,返回结果中包含请求头、URL、IP等信息。

那么,对于GET请求,如果要附加额外的信息,一般怎样添加呢? 一般情况下,这种信息会用字典来存储,我们可以利用params 参数来构造这个链接:

import requests

params = {'name': 'william',
          'age': 20,
          }
r = requests.get('http://httpbin.org/get', params=params)
print(r.text)

在这里插入图片描述

通过运行结果可以判断,请求的链接自动被构造成我们想要的链接。

网页的返回类型实际上是 str 类型,但它很特殊,是JSON 格式的字符串,我们可以通过调用json() 方法,将其转化为字典格式。示例如下:

import requests

r = requests.get('http://httpbin.org/get')
print(type(r.text))
print(r.json())
print(type(r.json()))

运行结果如下:

<class 'str'>
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-60360190-3b8a59ad66a473680a73907a'},or7.47.253', ' 'origin': '122.4.214.253', 'url': 'http://httpbin.org/get'}
<class 'dict'>

需要注意的是,如果返回的结果不是JSON格式,便会出现解析错误,抛出异常。

抓取网页

上面请求的链接返回的是JSON形式的字符串,那么如果请求普通的网页,则肯定能获取相应的内容,下面以‘知乎’>>>‘发现’ 页面为例来看一下:

import requests,re

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74',
}
r = requests.get('https://www.zhihu.com/explore', headers=headers)

lst = re.findall(r'class="ExploreSpecialCard-contentTitle".*?>(.*?)</a>',r.text,re.S)
print(lst)

这里我们加入了 headers 信息,其中包含了User-Agent 字段信息,如果不加,知乎会禁止爬取。
同时,我们采用正则表达式来进行数据解析。

运行结果如下:

['自己的车被借走后遇到过哪些坑爹事?', '借车出了车祸,车主要负责吗?这几种情况说啥都别借,否则赔惨你', '关于借车的一点忠告', '肖战 227 事件过去一周年,娱乐圈因此受到了怎样的影响?你对「饭圈」与「流量」的 
看法有哪些改变?', '马上 227 事件一周年,你有什么想说的吗?', '如何应对非专业人士由于拥有大量流量而抢 
占专业话语权?这是新媒体时代要面对的必然问题吗?', '你觉得哪些APP在「偷窥」你?', '你在买车(含新能源 
汽车)过程中遭遇过哪些坑?你的车有哪些质量问题难解决?维权的主要难题是什么?', '你的家电产品遇到过哪些
质量问题?维修乱收费怎么办?', '旅行中有一个对的旅伴有多重要?', '有哪些适合两个人旅游拍照的姿势?', '为什么旅行是检验情侣的重要标准?']
抓取二进制数据

上一个例子,我们抓取的是知乎的一个页面,实际上就是一个HTML文档。那么如果想抓取图片、音频、视频等文件,应该怎么办呢?

这些文件本质上都是由二进制码组成的,由于有特定的保存格式和对应的解析方式,我们才可以看到这些形形色色的多媒体。所有,我们需要拿到它们的二进制数据。
下面以GitHub 的站点图标为例来看一下:

import  requests

r = requests.get('https://github.com/favicon.ico')
print(r.content)

这里打印了Response 对象的content 属性值,可以看到结果是bytes 类型的数据,这是因为图片是二进制数据。

保存图片

将刚才提取到的图片保存下来:

import  requests

r = requests.get('https://github.com/favicon.ico')
with open('favicon.ico', 'wb') as f:
    f.write(r.content)

运行结束后,可以发现文件夹中出现了名为favicon.ico 的图标。
同样的,音频和视频文件也可以用这种方法获取。

POST 请求

使用 request 实现 POST 请求同样非常简单,示例如下:

import  requests

data = {'name':'william','age':20}
r = requests.post('http://httpbin.org/post', data=data)
print(r.text)

在这里,我们向 http://httpbin.org/post 发起请求,该网站可以判断如果请求方式是POST 方式,就把相关请求信息返回。

其中 form 部分就是提交的数据,这就证明POST 请求成功发送了。

响应

发送请求后,得到的自然就是响应。我们可以使用 text 和 content 获取响应内容。此外,我们还可以获取状态码、响应头、Cookies 等。

import  requests

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74',}

r = requests.get('http://www.jianshu.com',headers=headers)
print(r.status_code)
print(r.headers)
print(r.cookies)
print(r.url)
print(r.history)

这里分别打印输出 status_code 属性得到状态码,输出 headers 属性得到响应头,等等…

运行结果如下:

<class 'requests.models.Response'> <Response [200]>
<class 'int'> 200
<class 'requests.structures.CaseInsensitiveDict'> {'Date': 'Sun, 28 Feb 2021 08:38:39 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'ETag': 'W/"bfa94fd14ed092029dfd7519ba9e9220"', 'Cache-Control': 'max-age=0, private, must-revalidate', 'Set-Cookie': 'locale=zh-CN; path=/', 'X-Request-Id': '08e2b8d5-b292-452a-9ce7-25214d44cefe', 'X-Runtime': '0.003455', 'Content-Encoding': 'gzip'}
<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[<Cookie locale=zh-CN for www.jianshu.com/>]>
<class 'str'> https://www.jianshu.com/
<class 'list'> [<Response [301]>]
拓展

状态码常用来判断请求是否成功, 而 requests 还提供了一个内置的状态码查询对象 requests.codes ,示例如下:

import requests

r = requests.get('http://www.jianshu.com')
print('Request Successfully') if r.status_code == requests.codes.ok else exit()

通过比较返回的状态码和内置的成功的返回码,可以判定请求是否得到正常响应,否则程序终止。

高级用法

在前面我们了解了 requests 的基本用法, 如基本的 GET、POST 请求以及 Reponse 对象。下面,我们来了解下 requests 的一些高级用法,如文件上传、代理设置等。

文件上传

我们知道 requests 可以模拟提交一些数据。假如有的网站需要上传文件,我们也可以用它来实现,示例如下:

import requests

files = {'file': open('favicon.ico', 'rb')}
r = requests.post('http://httpbin.org/post', files=files)
print(r.text)

在前一节中我们保存了一个文件 favicon.ico ,这次用它来模拟文件上传的过程。

运行结果如下:

{
  "args": {},
  "data": "",
  "files": {
    "file": "data:application/octet-stream;base64,AAABAAIAEBAAAAEAIAAoBQAAJgAAACAgAA...="
  },
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "6665",
    "Content-Type": "multipart/form-data; boundary=7be2b84f34fc665ed1c11111c359b7e6",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.22.0",
    "X-Amzn-Trace-Id": "Root=1-603b5cf4-704d878e53f685ae0a73648d"
  }
}

以上省略部分内容,这个网站会返回响应,里面包含 files 这个字段,而 form 字段是空的,这证明文件上传部分会单独有一个 files 字段来标识。

Cookies

有了 requests ,我们获取和设置 Cookies 只需一步即可完成。

我们先用一个实例看一下获取 Cookies 的过程:

import requests

r = requests.get('https://www.baidu.com')
print(r.cookies)
for key,value in r.cookies.items():
    print(key + '=' + value)

这里我们首先调用 cookies 属性即可成功得到 Cookies,可以发现它是 RequestCookieJar 类型。然后用 items()方法将其转化为元组组成的列表,通过遍历得到每一个Cookie的名称和值。

模拟登陆

当然,我们也可以直接利用Cookie来维持登陆状态。首先登录知乎,将Headers 中的Cookie 复制下来。

这里可以替换成自己的Cookie,将其设置到Headers里面,然后发送请求即可。示例如下:

import requests

headers = {'Cookie': 'q_c1=2356dddhdjdd5655',
           'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74'
           }
 r = requests.get('https://www.zhihu.com', headers=headers)      
 print(r.text)

可以发现,结果中包含了登录后的结果。

当然,你也可以通过 cookies 参数来设置,不过这样需要构造 RequestsCookieJar 对象,而且需要分割一下cookies。效果是相同的。示例如下:

import requests

cookies = 'q_c1=2356dddhdjdd5655'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74'
           }
  jar = requests.cookies.RequestsCookieJar()
  for cookie in cookies.split(';'):
      key, value = cookie.split('=', 1)
      jar.set(key, vale)
 r = requests.get('https://www.zhihu.com', cookies=jar, headers=headers)      
 print(r.text)

这里先创建一个RequestsCookieJar对象,然后使用set()方法设置好每个Cookie的key和value,最后调用get()方法并传递给cookies参数即可。

会话维持

设想这样一个场景,第一个请求利用 post() 方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,你又用了一次 get() 方法去请求个人信息页面。实际上,这相当于打开了两个浏览器,是两个完全不相关的会话,能成功获取个人信息吗?那当然不能。

可能有的小伙伴会说,我在两次请求时设置一样的cookies不就行了?可以,但这样做显得很繁琐,我们有更简单的解决方案。

利用Session对象,我们可以方便的维护一个会话,而不用担心cookies的问题,它会帮我们自动处理好。

import  requests

requests.get('http://httpbin.org/cookies/set/number/123456789')
r = request.get('http://httpbin.org/cookies')
print(r.text)

这里我们请求一个测试网址。在请求这个网址时,可以设置一个cookie,名称叫做number ,内容是123456789,随后又请求了 http://httpbin.org/cookies,此网址可以获取当前的Cookies。

# 运行结果如下
{
  "cookies": {}
}

这并不行,我们再用Session 试试看。

import  requests

s = requests.session()
s.get('http://httpbin.org/cookies/set/number/0123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)

再看下运行结果:

{
  "cookies": {
    "number": "0123456789"
  }
}

成功获取!这下能体会到同一个会话和不同会话的区别了吧。

利用Session,可以做到模拟同一个会话而不用担心Cookies的问题。它通常用于模拟登录成功后再进行下一步的操作。

SSL 证书验证

(具有服务器身份验证和数据传输加密功能)
requests还提供证书验证的功能。当发送HTTP请求时,会检查SSL证书,我们可以使用verify参数控制是否检查此证书。
当证书没有被官方的CA机构信任,就会出现证书验证错误的结果。

现在我们来测试一下:

import requests

response = requests.get('https://inv-veri.chinatax.gov.cn/')
print(response.status_code)

运行结果如下:

requests.exceptions.SSLError: HTTPSConnectionPool(host='inv-veri.chinatax.gov.cn', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)'),))

这里提示一个错误 SSLError,表示证书验证错误。所以当遇到这种情况,就不能正常请求网页,那么如何避免这个错误呢?很简单,把verify 参数设置为False 即可。

import requests

response = requests.get('https://inv-veri.chinatax.gov.cn/', verify=False)
print(response.status_code)

运行结果如下:

InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning,
200

这里报了一个警告,它建议我们给它指定证书。我们可以设置忽略警告的方式来屏蔽这个警告:

import requests
from requests.packages import urllib3

urllib3.disable_warnings()
response = requests.get('https://inv-veri.chinatax.gov.cn/', verify=False)
print(response.status_code)
代理设置

对于某些网站,在测试的时候请求几次,能正常获取到内容。但是一旦大规模爬取,网站可能会弹出验证码,或者跳转到登录认证界面,更或者会封禁客户端IP,导致一定时间段内无法访问。

这就需要用到设置代理来解决这个问题,需要用到proxies参数。可以用这样的方式来设置:

import requests

proxies = {'http': '10.10.1.10:3128',
           'https': '10.10.1.10:1080'
           }
requests.get('https://www.taobao.com', proxies=proxies)

当然,直接运行这个实例可能不行,因为这个代理可能是无效的,请换成自己的有效代理试验一下。

超时设置

在本机网络状况不好或者服务器响应太慢时,我们应当设置一个超时时间,即超过了这个时间还没有响应,那就报错。这需要使用timeout这个参数。这个时间的计算是发出请求到服务器返回响应的时间。

import requests

r = requests.get('http://www.taobao.com', timeout=1)
print(r.status_code)

通过这种方式,我们将超时时间设置为1秒,如果1秒内没有响应,那就抛出异常。

身份认证

在访问网站时,我们可能会遇到这样的认证页面:
在这里插入图片描述
此时可以使用requests自带的身份认证功能,示例如下:

import requests

r = requests.get('http://localhost:5000', auth=('uesrname', 'password'))
print(r.status_code)

如果用户名和密码正确的话,请求时就会自动认证成功,返回200状态码;如果认证失败,则返回401状态码。

拓展(Prepared Request)

类似于urllib里面的Request对象,把请求当作独立的对象来看待。我们用实例来看一下:

from requests import Request, Session

url = 'http://httpbin.org/post'
data = {
    'name': 'william'
}
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74'
}
s = Session()
req = Request('post', url, data=data, headers=headers)
prepared = s.prepare_request(req)
r = s.send(prepared)
print(r.text)

这里我们引入Request,然后构造了一个Request对象,这时需要调用Session的prepare_request()方法将其转化为PreparedRequest对象,然后调用send()方法发送请求即可。
运行结果如下:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "william"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win32; x32) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74",
    "X-Amzn-Trace-Id": "Root=1-603bafe3-32bd02be77c2c1fb2b6360b7"
  "json": null,
  "origin": "183.33.205.155",
  "url": "http://httpbin.org/post"
}

可以看到,我们达到了同样的POST请求效果。

有了这个请求对象,这样在进行队列调度时会非常方便。

官方文档

更多用法请参考requests官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值