Python爬虫学习笔记(三)—— [requests]

24 阅读31分钟

requests,是一个更为强大的类库,可以更高效地完成Cookie、登录验证、代理设置等操作。

首先,安装requests库:

pip3 install requests

若能正常导入,则说明安装成功:

import requests

先通过实例来了解下requests库相较于urllib库的强大

import requests

r = requests.get('https://www.baidu.com/')
print(type(r))
print(r.status_code)
print(type(r.text))
print(r.text[:100])
print(r.cookies)

输出结果:

<class 'requests.models.Response'>
200
<class 'str'>
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charse
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>

这里我们调用get方法实现了与urlopen方法相同的操作,返回一个Response对象,并将其存放在变量r中,然后分别输出了响应的类型、状态码,响应体的类型、内容,以及Cookie。

观察运行结果可以发现,返回的响应类型是 requests.models.Response,响应体的类型是字符串str,Cookie的类型是RequestsCookieJar

import requests
r = requests.get('https://www.httpbin.org/get')
r = requests.post('https://www.httpbin.org/post')
r = requests.put('https://www.httpbin.org/put')
r = requests.delete('https://www.httpbin.org/delete')
r = requests.patch('https://www.httpbin.org/patch')

这里分别用post、put、delete等方法实现了POST、PUT、DELETE等请求。相较urllib的实现来说要简单多了。

GET请求

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

基本实例

import requests

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

输出结果:

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "www.httpbin.org", 
    "User-Agent": "python-requests/2.31.0", 
    "X-Amzn-Trace-Id": "Root=1-661a5280-0574883c4507df700f3edb63"
  }, 
  "origin": "36.155.101.131", 
  "url": "https://www.httpbin.org/get"
}

我们可以使用params参数来向GET请求添加额外的信息:

import requests

data = {
    'name': 'Alan',
    'age': '25'
}
r = requests.get('https://www.httpbin.org/get', params=data)
print(r.text)

输出结果:

{
  "args": {
    "age": "25", 
    "name": "Alan"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "www.httpbin.org", 
    "User-Agent": "python-requests/2.31.0", 
    "X-Amzn-Trace-Id": "Root=1-661a5379-63fa404b41ccb9ae0355bc64"
  }, 
  "origin": "36.155.101.131", 
  "url": "https://www.httpbin.org/get?name=Alan&age=25"
}

网页的返回类型虽然是str类型,但它是JSON格式的。 所以,如果想直接解析返回结果,得到一个JSON格式的数据,可以直接调用json方法。

import requests

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

输出结果:

<class 'str'>
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'www.httpbin.org', 'User-Agent': 'python-requests/2.31.0', 'X-Amzn-Trace-Id': 'Root=1-661a5547-57ca6745605f266d289b6285'}, 'origin': '113.204.120.103', 'url': 'https://www.httpbin.org/get'}
<class 'dict'>

可以发现,调用json方法可以将返回结果(JSON 格式的字符串)转化为字典。 但需要注意的是,如果返回结果不是JSON 格式,就会出现解析错误,抛出 json.decoder. JSONDecodeError 异常。

抓取网页

对普通网页发送请求

下面抓取页面 ssrl.scrape.cente/ 作为演示:

import requests
import re

r = requests.get('https://ssr1.scrape.center')
pattern = re.compile('<h2.*?>(.*?)</h2>', re.S)
titles = re.findall(pattern, r.text)
print(titles)

输出结果:

['霸王别姬 - Farewell My Concubine', '这个杀手不太冷 - Léon', '肖申克的救赎 - The Shawshank Redemption', '泰坦尼克号 - Titanic', '罗马假日 - Roman Holiday', '唐伯虎点秋香 - Flirting Scholar', '乱世佳人 - Gone with the Wind', '喜剧之王 - The King of Comedy', '楚门的世界 - The Truman Show', '狮子王 - The Lion King']

这里我们用最基础的正则表达式来匹配所有的标题内容。

抓取二进制数据

图片、音频、视频这些文件本质上都是由二进制码组成的,所以想要抓取这些媒体文件就要拿到它们的二进制数据。

下面抓取示例网站的站点图标:

import requests

r = requests.get('https://scrape.center/favicon.ico')
print(r.text)
print(r.content)

输出结果:

�     (       @                         W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��W?��X@��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��ZB��X@��W?��W?��W?��W?��W?��W?��W?��X@��R:��J1��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��K2��J1��R:��X@��W?��W?��W?��W?��W?                                 
b'\x00\x00\x01\x00\x01\x00  \x00\x00\x01\x00 \x00\xa8\x10\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00 \x00\x00\x00\x00\x00\x00\x10\x00\x00\x12\x0b\x00\x00\x12\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00W?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?\xeb\xffW?

可以看到,r.text中出现了乱码,r.content 的前面带有一个b代表这是bytes类型的数据。由于图片是二进制数据,所以前者在打印时会转化为str类型,也就是图片直接转化为字符串,理所当然会出现乱码。

上面的运行结果我们并不能看懂,它实际上是图片的二进制数据。不过,我们只需要将刚才提取 到的信息保存下来就好了。

import requests

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

这里用了 open 方法,其第一个参数是文件名称,第二个参数代表以二进制写的形式打开文件,可以向文件里写入二进制数据。

可以发现在文件夹中出现了名为 favicon.ico 的图标,这样,我们就把二进制数据成功保存成了一张图片,这个小图标被我们成功爬取下来了。

添加请求头

要设置请求头Requests Headers,可以使用headers参数

在刚才的实例中,实际上是没有设置请求头信息的,这样的话,某些网站会发现这并不是一个由正常浏览器发起的请求,于是可能会返回异常结果,导致网页抓取失败。

添加一个User-Agent字段:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) ApplewebKit/537.36(KHTML, like Gecko)Chrome/52.0.2743.116 Safari/ 537.36'
}
r = requests.get('https://www.httpbin.org/post', data=headers)
print(r.text)

输出结果:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>

POST请求

另外一种比较常见的请求方式是POST。

使用requests库实现 POST请求

import requests

data = {
    'name': 'Alan',
    'age': '25'
}
r = requests.post("https://www.httpbin.org/post",data=data)
print(r.text)

输出结果:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "age": "25", 
    "name": "Alan"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "16", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "www.httpbin.org", 
    "User-Agent": "python-requests/2.31.0", 
    "X-Amzn-Trace-Id": "Root=1-661a7696-2319599f2fe6076b39913821"
  }, 
  "json": null, 
  "origin": "36.155.101.131", 
  "url": "https://www.httpbin.org/post"
}

我们成功获得了返回结果,其中form部分就是提交的数据,这证明 POST请求成功发送了。

响应 

除了text和content 外,还有很多属性和方法可以用来获取其他信息,例如状态码、响应头、Cookie 等。

import requests

r = requests.get('https://ssr1.scrape.center/')
print(type(r.status_code), r.status_code)
print(type(r.headers), r.headers)
print(type(r.cookies), r.cookies)
print(type(r.url), r.url)
print(type(r.history), r.history)

这里通过status_code属性得到状态码、通过headers 属性得到响应头、通过 cookies 属性得到Cookie、通过url属性得到 URL、通过history 属性得到请求历史。并将得到的这些信息分别打印出来。

输出结果:

<class 'int'> 200
<class 'requests.structures.CaseInsensitiveDict'> {'Date': 'Sat, 13 Apr 2024 12:28:58 GMT', 'Content-Type': 'text/html; charset=utf-8', 'X-Frame-Options': 'DENY', 'X-Content-Type-Options': 'nosniff', 'Expires': 'Sat, 13 Apr 2024 12:35:15 GMT', 'Strict-Transport-Security': 'max-age=15724800; includeSubDomains', 'Server': 'Lego Server', 'X-Cache-Lookup': 'Cache Miss, Cache Miss', 'Cache-Control': 'max-age=600', 'Age': '0', 'Content-Length': '41667', 'X-NWS-LOG-UUID': '6014845362198123169', 'Connection': 'keep-alive'}
<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[]>
<class 'str'> https://ssr1.scrape.center/
<class 'list'> []

headerscookies 这两个属性得到的结果分别是CaseInsensitiveDictRequestsCookie]ar 对象。

requests 库还提供了一个内置的状态码查询对象 requests.codes

import requests

r = requests.get('https://ssr1.scrape.center/')
exit() if not r.status_code == requests.codes.ok else print("Requests Successfully")

这里通过比较返回码和内置的表示成功的状态码,来保证请求是否得到了正常响应,如果是,就输出请求成功的消息,否则程序终止运行,这里我们用 requests.codes.ok 得到的成功状态码是 200。

高级用法

文件上传

可以使用requests库实现文件的上传

import requests

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

输出结果:

{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "data:application/octet-stream;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABILAAASCwAAAAAAAAAAAABXP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1hA6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WEDr/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9YQOv/Ujrq/0ox6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0ox6v9SOur/WEDr/1c/6/9XP+v/Vz/r/1c/6/9XP+v/WEDr/1I66v9yXe7/n5H0/5qK8/+bi/P/m4vz/5uL8/+bi/P/m4vz/5uL8/+bi/P/m4vz/5uL8/+bi/P/m4vz/5uL8/+bi/P/m4vz/5uL8/+bi/P/m4vz/5uL8/+ZivP/n5H0/3Jd7v9SOur/WEDr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///////////////////////////////////////////////////////////////////////////////////////////////////z8////////mozz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mYrz///////6+f7//Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P//+vn+//////+aivP/SzLq/1pC6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8//////////////7+///+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///+/v/////////////8/P///////5uL8/9LMur/WkLr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///////////////////////////////////////////////////////////////////////////////////////////////////z8////////m4vz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P///v7///////+VhfL/dF/v/3tn7/96Zu//embv/3pm7/96Zu//embv/3pm7/96Zu//embv/3tn7/90X+7/lYXz///////+/v///Pz///////+bi/P/SzLq/1pC6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8///+/v///////3Rg7v9HLen/UTjq/0826v9PNur/Tzbq/0826v9PNur/Tzbq/0826v9PNur/UTjq/0cu6f90X+7///////7+///8/P///////5uL8/9LMur/WkLr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///7+////////e2jv/1E46v9aQ+v/WUHr/1lC6/9bROz/W0Ts/1tE7P9bROz/W0Ts/1tE7P9dRuz/VDzr/31q8P///////v7///z8////////m4vz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P///v7///////96Zu7/Tzbq/1lB6/9YQOv/VDzr/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0006v9DKen/cVzu///////+/v///Pz///////+bi/P/SzLq/1pC6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8///+/v///////3pm7v9PNur/WULr/1Q76/9mT+v/morz/5iI8/+YiPP/mIjz/5iI8/+YiPP/mYn0/5SD8/+toPb////////////8/P///////5uL8/9LMur/WkLr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///7+////////embu/0826v9aQ+v/Tzbr/3xq6f////7//v7///////////////////////////////////////////////////z8////////m4vz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P///v7///////96Zu7/Tzbq/1pD6/9PNuv/e2rq/////v/7+////Pz///z8///8/P///Pz///z8///8/P///f3//////////////Pz///////+ai/P/SzLq/1pC6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8///+/v///////3pm7v9PNur/WkPr/0826/98aur////+//39///+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///7+////////5qL8/9LMur/WkLr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///7+////////embu/0826v9aQ+v/Tzbr/3xq6f////7//v7//////v////7////+/////v////7////+/////v////7////+//z8/v//////m4zz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P///v7///////96Zu7/Tzbq/1lB6/9VPev/X0np/31s6f98aur/fGrq/3xq6v98aur/fGrq/3xq6v98aur/fGrq/3xq6v98aur/e2rq/39t6v9mUOr/VDzr/1hA6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8///+/v///////3pm7v9PNur/WUHr/1c/6/9VPev/TzXr/0826/9PNuv/Tzbr/0826/9PNuv/Tzbr/0826/9PNuv/Tzbr/0826/9PNuv/TjXr/1Q76/9YQOv/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///7+////////e2jv/1E46v9aQ+v/WUHr/1lB6/9bQ+v/WkPr/1pD6/9aQ+v/WkPr/1pD6/9aQ+v/WkPr/1pD6/9aQ+v/WkPr/1pD6/9bQ+v/WEHr/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P///v7///////90YO7/SC3p/1E46v9PNur/Tzbq/0826v9PNur/Tzbq/0826v9PNur/Tzbq/0826v9PNur/Tzbq/0826v9PNur/UDfq/0826v9UPOv/WEDr/1c/6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ajPP///////z8///+/v///////5WF8v9zYO7/e2jv/3pm7/96Zu//embv/3pm7/96Zu//embv/3pm7/96Zu//embv/3pm7/96Zu//embv/3pm7/95Zu//fGnv/2RP7f9VPOv/WEDr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///////////////////////////////////////////////////////////////////////////////////////////////////z8////////mozz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1pC6/9LMur/mozz///////8/P/////////////+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///+/v///v7///7+///+/v///Pv///////+ai/P/SzLq/1pC6/9XP+v/Vz/r/1c/6/9XP+v/WkLr/0sy6v+ZivP///////r5/v/8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///8/P///Pz///z8///6+f7//////5mK8/9LMur/WkLr/1c/6/9XP+v/Vz/r/1c/6/9aQuv/SzLq/5qM8////////Pz///////////////////////////////////////////////////////////////////////////////////////////////////z8////////m4zz/0sy6v9aQuv/Vz/r/1c/6/9XP+v/Vz/r/1hA6/9SOur/cl7u/5+R9P+ZivP/mozz/5qM8/+ajPP/mozz/5qM8/+ajPP/mozz/5qM8/+ajPP/mozz/5qM8/+ajPP/mozz/5qM8/+ajPP/mozz/5qM8/+ajPP/mYrz/5+R9P9yXe7/Ujrq/1hA6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1hA6/9SOur/SjHq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SzLq/0sy6v9LMur/SjHq/1I66v9YQOv/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1hA6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WkLr/1pC6/9aQuv/WEDr/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/Vz/r/1c/6/9XP+v/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "4433", 
    "Content-Type": "multipart/form-data; boundary=ee7496a8216f8acf00c95effe674b943", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.31.0", 
    "X-Amzn-Trace-Id": "Root=1-661a7fe7-665610bd00c28a1731f7396b"
  }, 
  "json": null, 
  "origin": "112.21.165.182", 
  "url": "https://httpbin.org/post"
}

上传文件后,网站会返回响应,响应中包含files字段和form字段,而form字段是空的,这证明文件上传部分会单独用一个 files 字段来标识。

Cookie设置

可以用Cookie来维持登录状态。

这里我将CSDN请求头中的Cookie复制下来,并设置到请求头里 ​转存失败,建议直接上传图片文件​​

import requests

headers = {
    'Cookie': '__bid_n=187faf08a155b24d9a4207; FPTOKEN=XYDlVZfbfx+S+kSteOCZfyDmIsAdtk8BNyj/0rulzDbVijqYqg6QAgER3I8TkehrAPRDAA3hT2cRLnNs2CZIGK+TCze/s0TdGaFXDukJGs18dHaym6PIir4jyqZz66qQZZDvZjTWA+MXb1GL8z/33GRVnYEgH2vrNJjC7Qlw5b+NR59CjPmA2ncB/Js3Z833yLsnNjRDUOGaB/9FFaKbdttASfb8JHYhgRNOd00jLQmLp4XdiZFNUPiV4fg7XdDCIFiiqEkWWM0h8Ob9uTaCIUQ6/LFlm/52/tqjSkcGT4nSFXOQGn0zR+h1467KKOP3Ku8DX4FbEnkfGjdWhbIlPhlGJDkl90Gjr2F3TYSRG6hRq2X+aICmu4sTsIgfDJ6Gi1lsFduZLe1jECuWGAIlQg==|OoAruOtrEJzTeMh9NyaLDAqt/q3j9fGP+VP91g+qWVY=|10|7d66efcbc045e60715f7cf87697b2ed6; UN=2302_78240669; Hm_lvt_e5ef47b9f471504959267fd614d579cd=1693722602; __gads=ID=13dd1d056399f9ed-22fbb0ff9fe00060:T=1683542280:RT=1695471462:S=ALNI_MaPzoL9tmBbigRvNL1iBKbTVB7gCw; __gpi=UID=00000c035844dae2:T=1683542280:RT=1695471462:S=ALNI_MYwI8XR6VG7bSXPplgeIS8r2F724Q; FCNEC=%5B%5B%22AKsRol-HMg57ynuth-6AOhWcIymSMN-qwwlevMMtQ_R_JeAPwx36m00E6Ygfq4TZN5D9mfVoM7tfPnB2cVxLRpX9ljj_WIzfHh_yALZbu3XVPP3IbGOg9r4x4H1vMNUQpB8ubvtiyHzYeV-mNhX4Do-d1YxUyo7vRg%3D%3D%22%5D%2Cnull%2C%5B%5D%5D; UserName=2302_78240669; UserInfo=b4434c6943014f56be6659f9b0269ee2; UserToken=b4434c6943014f56be6659f9b0269ee2; UserNick=%E5%8D%A1%E5%88%A9-%E5%AE%89; AU=B22; BT=1704093206853; p_uid=U010000; Hm_up_6bcd52f51e9b3dce32bec4a3997715ac=%7B%22islogin%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isonline%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isvip%22%3A%7B%22value%22%3A%220%22%2C%22scope%22%3A1%7D%2C%22uid_%22%3A%7B%22value%22%3A%222302_78240669%22%2C%22scope%22%3A1%7D%7D; historyList-new=%5B%5D; fpv=7794da9c5f03ee447eb04f5092d4c28e; c_dl_fref=https://so.csdn.net/so/search; c_adb=1; uuid_tt_dd=10_21307064330-1711270915914-299878; c_ins_prid=-; c_ins_rid=1712708849130_495126; c_ins_fref=https://www.csdn.net/; c_ins_fpage=/index.html; c_ins_um=-; ins_first_time=1712708851117; csrfToken=UIt_sIndea9Kc4ThvMJzJPO4; c_first_ref=default; c_first_page=https%3A//www.csdn.net/; c_segment=6; www_red_day_last=red; dc_sid=0dc59fd078867f619a2440315d4ef506; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1712708843,1712842012,1712916678,1712975317; _clck=bozxsa%7C2%7Cfkw%7C0%7C1547; SidecHatdocDescBoxNum=true; c_dl_prid=1709792516566_443788; c_dl_rid=1712976018478_529932; c_dl_fpage=/download/qq_57421630/77493872; c_dl_um=distribute.pc_search_result.none-task-blog-2%7Eall%7Etop_positive%7Edefault-2-107309591-null-null.142%5Ev100%5Epc_search_result_base6; firstDie=1; toolbar_remind_num=1; dc_session_id=10_1713013356279.102063; c_utm_term=%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5cookie; fe_request_id=1713013915310_4675_6648251; referrer_search=1713013923593; c_pref=default; log_Id_click=2343; c_utm_relevant_index=3; relevant_index=3; c_utm_medium=distribute.pc_search_result.none-task-blog-2%7Eall%7Esobaiduweb%7Edefault-3-130956719.142%5Ev100%5Epc_search_result_base6; c_page_id=default; creativeSetApiNew=%7B%22toolbarImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20231011044944.png%22%2C%22publishSuccessImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20240229024608.png%22%2C%22articleNum%22%3A0%2C%22type%22%3A0%2C%22oldUser%22%3Afalse%2C%22useSeven%22%3Atrue%2C%22oldFullVersion%22%3Afalse%2C%22userName%22%3A%222302_78240669%22%7D; _clsk=dthd7t%7C1713014332889%7C3%7C0%7Cb.clarity.ms%2Fcollect; c_ref=default; c_dsid=11_1713014419310.909393; log_Id_pv=1098;'
              ' Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1713014420;'' log_Id_view=38668; dc_tos=sbvufm',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0'
}
r = requests.get('https://www.csdn.net/',headers=headers)
print(r.text)

结果中包含了登录后才能包含的结果,如果结果中出现了类似的信息,说明用Cookie成功模拟了登录状态,这样就能爬取登录之后才能看到的页面了。

也可以通过cookies参数来设置Cookie的信息,这里我们可以构造一个RequestsCookieJar 对象,然后对刚才复制的Cookie 进行处理以及赋值,

import requests

cookies = '__bid_n=187faf08a155b24d9a4207; FPTOKEN=XYDlVZfbfx+S+kSteOCZfyDmIsAdtk8BNyj/0rulzDbVijqYqg6QAgER3I8TkehrAPRDAA3hT2cRLnNs2CZIGK+TCze/s0TdGaFXDukJGs18dHaym6PIir4jyqZz66qQZZDvZjTWA+MXb1GL8z/33GRVnYEgH2vrNJjC7Qlw5b+NR59CjPmA2ncB/Js3Z833yLsnNjRDUOGaB/9FFaKbdttASfb8JHYhgRNOd00jLQmLp4XdiZFNUPiV4fg7XdDCIFiiqEkWWM0h8Ob9uTaCIUQ6/LFlm/52/tqjSkcGT4nSFXOQGn0zR+h1467KKOP3Ku8DX4FbEnkfGjdWhbIlPhlGJDkl90Gjr2F3TYSRG6hRq2X+aICmu4sTsIgfDJ6Gi1lsFduZLe1jECuWGAIlQg==|OoAruOtrEJzTeMh9NyaLDAqt/q3j9fGP+VP91g+qWVY=|10|7d66efcbc045e60715f7cf87697b2ed6; UN=2302_78240669; Hm_lvt_e5ef47b9f471504959267fd614d579cd=1693722602; __gads=ID=13dd1d056399f9ed-22fbb0ff9fe00060:T=1683542280:RT=1695471462:S=ALNI_MaPzoL9tmBbigRvNL1iBKbTVB7gCw; __gpi=UID=00000c035844dae2:T=1683542280:RT=1695471462:S=ALNI_MYwI8XR6VG7bSXPplgeIS8r2F724Q; FCNEC=%5B%5B%22AKsRol-HMg57ynuth-6AOhWcIymSMN-qwwlevMMtQ_R_JeAPwx36m00E6Ygfq4TZN5D9mfVoM7tfPnB2cVxLRpX9ljj_WIzfHh_yALZbu3XVPP3IbGOg9r4x4H1vMNUQpB8ubvtiyHzYeV-mNhX4Do-d1YxUyo7vRg%3D%3D%22%5D%2Cnull%2C%5B%5D%5D; UserName=2302_78240669; UserInfo=b4434c6943014f56be6659f9b0269ee2; UserToken=b4434c6943014f56be6659f9b0269ee2; UserNick=%E5%8D%A1%E5%88%A9-%E5%AE%89; AU=B22; BT=1704093206853; p_uid=U010000; Hm_up_6bcd52f51e9b3dce32bec4a3997715ac=%7B%22islogin%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isonline%22%3A%7B%22value%22%3A%221%22%2C%22scope%22%3A1%7D%2C%22isvip%22%3A%7B%22value%22%3A%220%22%2C%22scope%22%3A1%7D%2C%22uid_%22%3A%7B%22value%22%3A%222302_78240669%22%2C%22scope%22%3A1%7D%7D; historyList-new=%5B%5D; fpv=7794da9c5f03ee447eb04f5092d4c28e; c_dl_fref=https://so.csdn.net/so/search; c_adb=1; uuid_tt_dd=10_21307064330-1711270915914-299878; c_ins_prid=-; c_ins_rid=1712708849130_495126; c_ins_fref=https://www.csdn.net/; c_ins_fpage=/index.html; c_ins_um=-; ins_first_time=1712708851117; csrfToken=UIt_sIndea9Kc4ThvMJzJPO4; c_first_ref=default; c_first_page=https%3A//www.csdn.net/; c_segment=6; www_red_day_last=red; dc_sid=0dc59fd078867f619a2440315d4ef506; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1712708843,1712842012,1712916678,1712975317; _clck=bozxsa%7C2%7Cfkw%7C0%7C1547; SidecHatdocDescBoxNum=true; c_dl_prid=1709792516566_443788; c_dl_rid=1712976018478_529932; c_dl_fpage=/download/qq_57421630/77493872; c_dl_um=distribute.pc_search_result.none-task-blog-2%7Eall%7Etop_positive%7Edefault-2-107309591-null-null.142%5Ev100%5Epc_search_result_base6; firstDie=1; toolbar_remind_num=1; dc_session_id=10_1713013356279.102063; c_utm_term=%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5cookie; fe_request_id=1713013915310_4675_6648251; referrer_search=1713013923593; c_pref=default; log_Id_click=2343; c_utm_relevant_index=3; relevant_index=3; c_utm_medium=distribute.pc_search_result.none-task-blog-2%7Eall%7Esobaiduweb%7Edefault-3-130956719.142%5Ev100%5Epc_search_result_base6; c_page_id=default; creativeSetApiNew=%7B%22toolbarImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20231011044944.png%22%2C%22publishSuccessImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20240229024608.png%22%2C%22articleNum%22%3A0%2C%22type%22%3A0%2C%22oldUser%22%3Afalse%2C%22useSeven%22%3Atrue%2C%22oldFullVersion%22%3Afalse%2C%22userName%22%3A%222302_78240669%22%7D; _clsk=dthd7t%7C1713014332889%7C3%7C0%7Cb.clarity.ms%2Fcollect; c_ref=default; c_dsid=11_1713014419310.909393; log_Id_pv=1098;Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1713014420;'' log_Id_view=38668; dc_tos=sbvufm'
jar = requests.cookies.RequestsCookieJar()
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0'
}
for cookie in cookies.split(';'):
    key, value = cookie.split('=', 1)
    jar.set(key, value)

r = requests.get('https://www.csdn.net/',cookies=jar, headers=headers)
print(r.text)

这里我们首先新建了一个 RequestCookieJar 对象,然后利用 split 方法对复制下来的 Cookie 内容做分割,接着利用set方法设置好每个Cookie条目的键名和键值,最后通过调用requests库的get方法并把 RequestCookieJar 对象通过 cookies 参数传递,最后即可获取登录后的页面。

Session维持 

直接利用requests库中getpost方法的确可以做到模拟网页的请求,但这两种方法实际上相当于不同的 Session,或者说是用两个浏览器打开了不同的页面。

也就是说,如果要获取登录后的个人信息, 想通过向网页发送两次请求来获取个人页面就要在两次请求中设置相同的Cookie,但有些烦琐。

在请求中维持同一个Session可以高效便捷地解决这个问题。利用Session对象,我们可以方便地维护一个Session,而且不用担心Cookie的问题。

import requests

requests.get("https://www.httpbin.org/cookies/set/number/123456789")
r = requests.get("https://www.httpbin.org/cookies")
print(r.text)

这里我们请求了一个测试网址https:/www.htpbin.org/cookies/setnumber/123456789,请求这个网址时,设置了一个Cookie条目,名称是number,内容是123456789。随后又请求了www.httpbin.org/cookies,以获取当前的 Cookie 信息。

输出结果:

{
  "cookies": {}
}

可以发现无法获取Cookie

下面使用Session:

import requests

s = requests.Session()
s.get("https://www.httpbin.org/cookies/set/number/123456789")
r = s.get("https://www.httpbin.org/cookies")
print(r.text)

输出结果:

{
  "cookies": {
    "number": "123456789"
  }
}

可以看到Cookie 被成功获取了!

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

Session 在平常用得非常广泛,可以用于模拟在一个浏览器中打开同一站点的不同页面。

SSL证书验证

现在很多网站要求使用HTTPS协议,但是有些网站可能并没有设置好HTTPS证书,或者网站的HTTPS 证书可能并不被CA机构认可,这时这些网站就可能出现 SSL证书错误的提示。

例如这个实例网站:https:/ssr2.scrape.center/,如果用Chrome浏览器打开它,则会提示“您的连接不是私密连接”这样的错误 ​转存失败,建议直接上传图片文件

当我们用requests库来请求这个网站:

import requests

response = requests.get("https://ssr2.scrape.center/")
print(response.status_code)

输出结果:

requests.exceptions.SSLError: HTTPSConnectionPool(host='ssr2.scrape.center', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:997)')))

可以看到,直接抛出了 SSLError 错误,原因是我们请求的URL的证书是无效的。

我们可以使用 verify 参数控制是否验证证书,如果将此参数设置为False,那么在请求时就不会再验证证书是否有效。如果不设置verify参数,其默认值是True,会自动验证。

import requests

response = requests.get("https://ssr2.scrape.center/", verify=False)
print(response.status_code)

输出结果:

D:\Python3.10.2\lib\site-packages\urllib3\connectionpool.py:1095: InsecureRequestWarning: Unverified HTTPS request is being made to host 'ssr2.scrape.center'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(
200

其中报了一个警告,它建议我们给它指定证书。

我们可以通过设置忽略警告的方式来屏蔽这个警告:

import requests
from requests.packages import urllib3

urllib3.disable_warnings()
response = requests.get("https://ssr2.scrape.center/", verify=False)
print(response.status_code)

或者通过捕获警告到日志的方式忽略警告:

import logging
import requests

logging.captureWarnings(True)
response = requests.get("https://ssr2.scrape.center/", verify=False)
print(response.status_code)

也可以指定一个本地证书用作客户端证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

import requests

response = requests.get("https://ssr2.scrape.center/", cert=("/path/server.crt", "/path/server.key"))
print(response.status_code)

我们需要有crtkey文件,并且指定它们的路径。另外注意,本地私有证书的 key 必须是解密状态,加密状态的 key 是不支持的。

超时设置

为了防止服务器不能及时响应,应该设置一个超时时间,如果超过这个时间还没有得到响应,就报错。这需要用到timeout 参数,其值是从发出请求到服务器返回响应的时间。

import requests

r = requests.get("https://www.httpbin.org/get", timeout=1)
print(r.status_code)

通过这样的方式,我们可以将超时时间设置为1秒,意味着如果1秒内没有响应,就抛出异常。

实际上,请求分为两个阶段:连接(connect)读取(read)。 上面设置的 timeout 是用作连接和读取的 timeout 的总和。

如果要分别指定用作连接和读取的 timeout,则可以传入一个元组:

r = requests.get("https://www.httpbin.org/get", timeout=(5, 30))

也可以设置timeout的值为None来不返回超时错误,或者直接省略该参数:

r = requests.get("https://www.httpbin.org/get", timeout=None)

身份验证

我们可以使用requests库自带的身份认证功能,通过auth参数即可设置,实现身份验证:

import requests
from requests.auth import HTTPBasicAuth

r = requests.get("https://ssr3.scrape.center/", auth=HTTPBasicAuth('admin', 'admin'))
print(r.status_code)

这个实例网站的用户名和密码都是admin,在这里我们可以直接设置。 如果用户名和密码正确,那么请求时就会自动认证成功,返回200状态码;如果认证失败,则返回401状态码。

requests 库提供了一个更简单的写法,可以直接传一个元组,它会默认使用HTTPBasicAuth 这个类来认证。

import requests
from requests.auth import HTTPBasicAuth

r = requests.get("https://ssr3.scrape.center/", auth=('admin', 'admin'))
print(r.status_code)

此外,requests 库还提供了其他认证方式,如OAuth 认证,不过此时需要安装 oauth 包,安装命令如下:

pip3 install requests_oauthlib

使用OAuth1认证:

import requests
from requests_oauthlib import OAuth1

url = "https://api.twitter.com/1.1/account/verify_credentials.json"
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
              'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)

代理设置

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

那么,为了防止这种情况发生,我们需要设置代理来解决这个问题,这时就需要用到 proxies 参数。

import requests

proxies = {
'http': 'http://10.10.10,10:1080',
'https': 'http://10.10.10.10:1080',
}
requests.get('https://ww.httpbin.org/get', proxies=proxies)

直接运行这个实例可能不行,因为这个代理可能是无效的,可以直接搜索寻找有效的代理并替换试验一下。

若代理需要使用上文所述的身份认证,可以使用类似 http:/user:password@host:port 这样的语法来设置代理。

import requests

proxies={'https': 'http://user:passworde10.10.10.10:1080/', }
requests.get('https://www.httpbin.org/get', proxies=proxies)

除了基本的 HTTP代理外,requests 库还支持 SOCKS 协议的代理。

首先,需要安装 socks 这个库:

pip3 install "requests[socks]"

Prepared Request

get 和 post发送请求在Requests内部的实现:

实际上,requests 在发送请求的时候,是在内部构造了一个 Request 对象,并给这个对象赋予了各种参数,包括 url、headers、data等,然后直接把这个Request 对象发送出去,请求成功后会再得到一个Response对象,解析这个对象即可。

Response对象实际上是Prepared Request类型的

不用get方法,直接构造一个 Prepared Request 对象:

from requests import Request, Session

url = 'https://www.httpbin.org/post'
data = {'name': 'germey'}
headers ={
    'User-Agent': 'Mozilla/5.0(Macintosh; Intel Mac 05 X 1011_4)ApplewebKit/537.36(KHTML, like Cecko)Chrome/53.0.2785.116 Safari/537.36'
}
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = s.prepare_request(req)
r = s.send(prepped)
print(r.text)

这里我们引入了Request类,然后用url、dataheaders参数构造了一个 Request 对象,这时需要再调用Session类的 prepare_request方法将其转换为一个 Prepared Request 对象,再调用 send方法发送,

输出结果:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "name": "germey"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "11", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "www.httpbin.org", 
    "User-Agent": "Mozilla/5.0(Macintosh; Intel Mac 05 X 1011_4)ApplewebKit/537.36(KHTML, like Cecko)Chrome/53.0.2785.116 Safari/537.36", 
    "X-Amzn-Trace-Id": "Root=1-661bb56b-08f36f231af036a008a5741d"
  }, 
  "json": null, 
  "origin": "222.85.167.205", 
  "url": "https://www.httpbin.org/post"
}

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

有了Request 这个对象,就可以将请求当作独立的对象来看待,这样在一些场景中我们可以直接操作这个 Request 对象,更灵活地实现请求的调度和各种操作。