当前位置: 首页 > news >正文

Python网络爬虫(一) - 爬取静态网页

文章目录

  • 一、静态网页概述
    • 1. 静态网页介绍
    • 2. 静态网页爬取技术Requests介绍
  • 二、安装 Requests 库
  • 三、发送请求并获取响应
    • 1. 发送 GET 请求
      • 1.1 get() 方法介绍
      • 1.2 get() 方法签名介绍
      • 1.3 get() 方法参数介绍
      • 1.4 示例:发送get请求
    • 2. 发送 POST 请求
      • 2.1 post() 方法介绍
      • 2.2 post() 方法签名介绍
      • 2.3 post() 方法参数介绍
      • 2.4 请求与响应测试服务网站介绍
      • 2.5 示例:发送 POST 请求
  • 四、处理响应
    • 1. 响应类 Response 介绍
    • 2. Response 类的属性介绍
    • 3. 示例:处理响应
  • 五、处理复杂 GET 请求
    • 1. 定制请求头
      • 1.1 User-Agent 介绍
      • 1.2 Cookie 介绍
      • 1.3 示例:访问登录后的网易云阅读
    • 2. 保持会话
      • 2.1 Session 介绍
      • 2.2 示例:使用 Session 保持会话
        • 2.2.1 基本用法示例
        • 2.2.2 进阶:设置全局请求头
    • 3. SSL证书验证
      • 3.1 SSL证书介绍
      • 3.2 关闭SSL证书验证
        • 3.2.1 全局关闭
        • 3.2.2 在Session中关闭
    • 4. 代理服务器
      • 4.1 代理服务器介绍
      • 4.2 代理服务器的作用
      • 4.3 设置代理服务器
        • 4.3.1 常见的代理网站介绍
        • 4.3.2 检测免费代理IP的有效性
        • 4.3.3 设置代理服务器
  • 六、实战:爬取豆瓣读书中小说的网页


一、静态网页概述

1. 静态网页介绍

静态网页是指内容固定不变的网页,其页面代码(HTML、CSS、JavaScript等)预先编写完成并存储在服务器上,当用户访问时,服务器直接将现成的网页文件发送到用户浏览器进行展示。页面内容不会因用户操作或其他因素动态改变,所有访问者看到的内容完全一致。常见的静态网页文件后缀有.html、.htm等,适用于内容更新频率低的场景。

2. 静态网页爬取技术Requests介绍

Requests是Python中一款简洁高效的HTTP库,专门用于发送HTTP请求,是爬取静态网页的常用工具。使用时,通过导入requests库,调用get()、post()等方法即可向目标网页服务器发送请求,获取网页的HTML源代码。其操作流程简单:先使用requests.get(url)向指定URL发送GET请求,再通过response.text获取响应的网页文本内容,后续可结合解析库(如BeautifulSoup)提取所需数据。相比Python内置的urllib库,Requests语法更简洁,支持自动处理Cookie、设置请求头、处理编码等功能,大幅降低了静态网页爬取的实现难度。


二、安装 Requests 库

在开始使用 Python 发送网络请求前,需要先安装 Requests 库。Requests 是 Python 生态中最受欢迎的 HTTP 客户端库之一,它简化了网络请求的流程,支持 GET、POST 等多种请求方法,并能轻松处理响应数据、Cookie、 headers 等细节,极大降低了网络编程的门槛。

为了确保安装过程稳定高效,同时避免版本兼容性问题,推荐指定具体版本(本教程使用 2.31.0 版本),并通过国内镜像源加速下载。以下是具体的安装命令:

pip install requests==2.31.0 -i https://mirrors.aliyun.com/pypi/simple/

三、发送请求并获取响应

1. 发送 GET 请求

GET 请求是 HTTP 协议中最常用的请求方法之一,主要用于从服务器获取资源(如网页内容、API 数据等)。Requests 库通过 requests.get() 方法实现 GET 请求的发送,使用简单且功能强大。

1.1 get() 方法介绍

requests.get()Requests 库中用于发送 HTTP GET 请求的核心方法。它的作用是向指定的 URL 发送 GET 请求,并返回服务器响应的结果(封装在 Response 对象中)。

1.2 get() 方法签名介绍

方法签名(Signature)描述了方法的定义形式,包括方法名、参数列表和返回值。requests.get() 方法的完整签名如下:

def get(url, params=None, *args, **kwargs):return request('get', url, params=params,** kwargs)

从签名可以看出:

  • 该方法本质上是对 requests.request() 方法的封装,指定了请求方法为 'get'
  • url 是必填参数,其余参数为可选;
  • 返回值是一个 Response 对象,包含服务器响应的所有信息(状态码、响应体、headers 等)。

1.3 get() 方法参数介绍

requests.get() 方法的参数可分为 URL 相关参数和请求配置参数,以下是常用参数的详细说明(按使用频率排序):

参数名类型是否必填说明示例
urlstr目标资源的 URL 地址,即需要发送请求的服务器端点。url="https://api.example.com/data"
paramsdict/list/bytes用于构建 URL 的查询参数(Query String),会自动转换为 URL 中的键值对并进行 URL 编码。params={"id": 1, "name": "test"}(等价于 URL 后拼接 ?id=1&name=test
headersdict请求头信息,用于模拟浏览器环境、传递认证信息等,如 User-AgentAuthorization 等。headers={"User-Agent": "Mozilla/5.0"}
timeoutfloat/tuple请求超时时间(单位:秒)。单值表示总超时时间;元组表示(连接超时,读取超时)。timeout=3timeout=(2, 5)
cookiesdict/CookieJar携带的 Cookie 信息,用于维持会话状态(如登录状态)。cookies={"session_id": "abc123"}
verifybool/str是否验证 SSL 证书。默认 True(验证);设为 False 可跳过验证(不建议生产环境使用)。verify=False(跳过 SSL 验证)
allow_redirectsbool是否允许自动处理重定向(如 301、302 状态码)。默认 True(允许)。allow_redirects=False(禁止自动重定向)
authtuple/AuthBase用于 HTTP 认证的信息,如 Basic Auth、Digest Auth 等。auth=("username", "password")(Basic 认证)
proxiesdict代理服务器配置,用于通过代理发送请求。proxies={"http": "http://127.0.0.1:8080"}

1.4 示例:发送get请求

# 导入 requests 库,用于发送 HTTP 请求
import requests# 定义目标网站的基础 URL(豆瓣图书中“小说”标签的页面)
base_url = 'https://book.douban.com/tag/小说'# 设置请求头(headers),模拟浏览器访问,避免被服务器识别为爬虫而拒绝请求
# User-Agent 表示客户端信息,这里模拟的是 Chrome 浏览器在 Windows 10 系统上的访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
}# 定义 URL 的查询参数(query parameters)
# 这些参数会自动拼接到 URL 后面,形成完整的请求地址
params = {'start': 20, 'type': 'T'}# 发送 GET 请求到目标 URL
# 请求会自动将 base_url 与 params 拼接成完整地址:https://book.douban.com/tag/小说?start=20&type=T
# 同时携带 headers 中的请求头信息
response = requests.get(base_url, headers=headers, params=params)# 输出响应的状态码
# 200 表示请求成功,404 表示页面未找到,403 表示禁止访问等
print(response.status_code)# 输出最终请求的完整 URL(包含查询参数)
# 用于确认实际请求的地址是否正确
print(response.url)

打印结果如下图所示:

在这里插入图片描述

2. 发送 POST 请求

POST 请求是 HTTP 协议中另一种常用的请求方法,主要用于向服务器提交数据(如表单提交、API 数据提交等),数据通常包含在请求体中,而非 URL 中,适用于传输敏感信息或大量数据的场景。Requests 库通过 requests.post() 方法实现 POST 请求的发送,功能灵活且易于扩展。

2.1 post() 方法介绍

requests.post()Requests 库中用于发送 HTTP POST 请求的核心方法。它的作用是向指定的 URL 发送包含数据的 POST 请求,并返回服务器响应的结果(封装在 Response 对象中)。

与 GET 请求相比,POST 请求的核心特点是数据放在请求体中传输,因此更适合以下场景:

  • 提交用户表单数据(如登录、注册);
  • 向服务器发送 JSON 格式的 API 数据;
  • 上传文件或二进制数据;
  • 传输需要隐藏的敏感信息(如密码、Token)。

2.2 post() 方法签名介绍

requests.post() 方法的签名与 get() 类似,本质上也是对 requests.request() 方法的封装,只是固定了请求方法为 'post'。其完整签名如下:

def post(url, data=None, json=None, *args, **kwargs):return request('post', url, data=data, json=json,** kwargs)

从签名可以看出:

  • url 是必填参数,用于指定请求的目标 URL;
  • datajson 是 POST 请求特有的参数,用于传递请求体数据;
  • 其他参数(如 headerstimeout 等)与 get() 方法一致,通过 **kwargs 传入;
  • 返回值为 Response 对象,包含服务器响应的状态码、响应体、headers 等信息。

2.3 post() 方法参数介绍

requests.post() 方法的参数在继承 get() 方法通用参数(如 urlheaderstimeout 等)的基础上,增加了两个核心参数用于传递请求体数据。以下是常用参数的详细说明(重点标注 POST 特有参数):

参数名类型是否必填说明示例
urlstr目标资源的 URL 地址,即接收 POST 数据的服务器端点。url="https://api.example.com/login"
datadict/str/bytesPOST 请求体数据,适用于表单提交(application/x-www-form-urlencoded 格式)或二进制数据。data={"username": "test", "age": 20}data="key=value&key2=value2"
jsondict/listPOST 请求体数据,自动序列化为 JSON 格式(application/json 格式),适用于 API 接口调用。json={"user": {"name": "test", "role": "admin"}}
headersdict请求头信息,可指定数据格式(如 Content-Type: application/json)、认证信息等。headers={"Content-Type": "application/json", "Token": "xxx"}
timeoutfloat/tuple请求超时时间(单位:秒),同 get() 方法。timeout=5timeout=(3, 7)
cookiesdict/CookieJar携带的 Cookie 信息,用于维持会话状态,同 get() 方法。cookies={"session": "abc123"}
filesdict文件上传参数,键为字段名,值为文件对象或路径(multipart/form-data 格式)。files={"avatar": open("test.jpg", "rb")}
verifybool/str是否验证 SSL 证书,同 get() 方法。verify=True(默认验证)
allow_redirectsbool是否允许自动重定向,同 get() 方法,默认 Trueallow_redirects=False
authtuple/AuthBaseHTTP 认证信息,同 get() 方法。auth=("admin", "123456")

注意datajson 参数通常二选一:

  • 若提交表单数据或键值对,用 data
  • 若提交 JSON 格式数据,用 jsonRequests 会自动设置 Content-Type: application/json 头)。

2.4 请求与响应测试服务网站介绍

https://httpbin.org/是一个开源的HTTP请求与响应测试服务网站,基于Python的Flask框架开发,能为开发者提供多种实用功能,在开发和测试工作中作用显著。

  • 核心功能
    • HTTP方法测试:支持GET、POST、PUT、PATCH、DELETE等所有HTTP动词,可返回请求的完整信息,像参数、头部、URL等,方便开发者测试不同类型请求。
    • 状态码测试:能生成指定状态码的响应,例如200 OK、404 Not Found、500 Internal Server Error等,有助于测试客户端的错误处理机制。
    • 请求与响应检查:允许开发者查看请求的详细信息,包括头部、查询参数、请求体内容等;也能检查响应数据,如缓存信息、响应头。
    • 多种响应格式:可返回JSON、HTML、图片等不同格式的响应,方便测试客户端对不同内容类型的处理能力。
    • 动态数据生成:能生成随机和动态数据,例如随机的JSON响应或指定尺寸的图片,便于测试客户端在各种边界情况下的表现。
    • Cookie操作:支持创建、读取和删除Cookie,方便测试涉及Cookie的业务逻辑。
    • 重定向测试:可以返回不同的重定向响应,用于测试客户端对重定向的处理。
  • 应用场景
    • 接口测试:快速验证请求和响应的结构是否正确,检查接口是否按预期工作。
    • 网络诊断:帮助检查代理、IP、头部等网络层问题,例如确定代理是否正常工作、请求的IP是否正确。
    • 开发调试:在开发HTTP客户端或涉及网络请求的应用时,借助该网站模拟各种请求和响应情况,辅助调试代码。
  • 优势与不足:该网站轻量且功能全面,支持复杂场景测试;但由于服务器在国外,可能存在访问速度慢的问题。

2.5 示例:发送 POST 请求

模拟网页表单提交(如登录、注册),数据格式为 application/x-www-form-urlencoded

# 导入 requests 库,用于发送 HTTP 请求
import requests# 定义目标 URL,这里使用的是 httpbin.org 的 POST 测试接口
# 该网站用于测试 HTTP 请求,/post 接口会接收并返回你发送的数据
url = "https://httpbin.org/post"# 定义要发送的表单数据(字典格式)
# 通常用于模拟用户登录、提交表单等场景
data = {"username": "username","password": "password"
}# 使用 POST 方法向指定 URL 发送请求
# 参数说明:
#   - url: 目标地址
#   - data: 要发送的表单数据(会以 application/x-www-form-urlencoded 格式编码)
#   - timeout=5: 设置请求超时时间为 5 秒,防止程序无限等待
# 返回一个 Response 对象,包含服务器响应信息
response = requests.post(url=url, data=data, timeout=5)# 打印服务器返回的 HTTP 状态码
# 常见状态码:
#   - 200: 请求成功
#   - 400: 请求错误(如参数不合法)
#   - 404: 页面未找到
#   - 500: 服务器内部错误
# 这里预期输出为 200,表示请求成功被接收和处理
print("状态码:", response.status_code)

打印结果如下所示:

状态码: 200

四、处理响应

当使用 requests.get()requests.post() 等方法发送请求后,Requests 库会返回一个 Response 对象。该对象封装了服务器响应的所有信息,开发者可以通过它获取状态码、响应体、请求头等关键数据,从而完成对请求结果的处理。

1. 响应类 Response 介绍

ResponseRequests 库中用于表示服务器响应的核心类,它包含了一次 HTTP 请求的全部响应信息。无论是成功的请求(如 200 状态码)还是失败的请求(如 404 状态码),都会返回该类的实例。

Response 类的主要作用是:

  • 提供对响应状态的判断(如是否成功、是否重定向);
  • 解析并返回响应体数据(如文本、JSON、二进制内容);
  • 暴露请求和响应的元数据(如请求 URL、响应头、Cookie 等);
  • 提供便捷的方法处理响应内容(如自动解码、JSON 解析)。

2. Response 类的属性介绍

Response 类提供了丰富的属性,用于获取响应的各类信息。以下是常用属性的详细说明(按使用频率排序):

属性名类型说明示例(基于成功请求)
status_codeint服务器返回的 HTTP 状态码,用于判断请求是否成功(200 表示成功,4xx 表示客户端错误,5xx 表示服务器错误)。print(response.status_code)200
textstr响应体的文本内容,自动根据响应头的 Content-Type 猜测编码并解码(如 UTF-8)。print(response.text) → 输出网页 HTML 或 JSON 字符串
json()方法(返回 dict/list将响应体的 JSON 字符串解析为 Python 字典或列表(仅适用于 Content-Type: application/json 的响应)。data = response.json()print(data["args"]) 访问 JSON 中的字段
contentbytes响应体的二进制原始数据,适用于处理图片、文件等非文本内容。with open("image.jpg", "wb") as f: f.write(response.content) 保存图片
headersCaseInsensitiveDict服务器返回的响应头,是一个不区分大小写的字典(如 headers["Content-Type"]headers["content-type"] 等价)。print(response.headers["Content-Type"])application/json
urlstr实际请求的 URL(可能因重定向与原始 URL 不同)。print(response.url)https://httpbin.org/get(若发生重定向则显示最终 URL)
encodingstr响应内容的编码方式(如 utf-8),可手动修改以纠正解码错误。response.encoding = "utf-8" 强制设置编码
cookiesRequestsCookieJar服务器返回的 Cookie 信息,可像字典一样访问。print(response.cookies["session_id"]) → 获取名为 session_id 的 Cookie 值
elapsedtimedelta请求发送到接收响应的耗时(时间差),用于分析请求性能。print(response.elapsed.total_seconds())0.23(表示耗时 0.23 秒)
historylist包含请求重定向历史的 Response 对象列表(按重定向顺序排列),无重定向时为空。print([h.status_code for h in response.history])[301](表示一次 301 重定向)
okbool快捷判断请求是否成功(status_code 在 200~400 之间时为 True,否则为 False)。if response.ok: print("请求成功")
reasonstr状态码对应的原因短语(如 200 对应 “OK”,404 对应 “Not Found”)。print(response.reason)OK

3. 示例:处理响应

# 导入 requests 库,用于发送 HTTP 请求
import requests# 定义目标网站的基础 URL(豆瓣图书中“小说”标签的页面)
base_url = 'https://book.douban.com/tag/小说'# 设置请求头(headers),模拟浏览器访问,避免被服务器识别为爬虫而拒绝请求
# User-Agent 表示客户端信息,这里模拟的是 Chrome 浏览器在 Windows 10 系统上的访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
}# 定义 URL 的查询参数(query parameters)
# 这些参数会自动拼接到 URL 后面,形成完整的请求地址
params = {'start': 20, 'type': 'T'}# 发送 GET 请求到目标 URL
# 请求会自动将 base_url 与 params 拼接成完整地址:https://book.douban.com/tag/小说?start=20&type=T
# 同时携带 headers 中的请求头信息
response = requests.get(base_url, headers=headers, params=params)# 获取服务器返回的 HTTP 状态码(整数类型)
# 常见状态码:200(成功)、404(未找到)、403(禁止访问)、500(服务器错误)等
status_code = response.status_code
print('status_code:', status_code)  # 输出如:status_code: 200# 判断请求是否成功(布尔值)
# 当 status_code < 400 时返回 True(即 200-399 范围内都算“成功”)
ok = response.ok
print('ok:', ok)  # 成功时输出 True,失败时输出 False# 获取状态码对应的原因短语(Reason Phrase)
# 例如 200 -> 'OK',404 -> 'Not Found',500 -> 'Internal Server Error'
reason = response.reason
print('reason:', reason)  # 输出如:OK# 获取响应头信息,返回一个字典(dict)
# 包含服务器类型、内容类型、内容长度、编码方式、Set-Cookie、缓存策略等元数据
headers = response.headers
print('headers:', headers)  # 输出如:{'Content-Type': 'application/json', 'Server': 'nginx', ...}# 获取实际请求的完整 URL
# 如果发生了重定向,url 返回的是最终到达的页面地址(而非原始请求地址)
url = response.url
print('url:', url)  # 输出如:https://httpbin.org/get?name=test# 获取或设置当前使用的字符编码
# requests 会自动猜测编码,但有时不准确(如中文网页为 GBK),可手动设置 response.encoding = 'gbk'
encoding = response.encoding
print('encoding:', encoding)  # 输出如:utf-8# 获取服务器返回的 Cookies
# 返回一个 RequestsCookieJar 对象,可用于会话保持(如登录后访问其他页面)
cookies = response.cookies
print('cookies:', cookies)  # 输出如:<RequestsCookieJar[<Cookie sessionid=abc123 for .example.com/>]># 获取响应内容的字符串形式(自动解码后的文本)
# requests 会根据响应头中的 charset 自动推断编码(如 UTF-8),也可手动设置 encoding
text = response.text
print('text:', text)  # 通常用于查看网页 HTML 或 API 返回的文本内容# 获取响应内容的原始字节数据(bytes 类型)
# 适用于处理非文本数据,如图片、PDF、音频、视频等二进制文件
content = response.content
print('content:', content)  # 输出为字节串,例如:b'{"key": "value"}'

运行部分结果如下图所示:

在这里插入图片描述


五、处理复杂 GET 请求

在实际开发中,简单的 GET 请求往往无法满足需求。例如,部分网站会验证请求的合法性(如检查浏览器标识、登录状态等),此时需要通过定制请求头、携带 Cookie 等方式模拟真实的浏览器行为。以下介绍如何处理这类复杂的 GET 请求。

1. 定制请求头

请求头(Headers)是 HTTP 请求中包含的元数据,用于向服务器传递附加信息(如浏览器类型、支持的数据格式、认证信息等)。通过定制请求头,开发者可以模拟不同的客户端环境,绕过部分服务器限制(如反爬虫机制)。

1.1 User-Agent 介绍

User-Agent 是请求头中最常用的字段之一,用于标识发送请求的客户端(如浏览器、爬虫程序等)。服务器通过该字段判断请求来源,部分网站会拒绝非浏览器的请求(如直接使用 Requests 库默认的 User-Agent)。

特点与作用:

  • 格式:通常包含客户端类型、版本、操作系统等信息,例如浏览器的 User-Agent 格式为:
    Mozilla/5.0 (操作系统) 浏览器引擎/版本 浏览器/版本
  • 作用:服务器可根据 User-Agent 返回适配客户端的内容(如移动端和 PC 端页面),或限制非浏览器请求(反爬虫)。
  • 默认问题Requests 库的默认 User-Agentpython-requests/版本号,容易被服务器识别为爬虫并拦截。

1.2 Cookie 介绍

Cookie 是服务器存储在客户端的小型数据片段,主要用于维持会话状态(如登录状态、用户偏好等)。当客户端再次向同一服务器发送请求时,会自动携带对应的 Cookie,使服务器识别用户身份。

特点与作用:

  • 数据格式:由键值对组成(如 session_id=abc123; user_id=100),通常包含过期时间、域名等属性。
  • 作用:实现跨请求的状态保持,例如:
    • 登录后服务器返回 session_id Cookie,后续请求携带该 Cookie 即可保持登录状态;
    • 记录用户的浏览历史、购物车等信息。
  • 获取方式:Cookie 通常由服务器通过响应头的 Set-Cookie 字段返回,客户端(如浏览器)自动保存。

1.3 示例:访问登录后的网易云阅读

目的:使用代码访问登录后的网易云阅读的余额查询页面并保存。

登录后的网易云阅读个人信息页面如下图所示,可以看到网页标题是余额查询–网易云阅读,网址是https://yuedu.163.com/recharge.do?operation=balance。

在这里插入图片描述

接下退出登录,然后直接访问https://yuedu.163.com/recharge.do?operation=balance,会跳转到登录页面,需要先登录才能访问,此时的标题是登录–网易云阅读,如下图所示。

在这里插入图片描述

查看登录后的Cookie,如下图所示。

在这里插入图片描述

代码实现如下:

# 导入 pathlib 中的 Path 类,用于跨平台安全地处理文件和目录路径
from pathlib import Path# 导入 requests 库,用于发送 HTTP 请求
import requests# 定义目标 URL:网易阅读(yuedu.163.com)的余额查询接口
# 该接口通常用于获取用户的账户余额信息
base_url = 'https://yuedu.163.com/recharge.do?operation=balance'# 设置请求头(headers),模拟浏览器行为,避免被服务器识别为爬虫
headers = {# User-Agent:标识客户端浏览器和操作系统信息'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',# Cookie:携带登录状态信息# 注意:这里的 '登录后的Cookie' 是占位符,实际使用时需替换为真实登录后的 Cookie 字符串# 否则服务器会认为你未登录,返回登录页或错误信息'Cookie': '登录后的Cookie'
}# 向目标 URL 发送 GET 请求
# 请求携带指定的 headers(包含 User-Agent 和 Cookie)
# 由于 URL 中已包含参数 ?operation=balance,无需额外传入 params
response = requests.get(base_url, headers=headers)# 获取服务器返回的 HTTP 状态码,用于判断请求是否成功
status_code = response.status_code# 判断状态码是否为 200(表示请求成功)
if status_code == 200:# 如果请求成功,获取响应的文本内容(HTML 或 JSON 等)text = response.text# 定义本地保存文件的目录路径dir_path = Path('./html')# 定义保存的文件名file_name = 'wagnyi.html'# 创建目录(如果不存在)# parents=True:支持创建多级目录(如 ./html/a/b)# exist_ok=True:如果目录已存在,不会报错dir_path.mkdir(parents=True, exist_ok=True)# 打开文件并写入响应内容# 使用 with 语句确保文件在写入后自动关闭,避免资源泄漏# 'w' 模式:写入文本# encoding='utf-8':指定编码为 UTF-8,支持中文等多语言字符with open(dir_path / file_name, 'w', encoding='utf-8') as fp:fp.write(str(text))  # 将响应内容(text)转换为字符串并写入文件# 提示:网页内容已成功保存print(f"✅ 成功将响应内容保存到: {dir_path / file_name}")
else:# 如果请求失败,输出错误状态码print(f"❌ 请求失败,状态码: {status_code}")

2. 保持会话

在实际网络交互中,许多操作需要多步请求协同完成,且后续请求依赖于前期请求的状态(例如:登录后才能访问个人中心,购物车操作依赖登录状态等)。此时,单纯通过手动传递 Cookie 维持状态会非常繁琐,而 Requests 库提供的 Session 类可以完美解决这一问题。

2.1 Session 介绍

SessionRequests 库中用于创建持久会话的类,它能够在多个请求之间保持状态信息(如 Cookie、请求头),模拟浏览器的“会话”机制。简单来说,Session 就像一个“持续打开的浏览器”,在其生命周期内发送的所有请求会自动共享 Cookie 和部分配置,无需手动传递。

核心特点:

  1. 状态持久性

    • 会话内的所有请求会自动携带服务器返回的 Cookie(如登录后的 session_id),无需手动设置 cookies 参数。
    • 例如:用 Session 发送登录请求后,后续请求会自动携带登录状态的 Cookie,直接访问需要登录的资源。
  2. 请求头共享

    • 可以为 Session 设置全局请求头(如 User-Agent),会话内的所有请求会自动继承这些头信息,减少重复代码。
  3. 连接复用

    • 会话会复用与服务器的 TCP 连接,减少重复建立连接的开销,提升多请求场景的效率。

与普通请求的区别:

场景普通请求(requests.get()Session 请求(session.get()
Cookie 处理需手动提取和传递 Cookie自动保存和携带 Cookie
请求头复用每次请求需单独设置 headers可设置全局 headers,所有请求自动继承
状态维持无状态,每次请求独立有状态,多个请求共享会话信息

2.2 示例:使用 Session 保持会话

2.2.1 基本用法示例
import requests# 创建一个 Session 对象
# Session 类似于一个浏览器会话,可以跨请求保持某些参数(如 Cookies、HTTP 认证等)
# 在这里,Session 会自动管理登录后返回的 Cookie,后续请求会自动带上这些 Cookie
session = requests.Session()# 设置测试用的Cookie
session.get('http://httpbin.org/cookies/set/test_cookie/cookie_value')# 第一步:发送登录请求,模拟用户登录操作
# 登录接口地址(此处使用 httpbin.org 的 /post 接口用于测试和演示)
login_url = "https://httpbin.org/post"# 登录所需的表单数据(用户名和密码)
login_data = {"username": "username","password": "password"
}# 使用 session 发送 POST 请求到登录接口
# 由于使用了 Session,服务器返回的 Cookie 会被自动保存和管理
login_response = session.post(login_url, data=login_data)# 打印登录请求的响应状态码,用于判断请求是否成功(如 200 表示成功)
print("登录响应状态码:", login_response.status_code)# 第二步:访问需要登录后才能访问的页面
# 此处使用 httpbin.org 的 /get 接口来验证请求头信息
profile_url = "https://httpbin.org/get"# 使用同一个 session 发起 GET 请求
# Session 会自动携带之前登录时获取的 Cookie,实现“保持登录状态”的效果
profile_response = session.get(profile_url)# 查看本次请求的请求头中是否包含 Cookie
# 这是为了验证 Session 是否自动将登录时获得的 Cookie 添加到了请求头中
print("请求携带的 Cookie:", profile_response.request.headers.get("Cookie"))# 第三步:关闭 Session(可选操作)
# 关闭 Session 可以释放底层连接资源,特别是在长时间运行的程序中建议显式关闭
# Python 的垃圾回收机制通常也会自动处理,但显式关闭是良好的编程习惯
session.close()

打印结果如下图所示:

在这里插入图片描述

2.2.2 进阶:设置全局请求头

使用Cookie登录网易云阅读后,保存个人信息页面和余额查询页面;通过 Sessionheaders 属性设置全局请求头,会话内的所有请求会自动携带这些头信息。

# 导入 requests 库,用于发送 HTTP 请求
import requests# 创建一个持久会话对象(Session)
# Session 的作用是:在多个请求之间自动保持状态(如 Cookies、Headers、认证信息等)
# 类似于浏览器打开一个标签页,所有请求共享同一个上下文
session = requests.Session()# 设置全局请求头(headers)
# 使用 update() 方法为该 Session 设置默认请求头
# 后续通过此 session 发出的所有请求都会自动携带这些 headers
session.headers.update({# User-Agent:标识客户端浏览器和操作系统,防止被服务器识别为爬虫而拒绝"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/114.0.0.0 Safari/537.36",# Cookie:携带用户的登录凭证# 注意:'登录后的Cookie' 是占位符,实际使用时需替换为真实登录后获取的 Cookie 字符串# 否则服务器会认为你未登录,返回登录页或权限错误"Cookie": '登录后的Cookie'
})# 发送第一个 GET 请求:访问“充值页面”,查询账户余额
# 由于使用了 session,请求会自动携带上面设置的 User-Agent 和 Cookie
# URL 中的 operation=balance 表示执行“查询余额”操作
response1 = session.get("https://yuedu.163.com/recharge.do?operation=balance")# 发送第二个 GET 请求:访问“我的信息”页面
# 同样自动携带 headers 和登录状态(Cookie)
# 可用于获取用户个人信息、阅读记录等
response2 = session.get("https://yuedu.163.com/myinfor")# 将第一个请求的响应内容(HTML)保存到本地文件
# './html/recharge.html' 是保存路径
# 'w' 模式表示写入文本
# encoding='utf-8' 确保中文等字符正确保存,避免乱码
with open('./html/recharge.html', 'w', encoding='utf-8') as fp:fp.write(str(response1.text))  # 将响应文本写入文件# 将第二个请求的响应内容(HTML)保存到本地文件
with open('./html/myinfor.html', 'w', encoding='utf-8') as fp:fp.write(str(response2.text))  # 将响应文本写入文件# 提示:两个页面内容已成功保存
print("页面数据已成功保存:")
print("   - recharge.html")
print("   - myinfor.html")

3. SSL证书验证

3.1 SSL证书介绍

SSL(Secure Sockets Layer,安全套接层)证书是一种数字证书,用于在客户端(如浏览器)和服务器之间建立加密连接,确保数据传输的安全性和完整性。

在Python爬虫中,requests库默认会验证SSL证书的有效性。如果目标网站的SSL证书无效(如自签名证书、过期证书等),会抛出SSLError异常。

3.2 关闭SSL证书验证

在某些特殊场景(如测试环境、内部系统)中,可能需要临时关闭SSL证书验证。使用requests库时,可以通过设置get()方法中的参数verify=False来关闭SSL证书验证。

3.2.1 全局关闭
import requests# 关闭全局SSL验证警告(可选,仅消除警告信息)
# requests.packages.urllib3.disable_warnings()# 发送请求时关闭SSL验证
response = requests.get(url="http://httpbin.org/get", verify=False)print(response.status_code)
3.2.2 在Session中关闭
import requests# 创建会话对象
session = requests.Session()# 关闭会话级别的SSL验证
session.verify = False# 发送请求(自动应用会话设置)
response = session.get("http://httpbin.org/get")# 打印响应状态码
print(response.status_code)

4. 代理服务器

4.1 代理服务器介绍

代理服务器(Proxy Server)是网络信息的中转站,是介于浏览器和Web服务器之间的一台服务器,其功能是代理网络用户去取得网络信息。

代理服务器作为连接Internet与Intranet的桥梁,工作主要在开放系统互联(OSI)模型的对话层。浏览器向代理服务器发出请求,由代理服务器来取回浏览器所需要的信息并传送给浏览器。并且,大部分代理服务器都具有缓冲功能,可将新取得数据包存到本机存储器上,若浏览器请求的数据已存在且为最新,就直接从存储器传输数据给浏览器,能提高浏览速度和效率。

此外,代理服务器还可实现网络的安全过滤、流量控制、用户管理等功能,能解决许多单位连接Internet引起的IP地址不足问题,也可作为一种网络防火墙技术。

4.2 代理服务器的作用

  • 突破访问限制:在一些国家或地区,政府或互联网服务提供商可能会限制或封锁某些网站或服务,使用代理可以绕过这些限制,使用户能够访问被封锁的网站或服务。例如,教育网用户可通过代理访问一些外部资源。
  • 提高访问速度:代理服务器通常在全球范围内分布有多个节点,用户可以选择就近的代理节点来提高访问速度。同时,其缓存机制也可以让用户在访问相同内容时,直接从代理服务器获取数据,减少数据传输的时间和延迟。
  • 隐藏真实IP地址:代理可以帮助用户隐藏其真实的IP地址,让用户的上网行为更加难以追踪,从而减少个人信息泄露的风险,增强隐私保护。
  • 增强网络安全:代理服务器可以充当防火墙的角色,阻止恶意内容的访问,过滤掉一些潜在的安全威胁,保护用户数据和隐私。此外,通过代理服务器,内部网络对外部呈现为一个IP地址,外界不能直接访问到内部网,提高了内部网络的安全性。
  • 内容过滤与管理:对于企业或学校等组织来说,代理服务器可以用于过滤不需要的内容,例如广告,或者阻止访问某些特定网站,有效地控制内部网络的内容访问。
  • 节省IP地址开销:对于局域网内的多个用户,只需代理服务器上有一个合法的IP地址,其他用户可以使用私有IP地址,这样可以节约大量的IP资源,降低网络的维护成本。

4.3 设置代理服务器

4.3.1 常见的代理网站介绍
  • 高可用全球免费代理IP库:网址为http://ip.jiangxianli.com,提供了大量的免费代理IP资源,可获取不同地区、类型的代理IP,适合对代理IP需求不太苛刻的临时性使用。
  • 西拉代理:网址是http://www.xiladaili.com/,有免费的代理IP可供使用,同时也提供一些付费代理服务,其免费代理IP会定期更新,有一定的可用性。
  • 快代理:网址为https://www.kuaidaili.com,提供免费和付费代理服务。免费代理IP部分更新速度较快,可在网站上直接查看可用的代理IP列表,也能按地区等条件进行筛选,但免费代理的稳定性可能稍差。
  • ProxyList:提供了大量的免费和付费代理IP,分类清晰,包括HTTP、HTTPS、SOCKS5等多种类型。还提供了IP筛选功能,可以按国家、城市、运营商等条件筛选代理IP,方便用户根据需求查找。
  • FreeProxyList:该网站提供的是免费的代理IP,数量和质量都相对较高,支持HTTP、HTTPS、SOCKS4、SOCKS5等多种协议。使用时,只需在搜索框中输入目标网站地址,就可以看到可用的代理IP列表。
4.3.2 检测免费代理IP的有效性

此处以快代理中的免费IP为示例,来检测免费代理IP的有效性。打开快代理,选用的免费代理IP如下图所示。

在这里插入图片描述

检测代理IP有效性的代码如下所示:

import requests# 定义一个代理 IP 地址列表,每个代理以字典形式存储
# 格式为 {"http": "http://IP:端口"},用于通过 HTTP 代理发送请求
# 这些代理可用于隐藏真实 IP、绕过访问限制或测试网络连通性
proxy_list = [{"http": "http://8.217.124.178:49440"},{"http": "http://8.219.97.248:80"},{"http": "http://139.196.172.172:7799"},{"http": "http://139.159.97.42:8788"},{"http": "http://125.77.135.240:80"}
]# 目标网站的基础 URL
# 此处为一个前端学习网站的首页(示例用途),用于测试代理是否能正常访问
base_url = 'http://erabbit.itheima.net/#/'# 设置请求头(Headers),模拟浏览器行为,避免被服务器识别为爬虫而拒绝访问
# User-Agent 字段表示客户端浏览器信息
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
}# 遍历代理列表的副本(使用 copy() 是为了避免在遍历时修改原列表导致异常)
for proxy_ip in proxy_list.copy():try:# 发送 GET 请求,使用当前代理 IP 和自定义请求头# timeout=3 表示最多等待 3 秒,超时则抛出异常response = requests.get(base_url, headers=header, proxies=proxy_ip, timeout=3)# 如果请求成功(未抛出异常),说明代理可用print(f'IP地址:{proxy_ip.get("http")}有效')except Exception as e:  # 捕获所有网络相关异常(如连接超时、拒绝连接等)# 如果请求失败(如超时、无法连接等),说明该代理不可用print(f'IP地址:{proxy_ip.get("http")}无效')# 将无效的代理从原始列表中移除,确保后续不再使用proxy_list.remove(proxy_ip)

运行结果如下图所示,可以看到在这几个IP地址中,只有一个是有效的。

在这里插入图片描述

4.3.3 设置代理服务器

以下代码演示了如何在 Python 爬虫中使用代理服务器发送请求,通过随机选择代理 IP 的方式隐藏真实网络地址,适用于绕过访问限制、避免 IP 被封禁等场景。

import randomimport requests# 定义一个代理 IP 地址列表,每个代理以字典形式存储
# 格式为 {"http": "http://IP:端口"},用于通过 HTTP 代理发送请求
# 这些代理可用于隐藏真实 IP、绕过访问限制或测试网络连通性
proxy_list = [{"http": "http://114.231.73.67:1080", "https": "https://114.231.73.67:1080"},{"http": "http://125.77.135.240:80", "https": "https://125.77.135.240:80"},{"http": "http://171.105.22.54:9909", "https": "https://171.105.22.54:9909"},
]# 随机选择一个代理 IP
proxies = random.choice(proxy_list)# 定义目标网站的基础 URL(豆瓣图书中“小说”标签的页面)
base_url = 'https://book.douban.com/tag/小说'# 设置请求头(headers),模拟浏览器访问,避免被服务器识别为爬虫而拒绝请求
# User-Agent 表示客户端信息,这里模拟的是 Chrome 浏览器在 Windows 10 系统上的访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
}# 定义 URL 的查询参数(query parameters)
# 这些参数会自动拼接到 URL 后面,形成完整的请求地址
params = {'start': 20, 'type': 'T'}# 发送 GET 请求到目标 URL
# 请求会自动将 base_url 与 params 拼接成完整地址:https://book.douban.com/tag/小说?start=20&type=T
# 同时携带 headers 中的请求头信息
response = requests.get(base_url, headers=headers, params=params, proxies=proxies)# 输出响应的状态码
# 200 表示请求成功,404 表示页面未找到,403 表示禁止访问等
print(response.status_code)# 输出最终请求的完整 URL(包含查询参数)
# 用于确认实际请求的地址是否正确
print(response.url)

六、实战:爬取豆瓣读书中小说的网页

代码如下所示:

import random
import time
from pathlib import Pathimport requests# 基础配置
BASE_URL = 'https://book.douban.com/tag/小说'  # 目标页面URL
SAVE_DIR = Path('./豆瓣读书/小说')  # 保存目录
MAX_PAGES = 50  # 最大爬取页数
PAGE_SIZE = 20  # 每页条目数# 创建保存目录(如果不存在)
SAVE_DIR.mkdir(parents=True, exist_ok=True)# 请求头,模拟浏览器访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
}# 循环爬取页面
for page in range(MAX_PAGES + 1):start = page * PAGE_SIZEfile_name = f'{start}.html'file_path = SAVE_DIR / file_name# 如果文件已存在,跳过当前页if file_path.exists():print(f"文件 {file_name} 已存在,跳过")continue# 构建请求参数params = {"start": start, "type": "T"}# 发送GET请求,设置10秒超时response = requests.get(url=BASE_URL, headers=headers, params=params, timeout=10)# 检查请求是否成功status_code = response.status_codeif status_code == 200:# 保存页面内容with open(file_path, 'w', encoding='utf-8') as fp:fp.write(response.text)print(f"成功保存: {file_path}")else:print(f"请求失败,状态码: {status_code}")continue# 随机延迟,避免请求过于频繁delay = random.uniform(0.1, 0.5)print(f"等待 {delay:.2f} 秒后继续...")time.sleep(delay)print("爬取任务完成")

部分打印结果如下图所示:

在这里插入图片描述

保存后的部分html文件如下图所示:

在这里插入图片描述

http://www.xdnf.cn/news/1281007.html

相关文章:

  • 爬虫与数据分析结和
  • 小白玩转 DINO-X MCP(1):如何接入 MCP Server
  • 赚钱有什么规律,怎么泛化?
  • 多人游戏中的帧同步策略
  • macOS 搭建 Gitea 私有 Git 服务器教程
  • 【linux】企业级WEB应用服务器tomcat
  • 教程 | Win11彻底关闭“推荐的项目“,解放开始菜单! (Windows11推荐项目设置器)
  • RabbitMQ 声明队列和交换机详解
  • 基于FPGA的热电偶测温数据采集系统,替代NI的产品(三)测试
  • 基于领域事件驱动的微服务架构设计与实践
  • 面试实战 问题二十三 如何判断索引是否生效,什么样的sql会导致索引失效
  • C++ 限制类对象数量的技巧与实践
  • CS钓鱼鱼饵制作的方式
  • RFID系统:物联网时代的数字化管理中枢
  • 网络性能优化:Go编程视角 - 从理论到实践的性能提升之路
  • PyTorch基础(使用Tensor及Antograd实现机器学习)
  • Unity大型场景性能优化全攻略:PC与安卓端深度实践 - 场景管理、渲染优化、资源调度 C#
  • 请求报文和响应报文(详细讲解)
  • Android16新特性速记
  • 查看 php 可用版本
  • Spring Boot文件上传功能实现详解
  • DNS(域名系统)
  • cesium/resium 修改子模型材质
  • 第5节 大模型分布式推理通信优化与硬件协同
  • typecho博客设置浏览器标签页图标icon
  • 标准io(1)
  • MySQL中GROUP_CONCAT函数的使用详解
  • 机器翻译:一文掌握序列到序列(Seq2Seq)模型(包括手写Seq2Seq模型)
  • ssh 远程连接加密算法报错
  • MyBatis执行器与ORM特性深度解析