本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目基于Python实现微博评论的爬取,涵盖网络请求、HTML/JSON解析、动态加载处理、登录状态维护、反爬策略应对及数据存储等核心技术。通过实战演练,帮助开发者掌握完整爬虫流程,适用于社交媒体数据采集与分析场景。项目代码清晰,适合Python爬虫进阶学习与实践。
微博评论Python代码实现

1. Python爬虫基础原理

在互联网数据爆炸式增长的今天,Python爬虫技术已成为数据采集与分析的重要工具。本章将从零开始,深入讲解爬虫的基本工作原理,帮助读者构建扎实的理论基础。

简单来说, 爬虫(Web Crawler) 是一种自动抓取网页内容的程序。它模拟浏览器行为,向服务器发送请求,获取响应数据,并从中提取所需信息。其核心流程包括:发送HTTP请求 → 获取HTML响应 → 解析页面内容 → 提取或存储数据。

为了更好地理解后续章节中涉及的 requests、BeautifulSoup 等工具,掌握爬虫的基本原理是不可或缺的第一步。接下来我们将逐步展开讲解。

2. requests库发送HTTP请求

2.1 HTTP协议基础

2.1.1 请求与响应结构

HTTP(HyperText Transfer Protocol)是用于客户端和服务器之间通信的应用层协议。它定义了客户端如何向服务器发送请求,以及服务器如何响应这些请求。一个完整的HTTP请求由三部分组成: 请求行(Request Line) 请求头(Headers) 请求体(Body)

  • 请求行 :包含请求方法(如GET、POST)、请求路径(URL路径)以及HTTP版本。
  • 请求头 :由多个键值对组成,用于传递客户端的附加信息,例如User-Agent、Content-Type等。
  • 请求体 :仅在POST或PUT请求中出现,包含实际传输的数据,比如表单数据或JSON内容。

一个典型的HTTP请求示例如下:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml

对应的HTTP响应结构则包括 状态行(Status Line) 响应头(Headers) 响应体(Body)

  • 状态行 :包含HTTP版本、状态码和状态描述。
  • 响应头 :描述服务器信息、内容类型、编码方式等。
  • 响应体 :即服务器返回的实际数据内容,如HTML页面、JSON数据等。

示例HTTP响应如下:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Server: Apache

<!DOCTYPE html>
<html>
<head><title>Example</title></head>
<body><h1>Hello, World!</h1></body>
</html>

理解HTTP请求与响应的结构是使用 requests 库进行网络爬虫开发的基础。只有清晰掌握这些结构,才能更好地构造请求、解析响应,并进行后续的数据处理。

2.1.2 常见状态码解析

HTTP状态码用于表示请求的处理结果,它们通常由三位数字组成。状态码分为五大类:

类别 范围 含义
1xx 100-199 信息性状态码,表示请求已接收,继续处理
2xx 200-299 成功状态码,表示请求已成功处理
3xx 300-399 重定向状态码,表示客户端需进一步操作
4xx 400-499 客户端错误状态码,如请求语法错误或资源不存在
5xx 500-599 服务器错误状态码,表示服务器处理请求时出错

常见状态码如下:

状态码 含义描述
200 请求成功
301 永久重定向,资源已被分配新的URI
302 临时重定向
400 错误请求,客户端发送的请求有语法错误
401 未授权,需要身份验证
403 禁止访问,服务器拒绝执行请求
404 未找到,请求的资源不存在
500 内部服务器错误
502 网关错误,服务器作为网关时从上游服务器收到无效响应
503 服务不可用,服务器暂时无法处理请求

在使用 requests 进行爬虫开发时,合理判断状态码是异常处理和请求重试策略的重要依据。例如,当遇到 404 状态码时,说明请求地址不存在;而 500 状态码则提示服务器端出现问题,可能需要等待或重试。

2.2 requests库核心功能

2.2.1 发送GET和POST请求

requests 是 Python 中最常用的 HTTP 客户端库,它简化了发送 HTTP 请求和处理响应的过程。其核心功能之一就是支持发送 GET 和 POST 请求。

GET 请求

GET 请求用于从服务器获取资源。请求参数通常附加在 URL 后面,形成查询字符串。

示例代码如下:

import requests

response = requests.get('https://httpbin.org/get', params={'key1': 'value1', 'key2': 'value2'})
print(response.url)  # 输出完整URL
print(response.text)  # 输出响应内容

逐行分析:

  • requests.get() :发送 GET 请求,第一个参数是目标 URL。
  • params :请求参数,以字典形式传递,会自动编码附加到 URL 中。
  • response.url :查看实际请求的完整 URL。
  • response.text :获取响应内容(字符串格式)。
POST 请求

POST 请求用于向服务器提交数据。请求参数通常放在请求体中。

示例代码如下:

response = requests.post('https://httpbin.org/post', data={'key1': 'value1', 'key2': 'value2'})
print(response.status_code)  # 查看响应状态码
print(response.json())  # 将响应内容解析为 JSON

逐行分析:

  • requests.post() :发送 POST 请求。
  • data :提交的数据,可以是字典、字符串或字节流。
  • response.status_code :查看响应状态码。
  • response.json() :将响应内容解析为 JSON 格式,适用于服务器返回 JSON 数据的情况。

2.2.2 请求头与参数设置

在某些场景中,服务器会根据请求头(Headers)判断请求来源,例如 User-Agent 用于标识客户端类型。因此,在使用 requests 发送请求时,常常需要自定义请求头。

自定义请求头
headers = {
    'User-Agent': 'MyApp/1.0',
    'Accept': 'application/json',
}

response = requests.get('https://httpbin.org/get', headers=headers)
print(response.request.headers)  # 打印实际发送的请求头

逐行分析:

  • headers :定义请求头信息,以字典形式传入。
  • response.request.headers :查看实际发送的请求头,验证是否设置成功。
请求参数设置

除了使用 params 设置查询参数,还可以使用 json 参数发送 JSON 格式的 POST 数据。

data = {'username': 'test', 'password': 'secret'}
response = requests.post('https://httpbin.org/post', json=data)
print(response.request.body)  # 查看请求体内容

逐行分析:

  • json=data :自动将字典转换为 JSON 格式,并设置 Content-Type: application/json
  • response.request.body :查看请求体内容,确认是否为 JSON 格式。
使用代理和超时设置
proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}

response = requests.get('https://httpbin.org/get', proxies=proxies, timeout=5)

逐行分析:

  • proxies :设置代理服务器地址。
  • timeout=5 :设置请求超时时间为 5 秒,避免长时间阻塞。

2.3 请求异常处理

2.3.1 超时与重试机制

在实际的网络爬虫开发中,网络状况不稳定可能导致请求超时。为此, requests 提供了超时控制和重试机制。

设置请求超时时间
try:
    response = requests.get('https://httpbin.org/delay/10', timeout=3)
except requests.exceptions.Timeout:
    print("请求超时,请重试")

逐行分析:

  • timeout=3 :设置连接和读取的总超时时间为 3 秒。
  • 如果服务器响应时间超过设定值,将抛出 Timeout 异常。
实现请求重试逻辑
import time

max_retries = 3
retry_count = 0

while retry_count < max_retries:
    try:
        response = requests.get('https://httpbin.org/get', timeout=5)
        if response.status_code == 200:
            print("请求成功")
            break
        else:
            print(f"请求失败,状态码:{response.status_code}")
            retry_count += 1
            time.sleep(2)  # 等待2秒后重试
    except requests.exceptions.RequestException as e:
        print(f"请求异常:{e}")
        retry_count += 1
        time.sleep(2)

逐行分析:

  • 使用 while 循环控制最大重试次数。
  • 捕获所有 RequestException 异常,包括连接错误、超时等。
  • 每次重试前使用 time.sleep(2) 暂停 2 秒,避免频繁请求。

2.3.2 异常捕获与日志记录

在生产环境中,捕获异常并记录日志是保障爬虫稳定运行的重要手段。 requests 提供了多种异常类型,开发者可以根据不同异常进行处理。

常见异常类型
异常类型 含义描述
ConnectionError 网络连接错误,如DNS解析失败等
Timeout 请求超时
HTTPError HTTP响应状态码非2xx
RequestException 所有 requests 异常的基类
示例代码:捕获异常并记录日志
import logging

logging.basicConfig(level=logging.ERROR)

try:
    response = requests.get('https://httpbin.org/status/404')
    response.raise_for_status()  # 如果状态码不是2xx,抛出HTTPError
except requests.exceptions.HTTPError as e:
    logging.error(f"HTTP错误:{e}")
except requests.exceptions.ConnectionError:
    logging.error("网络连接错误")
except requests.exceptions.Timeout:
    logging.error("请求超时")
except requests.exceptions.RequestException as e:
    logging.error(f"请求异常:{e}")

逐行分析:

  • response.raise_for_status() :手动抛出 HTTP 错误,便于集中处理。
  • 使用 logging 模块记录错误日志,便于后续排查问题。
  • 分类捕获不同异常类型,进行针对性处理。
异常处理流程图
graph TD
    A[发送请求] --> B{是否发生异常?}
    B -- 是 --> C[捕获异常]
    C --> D{异常类型}
    D -->|HTTPError| E[记录HTTP错误]
    D -->|ConnectionError| F[记录连接错误]
    D -->|Timeout| G[记录超时]
    D -->|其他| H[记录通用异常]
    B -- 否 --> I[处理响应]

通过合理的异常捕获和日志记录,可以提升爬虫的健壮性和可维护性,使其在面对各种网络状况时仍能稳定运行。

3. BeautifulSoup解析HTML数据

在现代网络爬虫开发中,获取到HTML内容只是第一步,真正决定数据质量的是如何高效准确地从HTML文档中提取出目标信息。而 BeautifulSoup 作为 Python 中最受欢迎的 HTML/XML 解析库之一,以其简洁的 API、强大的节点查找能力,成为许多爬虫开发者的首选工具。

本章将系统讲解 BeautifulSoup 的使用方法,从 HTML 文档的基本结构入手,逐步深入其核心功能与实际应用技巧,帮助你构建一套完整的 HTML 数据解析流程。

3.1 HTML基础结构与标签解析

HTML(HyperText Markup Language)是一种用于创建网页的标准标记语言,它通过一系列的标签(Tag)来组织文档结构。理解 HTML 的基本结构和标签嵌套关系,是使用 BeautifulSoup 进行数据解析的前提。

3.1.1 HTML文档结构分析

HTML 文档通常由以下几部分组成:

  • <!DOCTYPE html> :文档类型声明。
  • 标签 :整个文档的根节点。
  • 部分 :包含元信息,如页面标题、字符编码、样式表链接等。
  • 部分 :网页主体内容,包含各种可见的标签结构。

下面是一个简单的 HTML 页面结构示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>示例页面</title>
</head>
<body>
    <h1>欢迎来到示例页面</h1>
    <p class="description">这是一个用于演示的HTML页面。</p>
    <ul>
        <li>项目1</li>
        <li>项目2</li>
        <li>项目3</li>
    </ul>
</body>
</html>
HTML结构分析图(Mermaid流程图)
graph TD
    A[HTML文档] --> B(html标签)
    B --> C1[head部分]
    B --> C2[body部分]
    C1 --> D1[meta]
    C1 --> D2[title]
    C2 --> D3[h1]
    C2 --> D4[p]
    C2 --> D5[ul]
    D5 --> E1[li]
    D5 --> E2[li]
    D5 --> E3[li]

通过该结构图,可以清晰地看到 HTML 文档的层级关系。BeautifulSoup 正是通过这种树状结构来解析和查找节点。

3.1.2 标签嵌套与节点查找

HTML 文档的结构本质上是一个树状结构(DOM 树),每个标签节点都可能包含子节点或文本内容。例如:

<div class="container">
    <p>这是一个段落。</p>
    <span>这是一个内联元素</span>
</div>

在这个结构中:
- <div> 是父节点。
- <p> <span> 是其子节点。
- 每个节点都可以通过 BeautifulSoup 提供的 API 进行访问和操作。

示例代码:使用 BeautifulSoup 查找节点
from bs4 import BeautifulSoup

html = '''
<div class="container">
    <p>这是一个段落。</p>
    <span>这是一个内联元素</span>
</div>

soup = BeautifulSoup(html, 'html.parser')

# 查找父节点
container = soup.find('div', class_='container')

# 查找子节点
paragraph = container.find('p')
span = container.find('span')

print("段落内容:", paragraph.text)
print("内联内容:", span.text)
代码解析:
  1. 导入 BeautifulSoup :从 bs4 模块导入解析器。
  2. 解析 HTML 内容 :使用 html.parser 作为解析器,构造 BeautifulSoup 对象。
  3. 查找节点
    - find() 方法用于查找第一个匹配的节点。
    - 传入标签名和属性条件(如 class_='container' )可以精确定位。
  4. 提取文本内容 :通过 .text 属性获取节点内部的文本。
参数说明:
参数名 类型 说明
html str 待解析的 HTML 字符串
‘html.parser’ str 使用 Python 内置的解析器
class_ str 用于匹配 class 属性的值
find() function 查找第一个符合条件的节点

3.2 BeautifulSoup常用方法

BeautifulSoup 提供了多种方法来定位和提取 HTML 节点,包括 find() find_all() 和 CSS 选择器。掌握这些方法可以帮助我们更高效地处理复杂的 HTML 文档结构。

3.2.1 元素定位与提取

除了前面提到的 find() 方法,BeautifulSoup 还提供了 find_all() 来获取所有匹配的节点。

示例代码:查找所有 <li> 元素
html = '''
<ul>
    <li>项目1</li>
    <li>项目2</li>
    <li>项目3</li>
</ul>

soup = BeautifulSoup(html, 'html.parser')
items = soup.find_all('li')

for item in items:
    print(item.text)
代码解析:
  1. find_all() :查找所有 <li> 标签,返回一个列表。
  2. 遍历输出 :逐个输出每个 <li> 的文本内容。
参数说明:
参数名 类型 说明
name str 标签名称,如 ‘li’
attrs dict 标签属性,如 {‘class’: ‘list’}
recursive bool 是否递归查找,默认 True
limit int 返回的最大匹配数,默认 None

3.2.2 CSS选择器与find系列方法

CSS 选择器是一种强大的选择器语法,BeautifulSoup 支持使用 select() 方法通过 CSS 选择器来定位元素。

示例代码:使用 CSS 选择器提取数据
html = '''
<div class="content">
    <p class="text">段落1</p>
    <p class="highlight">段落2</p>
    <p class="text">段落3</p>
</div>

soup = BeautifulSoup(html, 'html.parser')

# 使用 CSS 选择器查找所有 class 为 'text' 的 <p> 标签
texts = soup.select('p.text')

for p in texts:
    print(p.text)
代码解析:
  1. select() :传入 CSS 选择器字符串 'p.text' ,表示选择所有 <p> 标签且 class 为 text 的元素。
  2. 输出结果 :只输出段落1和段落3。
CSS 选择器常用语法对照表:
选择器语法 含义说明
p 选择所有 <p> 标签
.class 选择 class 为指定值的元素
#id 选择 id 为指定值的元素
p.class 选择 <p> 标签中 class 为指定值的元素
div p 选择 <div> 内部的所有 <p> 标签
ul > li 选择 <ul> 的直接子元素 <li>

3.3 数据清洗与格式化

在实际爬虫项目中,提取到的数据往往包含多余空格、重复内容或特殊字符。BeautifulSoup 提取数据后,还需要进行必要的清洗和格式化处理。

3.3.1 文本去重与空格处理

示例代码:去除文本前后空格并去重
html = '''
<div>
    <p>   内容1   </p>
    <p>内容2</p>
    <p>内容1</p>
    <p>  内容3  </p>
</div>

soup = BeautifulSoup(html, 'html.parser')
paragraphs = soup.find_all('p')

# 去除前后空格,并去重
cleaned_texts = set(p.get_text(strip=True) for p in paragraphs)

print("清洗后的数据:", list(cleaned_texts))
代码解析:
  1. get_text(strip=True) :提取文本时去除前后空格。
  2. set() 去重 :利用集合的特性去除重复文本。
输出结果:
清洗后的数据: ['内容1', '内容2', '内容3']

3.3.2 特殊字符过滤与编码转换

网页中常常包含 HTML 实体(如 &amp; 表示 & ),BeautifulSoup 提取文本时会自动解码,但在某些场景下仍需手动处理。

示例代码:手动解码 HTML 实体
from bs4 import BeautifulSoup
import html

html_content = '''
<div>
    <p>版权 &copy; 2025</p>
    <p>小于号 &lt; 和大于号 &gt;</p>
</div>

soup = BeautifulSoup(html_content, 'html.parser')
paragraphs = soup.find_all('p')

for p in paragraphs:
    decoded_text = html.unescape(p.text)
    print(decoded_text)
代码解析:
  1. html.unescape() :将 HTML 实体转换为正常字符。
  2. 输出结果
    版权 © 2025 小于号 < 和大于号 >
参数说明:
函数名 说明
unescape() 将 HTML 实体字符转换为标准字符

通过本章的学习,你已经掌握了如何使用 BeautifulSoup 解析 HTML 文档、提取目标节点,并对提取的数据进行清洗与格式化处理。这些技能将为你在后续章节中处理更复杂的网页结构和数据提供坚实的基础。在下一章中,我们将探讨如何应对网页中的动态加载内容,使用 Selenium 或 Pyppeteer 来实现高级爬取功能。

4. 动态加载内容处理(Selenium/Pyppeteer)

在现代 Web 应用中,前端技术(如 JavaScript、Vue、React)的广泛应用使得网页内容不再全部由服务器直接返回,而是通过 AJAX(Asynchronous JavaScript and XML)异步加载或前端框架动态渲染完成。传统的基于 requests 和 BeautifulSoup 的静态页面抓取方式已经无法有效获取这类动态网页的内容。因此,掌握如何处理动态加载的网页内容,成为爬虫工程师进阶的重要技能之一。

本章将围绕 Selenium 和 Pyppeteer 两大主流工具展开,深入探讨它们在处理动态网页中的使用场景、技术原理以及具体实践方法。我们将从动态网页的加载机制入手,逐步过渡到 Selenium 和 Pyppeteer 的实际应用与优化技巧。

4.1 动态网页数据抓取挑战

4.1.1 AJAX 与前端渲染机制

AJAX 技术允许网页在不重新加载整个页面的情况下,与服务器进行异步通信并更新部分网页内容。例如,一个商品详情页在用户点击“查看评论”按钮后,会通过 AJAX 向服务器请求评论数据并插入到当前页面中。这种行为使得传统的静态解析方式(如 requests + BeautifulSoup)无法直接获取到完整的页面内容。

前端渲染(如使用 Vue、React 等框架)进一步增加了爬取难度。这类网页通常只在初始加载时返回一个 HTML 骨架,真正的内容由 JavaScript 在浏览器中动态生成。这意味着即使我们使用 requests 获取了 HTML 页面,其中也不包含我们想要的数据。

AJAX 请求结构示例
import requests

# 发送AJAX请求获取评论数据
url = "https://example.com/api/comments"
params = {
    "product_id": 12345,
    "page": 1
}
response = requests.get(url, params=params)
comments = response.json()
print(comments)

逐行解析:

  • 第 3 行:导入 requests 模块。
  • 第 6 行:定义 API 接口地址。
  • 第 7–9 行:定义请求参数。
  • 第 10 行:发送 GET 请求获取评论数据。
  • 第 11 行:将响应内容转换为 JSON 格式。
  • 第 12 行:打印返回的数据。

参数说明:

  • url :AJAX 接口地址。
  • params :请求参数,通常包括页码、商品 ID 等信息。
  • response.json() :将服务器返回的 JSON 字符串转换为 Python 字典对象。
动态网页与静态网页对比
特性 静态网页 动态网页(AJAX/前端渲染)
内容来源 服务器直接返回完整 HTML 内容由 JavaScript 动态加载
页面结构 页面即数据 页面为骨架,数据通过 API 获取
数据抓取难度 简单
工具适用性 requests + BeautifulSoup Selenium / Pyppeteer / Playwright
页面加载速度 初次加载快,内容加载依赖网络

4.1.2 真实请求分析与接口提取

面对动态网页,我们需要通过浏览器的开发者工具(F12)来分析其背后的网络请求,从而定位数据来源。

示例:使用 Chrome DevTools 查找 AJAX 请求
  1. 打开目标网页,点击需要动态加载内容的按钮。
  2. 打开 Chrome 开发者工具(F12),切换到 “Network” 标签。
  3. 勾选 “XHR” 或 “Fetch/XHR”,刷新页面并触发加载动作。
  4. 查找返回数据的请求(如 JSON 格式),右键选择 “Copy as cURL”。
  5. 使用 Python requests 模拟该请求获取数据。
import requests

headers = {
    'User-Agent': 'Mozilla/5.0',
    'Referer': 'https://example.com/product/12345'
}
params = {
    'product_id': 12345,
    'offset': 0,
    'limit': 10
}
response = requests.get('https://api.example.com/comments', headers=headers, params=params)
print(response.json())

逐行解析:

  • 第 3–6 行:定义请求头和请求参数。
  • 第 7 行:发送 GET 请求到 API 接口。
  • 第 8 行:输出返回的 JSON 数据。

参数说明:

  • headers :模拟浏览器请求,防止被反爬。
  • params :查询参数,用于指定获取的数据范围。
  • response.json() :将返回的 JSON 数据解析为 Python 对象。
mermaid 流程图:AJAX 请求抓取流程
graph TD
    A[打开目标网页] --> B[触发动态内容加载]
    B --> C[打开开发者工具]
    C --> D[筛选XHR请求]
    D --> E[复制请求URL和参数]
    E --> F[使用requests模拟请求]
    F --> G[提取JSON数据]

4.2 Selenium 自动化操作

Selenium 是一个用于自动化 Web 浏览器操作的强大工具,适用于模拟用户行为,处理 JavaScript 渲染的页面。

4.2.1 浏览器驱动配置与元素交互

Selenium 支持多种浏览器(如 Chrome、Firefox、Edge),需要配合对应的浏览器驱动(如 chromedriver)使用。

安装与配置步骤
  1. 安装 Selenium: pip install selenium
  2. 下载浏览器驱动(如 ChromeDriver
  3. 设置环境变量或在代码中指定驱动路径
from selenium import webdriver

# 指定chromedriver路径
driver = webdriver.Chrome(executable_path='/path/to/chromedriver')
driver.get('https://example.com')

逐行解析:

  • 第 1 行:导入 webdriver 模块。
  • 第 4 行:启动 Chrome 浏览器实例。
  • 第 5 行:访问目标网页。

参数说明:

  • executable_path :浏览器驱动的路径。
  • driver.get() :打开指定 URL。
元素定位与点击操作
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 等待按钮加载完成
wait = WebDriverWait(driver, 10)
button = wait.until(EC.element_to_be_clickable((By.ID, 'load-more-btn')))
button.click()

逐行解析:

  • 第 1–3 行:导入等待和条件判断模块。
  • 第 6 行:设置最大等待时间为 10 秒。
  • 第 7 行:等待 ID 为 load-more-btn 的按钮可点击。
  • 第 8 行:模拟点击按钮。

参数说明:

  • By.ID :按元素 ID 定位。
  • EC.element_to_be_clickable :等待元素可点击。
  • click() :模拟鼠标点击。

4.2.2 页面等待与事件触发

动态网页往往需要等待 JavaScript 执行完毕或某个元素加载完成,Selenium 提供了显式等待和隐式等待机制。

显式等待示例
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 显式等待元素出现
element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, 'comment-list'))
)
print(element.text)

逐行解析:

  • 第 6–7 行:等待 class 名为 comment-list 的元素出现。
  • 第 8 行:打印该元素的文本内容。
隐式等待示例
driver.implicitly_wait(10)  # 隐式等待10秒
element = driver.find_element(By.ID, 'dynamic-content')
print(element.text)

参数说明:

  • implicitly_wait :全局等待时间,适用于所有 find_element 操作。
事件触发与页面滚动
# 模拟页面滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

逐行解析:

  • 第 2 行:执行 JavaScript 脚本,滚动页面到底部。

4.3 Pyppeteer 无头浏览器应用

Pyppeteer 是一个基于 asyncio 的 Python 库,封装了 Chrome DevTools 协议(CDP),支持异步操作,适用于处理复杂的前端渲染页面。

4.3.1 异步编程与页面控制

Pyppeteer 采用异步非阻塞的方式进行页面控制,适用于高并发任务。

安装与基础使用
pip install pyppeteer
import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(headless=True)  # 无头模式
    page = await browser.newPage()
    await page.goto('https://example.com')
    await page.screenshot({'path': 'example.png'})  # 截图
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

逐行解析:

  • 第 5 行:启动浏览器实例(无头模式)。
  • 第 6 行:创建新页面。
  • 第 7 行:访问目标网址。
  • 第 8 行:截取当前页面并保存。
  • 第 9 行:关闭浏览器。

参数说明:

  • headless=True :启用无头模式(不显示浏览器窗口)。
  • screenshot() :截图函数,支持保存为 PNG 或 JPEG。
异步等待与元素查找
await page.waitForSelector('div.content')  # 等待元素加载
elements = await page.querySelectorAll('div.comment')
for el in elements:
    text = await page.evaluate('(el) => el.textContent', el)
    print(text)

逐行解析:

  • 第 1 行:等待 class 为 content 的元素加载。
  • 第 2 行:查找所有 class 为 comment 的元素。
  • 第 3–5 行:遍历每个元素,获取其文本内容。

参数说明:

  • waitForSelector :等待指定元素出现。
  • querySelectorAll :查找多个元素。
  • evaluate :在浏览器上下文中执行 JavaScript 表达式。

4.3.2 模拟点击与页面滚动

Pyppeteer 同样支持模拟用户交互行为,如点击、滚动等。

模拟点击操作
await page.click('button#load-more')
await page.waitForTimeout(2000)  # 等待2秒

参数说明:

  • click() :模拟点击指定选择器的元素。
  • waitForTimeout() :等待指定时间(毫秒)。
页面滚动到底部
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')

逐行解析:

  • 第 1 行:执行 JavaScript 滚动页面到底部。
表格:Selenium 与 Pyppeteer 对比
功能特性 Selenium Pyppeteer
支持浏览器 多浏览器(Chrome/Firefox) 主要支持 Chromium
是否异步
控制粒度 粗粒度(API 封装) 细粒度(CDP 协议)
并发能力 较低 高(支持 asyncio)
内存占用 相对较低
易用性

通过本章的学习,我们深入理解了动态网页加载的机制,并掌握了 Selenium 和 Pyppeteer 两大主流工具的使用方法。这些工具不仅帮助我们突破静态页面抓取的限制,还能模拟用户行为,处理复杂的前端交互逻辑,是现代爬虫开发中不可或缺的技术栈。在下一章中,我们将探讨如何通过 Session 和 Cookie 管理登录状态,实现更高级的认证爬取。

5. 登录状态管理(Session/Cookie)

在爬虫开发过程中,面对需要登录的网站时,如何模拟用户登录并维持登录状态成为核心问题。本章将深入探讨 Cookie 和 Session 的工作原理,解析常见的登录认证流程,并通过实战案例展示如何在 Python 中使用 requests 库进行登录模拟与状态维持。我们将以微博登录接口为例,详细分析接口请求参数、处理登录响应并验证登录状态,帮助读者掌握完整的登录状态管理方案。

5.1 Cookie与Session机制解析

在 Web 开发中,HTTP 协议是无状态的,这意味着每次请求都是独立的,服务器无法直接识别用户身份。为了解决这个问题,Cookie 和 Session 应运而生。

5.1.1 登录认证流程分析

典型的登录认证流程如下:

  1. 用户在客户端输入用户名和密码并提交登录表单。
  2. 服务器验证用户信息,若正确,则创建一个 Session 并将 Session ID 返回给客户端。
  3. 客户端将 Session ID 存储在 Cookie 中。
  4. 后续请求中,客户端自动携带该 Cookie 发送给服务器。
  5. 服务器通过 Cookie 中的 Session ID 识别用户身份,维持登录状态。

这一流程的核心在于 Cookie 的自动携带机制和服务器端 Session 的管理能力。

流程图:登录认证流程
graph TD
    A[用户输入账号密码] --> B[客户端提交登录请求]
    B --> C[服务器验证身份]
    C -->|验证成功| D[创建 Session & 返回 Session ID]
    D --> E[客户端存储 Session ID 到 Cookie]
    E --> F[后续请求自动携带 Cookie]
    F --> G[服务器通过 Cookie 识别用户]
    C -->|验证失败| H[返回错误信息]

5.1.2 Session对象的使用

在 Python 中, requests 库提供了 Session 对象,可以自动管理 Cookie,模拟浏览器会话。

示例代码:使用 requests.Session 模拟登录
import requests

# 创建 Session 对象
session = requests.Session()

# 登录请求
login_url = 'https://example.com/login'
login_data = {
    'username': 'test_user',
    'password': 'test_pass'
}

response = session.post(login_url, data=login_data)

# 检查是否登录成功
if response.status_code == 200:
    print('登录成功')
else:
    print('登录失败')

# 后续请求会自动携带 Cookie
profile_url = 'https://example.com/profile'
profile_response = session.get(profile_url)
print(profile_response.text)
代码逻辑分析:
  • 第 3 行 :创建 Session 实例,用于维持会话。
  • 第 6-9 行 :构造登录请求的 URL 和 POST 数据。
  • 第 11 行 :发送 POST 请求进行登录, Session 会自动保存服务器返回的 Cookie。
  • 第 13-17 行 :判断登录是否成功,并发送后续请求访问需要登录的页面。
  • 第 20 行 :使用 Session 对象发送 GET 请求,自动携带登录时获得的 Cookie,服务器即可识别用户身份。
参数说明:
  • login_data :包含用户名和密码的字典,需根据目标网站的实际参数名进行调整。
  • session.post() :发送 POST 请求,参数 data 用于传递表单数据。
  • session.get() :发送 GET 请求,自动携带之前保存的 Cookie。

5.2 登录模拟与身份维持

登录模拟的关键在于正确构造请求参数,并妥善处理 Cookie 和 Session。此外,验证码的处理也是模拟登录中常见的难点。

5.2.1 表单提交与验证码绕过

很多网站在登录时会加入验证码(如图形验证码、滑块验证码),以防止自动化脚本登录。对于这类问题,可以采用以下几种方式绕过:

  • 使用第三方识别服务 :如打码平台 API,识别图形验证码。
  • 模拟滑块轨迹 :使用 Selenium 或 Pyppeteer 控制浏览器模拟用户滑动。
  • 分析接口参数 :部分验证码接口返回 token,可直接调用接口绕过。
示例代码:使用 requests 模拟登录带验证码的网站(假设验证码已绕过)
import requests

session = requests.Session()

# 获取验证码 token
captcha_url = 'https://example.com/get_captcha'
captcha_response = session.get(captcha_url)
captcha_token = captcha_response.json()['token']

# 提交登录请求(含验证码 token)
login_url = 'https://example.com/login'
login_data = {
    'username': 'user123',
    'password': 'pass456',
    'captcha': captcha_token
}

response = session.post(login_url, data=login_data)

if response.status_code == 200:
    print('登录成功')
else:
    print('登录失败')
代码逻辑分析:
  • 第 6-8 行 :先请求验证码接口获取 token,假设该 token 可以绕过验证码。
  • 第 11-15 行 :构造登录请求,包含用户名、密码和验证码 token。
  • 第 17-21 行 :发送登录请求并判断是否成功。
参数说明:
  • captcha_token :从接口获取的验证码 token,用于绕过验证码验证。
  • login_data['captcha'] :将验证码 token 作为参数提交。

5.2.2 Cookie持久化与更新

在实际应用中,我们可能需要将 Cookie 持久化保存,以便下次使用时无需重新登录。Python 提供了 requests 配合 http.cookiejar 实现 Cookie 的保存与加载。

示例代码:Cookie 的保存与加载
import requests
import http.cookiejar as cookielib
import pickle

# 保存 Cookie 到文件
def save_cookie():
    session = requests.Session()
    session.cookies = cookielib.LWPCookieJar(filename='cookies.txt')

    login_url = 'https://example.com/login'
    login_data = {'username': 'user', 'password': 'pass'}

    session.post(login_url, data=login_data)
    session.cookies.save()

# 加载 Cookie 到 Session
def load_cookie():
    session = requests.Session()
    session.cookies = cookielib.LWPCookieJar(filename='cookies.txt')
    session.cookies.load()

    profile_url = 'https://example.com/profile'
    response = session.get(profile_url)
    print(response.text)

# 调用函数
save_cookie()
load_cookie()
代码逻辑分析:
  • 第 7 行 :创建 LWPCookieJar 实例,并指定保存路径。
  • 第 11 行 :登录后调用 save() 方法将 Cookie 保存到文件。
  • 第 16 行 :创建新的 Session 并加载之前保存的 Cookie。
  • 第 19 行 :使用已加载的 Cookie 发送请求,维持登录状态。
参数说明:
  • filename='cookies.txt' :指定 Cookie 保存的文件路径。
  • session.cookies.save() :将当前 Cookie 保存到指定文件。
  • session.cookies.load() :从文件加载 Cookie 到当前 Session。

5.3 微博登录接口分析实战

微博作为国内大型社交平台之一,其登录接口具有一定的反爬机制。本节将通过实战方式,分析微博登录接口的请求参数、处理登录响应并验证登录状态。

5.3.1 接口请求参数提取

微博登录接口通常为 https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19) ,请求方式为 POST。我们需要分析请求参数的构造方式。

示例代码:模拟微博登录请求
import requests
import time
import hashlib

# 构造加密密码
def encrypt_password(password):
    return hashlib.md5(password.encode()).hexdigest()

# 模拟登录
def login_weibo(username, password):
    login_url = 'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)'
    headers = {
        'User-Agent': 'Mozilla/5.0',
        'Referer': 'https://weibo.com/'
    }
    post_data = {
        'entry': 'weibo',
        'gateway': '1',
        'from': '',
        'savestate': '7',
        'qrcode_flag': 'false',
        'userticket': '1',
        'vsnf': '1',
        'service': 'miniblog',
        'servertime': str(int(time.time())),
        'nonce': 'ABC123',
        'pwencode': 'md5',
        'username': username,
        'password': encrypt_password(password),
        'rsakv': '1234567890',
        'prelt': '50',
        'url': 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack'
    }

    session = requests.Session()
    response = session.post(login_url, data=post_data, headers=headers)
    print(response.text)
    return session

# 调用登录函数
session = login_weibo('your_username', 'your_password')
代码逻辑分析:
  • 第 4 行 :定义密码加密函数,使用 MD5 加密。
  • 第 10 行 :构造请求头,模拟浏览器访问。
  • 第 14-29 行 :构造 POST 请求参数,其中 servertime nonce rsakv 等参数可能需要动态生成。
  • 第 31 行 :创建 Session 对象并发送 POST 请求。
  • 第 33 行 :输出响应内容,可用于判断是否登录成功。
参数说明:
  • servertime :服务器时间戳,通常需要从页面或接口中获取。
  • nonce :随机字符串,可能需要动态生成。
  • rsakv :RSA 加密的密钥参数,部分接口可能需要动态获取。
  • password :经过 MD5 加密的密码。

5.3.2 登录响应处理与状态验证

微博登录成功后,通常会返回包含用户信息的 JSON 数据。我们可以解析响应内容来验证登录状态。

示例代码:解析微博登录响应
import json

def check_login(session):
    # 访问用户主页
    profile_url = 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack'
    response = session.get(profile_url)
    # 提取 JSON 数据
    start_idx = response.text.find('(') + 1
    end_idx = response.text.rfind(')')
    json_data = response.text[start_idx:end_idx]
    user_info = json.loads(json_data)
    if 'userdomain' in user_info:
        print('登录成功,用户名:', user_info['userinfo']['username'])
    else:
        print('登录失败')

# 验证登录状态
check_login(session)
代码逻辑分析:
  • 第 6 行 :访问用户主页接口,获取响应内容。
  • 第 9-11 行 :提取 JSON 数据并解析。
  • 第 13-16 行 :判断是否包含用户信息字段,从而判断登录状态。
参数说明:
  • profile_url :微博用户主页接口地址。
  • json_data :提取的原始 JSON 字符串,需进行解析。
登录状态判断字段说明:
  • 'userdomain' :微博用户主页域名字段,存在则表示登录成功。
  • 'username' :用户昵称字段,可用于输出登录用户信息。

通过本章内容,我们详细解析了 Cookie 和 Session 的工作机制,演示了使用 requests.Session 进行登录模拟和状态维持的方法,并通过微博登录接口实战展示了登录请求参数的构造与响应处理。这些内容将为后续爬取需要登录的网站提供坚实基础。

6. 验证码识别与滑块验证处理

验证码(CAPTCHA)是互联网安全体系中常见的反爬机制,旨在通过图像、文字、滑块等形式,区分“人”与“机器”。随着技术的演进,验证码的复杂度不断上升,尤其是滑块验证码的出现,极大提高了自动识别的难度。本章将从验证码的类型、识别技术、滑块验证机制出发,深入探讨其处理方式与破解思路。

6.1 验证码类型与识别难点

验证码根据其呈现形式可分为 图形验证码、滑块验证码、文字验证码、点选验证码 等。不同类型的验证码具有不同的识别难度和应对策略。

6.1.1 图形验证码与OCR技术

图形验证码是最原始、最常见的一类验证码形式,通常由字母、数字或其组合构成,并叠加噪声、干扰线、颜色混淆等手段以提升识别难度。

OCR技术的原理

OCR(Optical Character Recognition)技术用于从图像中提取文本信息。常用的OCR工具包括:

  • Tesseract :开源OCR引擎,支持多语言识别。
  • 百度OCR API、腾讯云OCR API :云端服务,精度高但需调用接口。
示例:使用Tesseract识别验证码
from PIL import Image
import pytesseract

# 打开验证码图像
img = Image.open('captcha.png')

# 灰度化处理
img = img.convert('L')

# 二值化处理(阈值分割)
threshold = 128
img = img.point(lambda p: p > threshold and 255)

# 使用Tesseract识别
text = pytesseract.image_to_string(img)
print("识别结果:", text)

逐行解释:
- Image.open :加载验证码图片。
- convert('L') :将图像转为灰度图,减少颜色干扰。
- point(lambda p: ...) :进行二值化处理,增强对比度。
- image_to_string :调用Tesseract OCR进行识别。

识别难点分析
  • 图像噪声 :验证码中常加入干扰线、点、背景纹理,影响OCR识别效果。
  • 字体变形 :字母常被拉伸、旋转、倾斜,OCR难以准确匹配。
  • 多语言混合 :部分验证码混合中英文、符号,增加识别复杂度。

6.1.2 滑块验证码机制分析

滑块验证码(如极验、腾讯防水墙)是近年来较为流行的反爬机制,要求用户拖动滑块完成拼图验证。这类验证码不仅需要识别图像内容,还需模拟人类行为轨迹,技术门槛更高。

工作原理流程图(mermaid)
graph TD
A[用户点击滑块] --> B[加载背景图与滑块缺口图]
B --> C[生成验证轨迹]
C --> D[前端加密轨迹参数]
D --> E[发送验证请求]
E --> F{验证是否通过}
F -->|是| G[允许操作]
F -->|否| H[拒绝操作并刷新验证码]
核心挑战
  • 图像识别 :需识别滑块缺口位置。
  • 轨迹模拟 :需生成符合人类行为的拖动轨迹。
  • 参数加密 :滑块轨迹参数通常经过加密处理,需逆向分析。

6.2 验证码识别工具与API

验证码识别可通过调用第三方服务、自定义训练模型等方式实现。不同场景下选择不同的识别策略。

6.2.1 第三方识别服务调用

第三方识别平台提供高精度的验证码识别服务,适用于图形验证码、滑块验证码等多种类型。

示例:调用超级鹰OCR平台识别验证码
import requests

url = 'http://upload.chaojiying.net/Upload/Processing.php'
data = {
    'user': 'your_username',
    'pass2': 'your_password_md5',
    'softid': 'your_softid',
    'codetype': '1902'  # 滑块验证码类型
}

with open('slide_captcha.jpg', 'rb') as f:
    files = {'userfile': f}
    response = requests.post(url, data=data, files=files)
    result = response.json()
    print("识别结果:", result['pic_str'])

参数说明:
- user :平台用户名
- pass2 :MD5加密的密码
- softid :开发者ID
- codetype :验证码类型编码(1902表示滑块)
- pic_str :返回的滑块轨迹坐标

优势与局限
  • 优势 :识别准确率高,支持多种验证码类型。
  • 局限 :依赖外部服务,响应延迟,存在成本。

6.2.2 自定义训练与模型应用

对于企业级或高频识别需求,可训练自定义验证码识别模型。

技术流程说明
  1. 数据收集 :采集大量验证码图像。
  2. 数据标注 :对图像中的字符或滑块缺口进行标注。
  3. 模型训练 :使用CNN、Transformer等深度学习模型训练。
  4. 部署应用 :封装模型为API或本地服务。
示例:使用OpenCV识别滑块缺口位置
import cv2
import numpy as np

# 加载背景图与滑块图
bg_img = cv2.imread('background.jpg', 0)
slider_img = cv2.imread('slider.jpg', 0)

# 使用模板匹配算法寻找滑块位置
result = cv2.matchTemplate(bg_img, slider_img, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(result >= threshold)

# 输出匹配位置
for pt in zip(*loc[::-1]):
    print("滑块位置坐标:", pt)
    cv2.rectangle(bg_img, pt, (pt[0] + slider_img.shape[1], pt[1] + slider_img.shape[0]), (0, 0, 255), 2)

# 显示结果
cv2.imshow('Detected', bg_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

逻辑分析:
- cv2.imread(..., 0) :读取为灰度图,减少干扰。
- matchTemplate :模板匹配算法,寻找滑块在背景图中的位置。
- np.where :提取匹配结果的坐标。
- cv2.rectangle :绘制识别框。

优势与局限
  • 优势 :自主可控,适合高频使用。
  • 局限 :训练成本高,模型泛化能力有限。

6.3 滑块验证模拟与破解思路

滑块验证码的破解不仅涉及图像识别,还涉及 行为模拟 参数加密 ,是当前反爬与爬虫对抗的焦点。

6.3.1 轨迹模拟与行为特征分析

滑块验证码通常要求用户拖动滑块完成拼图。为模拟人类行为,需生成 具有加速度、停顿、抖动等特征的轨迹

示例:生成模拟拖动轨迹
import random
import time

def generate_track(distance):
    track = []
    current = 0
    mid = distance * 4 / 5
    t = 0.2
    v = 0

    while current < distance:
        if current < mid:
            a = random.uniform(2, 3)  # 加速度
        else:
            a = -random.uniform(3, 4)  # 减速
        v0 = v
        v = v0 + a * t
        move = v0 * t + 0.5 * a * t**2
        current += move
        track.append(round(move))
    return track

track = generate_track(120)
print("生成轨迹:", track)

逻辑说明:
- 初始加速度模拟用户加速拖动。
- 中段减速模拟手指松开前的惯性。
- track 数组保存每一步的位移值,用于后续模拟。

行为特征分析表
特征项 说明
加速度变化 人类拖动时有明显的加速与减速过程
抖动幅度 拖动过程中存在轻微抖动
停顿时间 用户可能在拖动过程中短暂停顿
总耗时 合理的拖动时间(通常1~3秒)

6.3.2 图像匹配与位置识别

滑块验证码的图像匹配是识别滑块缺口位置的关键步骤。

示例:使用OpenCV+模板匹配定位缺口
import cv2
import numpy as np

def find_gap(bg_img, slider_img):
    bg = cv2.imread(bg_img, 0)
    slider = cv2.imread(slider_img, 0)
    # 模板匹配
    result = cv2.matchTemplate(bg, slider, cv2.TM_CCOEFF_NORMED)
    loc = np.where(result >= 0.6)
    return loc[1][0]  # 返回x坐标

gap_x = find_gap('bg.jpg', 'slider.jpg')
print("缺口位置:", gap_x)

参数说明:
- cv2.TM_CCOEFF_NORMED :归一化相关系数匹配方法。
- loc[1][0] :获取匹配位置的X坐标。

滑块验证流程总结图(mermaid)
graph TD
A[加载验证码图像] --> B[图像预处理]
B --> C[识别滑块缺口位置]
C --> D[生成模拟拖动轨迹]
D --> E[模拟用户行为完成滑块验证]
E --> F[发送验证请求]
F --> G{验证是否通过}
G -->|是| H[成功登录]
G -->|否| I[重新识别与模拟]

本章从验证码的类型、识别技术到滑块验证的模拟与破解,层层递进地介绍了爬虫对抗验证码的完整技术链路。在实际项目中,验证码处理往往是爬虫开发中最复杂的环节之一,需要结合图像识别、行为模拟、参数逆向等多方面技术进行综合处理。

7. JSON数据解析与提取

7.1 JSON数据结构基础

7.1.1 键值对与嵌套结构

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛应用于网络请求的响应中。其基本结构由键值对(key-value pairs)组成,支持嵌套结构,适合表达复杂的数据关系。

{
    "name": "Alice",
    "age": 25,
    "address": {
        "city": "Beijing",
        "zipcode": "100000"
    },
    "hobbies": ["reading", "traveling", "coding"]
}
  • 键值对 :如 "name": "Alice" ,表示一个键为 name ,值为 "Alice" 的键值对。
  • 嵌套结构 :如 "address" 是一个对象,内部还包含 "city" "zipcode"
  • 数组类型 :如 "hobbies" 是一个数组,包含多个字符串。

7.1.2 Python中json模块使用

Python 提供了内置的 json 模块用于解析和生成 JSON 数据。

示例:解析 JSON 字符串
import json

data_str = '''
{
    "name": "Alice",
    "age": 25,
    "address": {
        "city": "Beijing",
        "zipcode": "100000"
    },
    "hobbies": ["reading", "traveling", "coding"]
}

# 将 JSON 字符串转换为 Python 字典
data_dict = json.loads(data_str)

# 访问字段
print(data_dict["name"])               # Alice
print(data_dict["address"]["city"])    # Beijing
print(data_dict["hobbies"][0])         # reading
示例:生成 JSON 字符串
# 构造 Python 字典
data = {
    "name": "Bob",
    "age": 30,
    "skills": ["Python", "Java", "C++"]
}

# 转换为 JSON 字符串
json_output = json.dumps(data, indent=2)
print(json_output)

输出结果:

{
  "name": "Bob",
  "age": 30,
  "skills": [
    "Python",
    "Java",
    "C++"
  ]
}

参数说明
- json.loads() :将 JSON 字符串转为 Python 对象(通常是字典或列表)。
- json.dumps() :将 Python 对象转为 JSON 字符串。
- indent=2 :用于美化输出,设置缩进空格数。

7.2 微博评论接口响应分析

7.2.1 接口返回格式与字段含义

微博评论接口通常返回 JSON 格式的数据,包含评论内容、用户信息、点赞数等字段。

示例响应片段:
{
    "data": {
        "comments": [
            {
                "id": "123456789",
                "text": "这个内容很有意思!",
                "user": {
                    "id": "987654321",
                    "screen_name": "用户A",
                    "profile_image_url": "http://example.com/avatar1.jpg"
                },
                "like_counts": 42,
                "created_at": "2024-04-01 15:30:00"
            },
            {
                "id": "987654321",
                "text": "学习到了很多东西,谢谢分享!",
                "user": {
                    "id": "654321987",
                    "screen_name": "用户B",
                    "profile_image_url": "http://example.com/avatar2.jpg"
                },
                "like_counts": 27,
                "created_at": "2024-04-01 15:45:00"
            }
        ],
        "total_number": 150
    },
    "ok": 1
}
  • "comments" :评论列表,是一个数组。
  • "text" :评论正文内容。
  • "user" :用户信息,包含昵称、头像、ID。
  • "like_counts" :点赞数量。
  • "created_at" :评论时间。
  • "total_number" :总评论数。

7.2.2 分页机制与数据提取策略

微博接口通常使用分页参数(如 page max_id )进行数据分页。

示例:提取第一页评论数据
import requests
import json

# 请求微博评论接口
url = "https://weibo.com/ajax/statuses/build_comments"
params = {
    "id": "1234567890",  # 微博文 ID
    "page": 1
}

response = requests.get(url, params=params)
data = response.json()

# 提取评论数据
for comment in data['data']['comments']:
    print(f"用户:{comment['user']['screen_name']}")
    print(f"评论:{comment['text']}")
    print(f"点赞数:{comment['like_counts']}")
    print('-' * 40)
分页策略示例(使用 max_id ):
page = 1
max_id = None

while True:
    params = {"id": "1234567890", "page": page}
    if max_id:
        params["max_id"] = max_id

    response = requests.get(url, params=params)
    data = response.json()

    if not data['data']['comments']:
        break

    for comment in data['data']['comments']:
        # 提取并处理评论
        pass

    max_id = data['data'].get('max_id')
    page += 1

7.3 数据结构转换与持久化

7.3.1 字段映射与清洗处理

在提取 JSON 数据后,通常需要将其映射到目标数据结构(如数据库字段或 DataFrame)并进行清洗。

示例:将评论数据映射为结构化字典
def process_comment(comment):
    return {
        'comment_id': comment['id'],
        'user_id': comment['user']['id'],
        'username': comment['user']['screen_name'],
        'content': comment['text'],
        'likes': comment['like_counts'],
        'created_at': comment['created_at']
    }

cleaned_comments = [process_comment(c) for c in data['data']['comments']]
清洗内容示例:去除 HTML 标签和特殊字符
import re

def clean_text(text):
    # 去除 HTML 标签
    text = re.sub(r'<[^>]+>', '', text)
    # 去除换行和多余空格
    text = re.sub(r'\s+', ' ', text).strip()
    return text

# 应用清洗函数
for comment in cleaned_comments:
    comment['content'] = clean_text(comment['content'])

7.3.2 多级嵌套结构解析技巧

当 JSON 数据存在多级嵌套时,可以通过递归函数或字典遍历方式提取深层字段。

示例:递归提取所有 text 字段
def extract_values(obj, key):
    """递归查找所有指定 key 的值"""
    arr = []

    def extract(obj, arr, key):
        if isinstance(obj, dict):
            for k, v in obj.items():
                if k == key:
                    arr.append(v)
                extract(v, arr, key)
        elif isinstance(obj, list):
            for item in obj:
                extract(item, arr, key)
        return arr

    results = extract(obj, arr, key)
    return results

texts = extract_values(data, 'text')
print(texts)

输出结果:

['这个内容很有意思!', '学习到了很多东西,谢谢分享!']

技巧总结
- 使用 isinstance() 判断对象类型。
- 对字典和列表分别处理。
- 利用递归深入嵌套结构。

(本章完)

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目基于Python实现微博评论的爬取,涵盖网络请求、HTML/JSON解析、动态加载处理、登录状态维护、反爬策略应对及数据存储等核心技术。通过实战演练,帮助开发者掌握完整爬虫流程,适用于社交媒体数据采集与分析场景。项目代码清晰,适合Python爬虫进阶学习与实践。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐