# 小红书爬虫工具 - 功能说明文档

## 版本信息
- 更新日期: 2026-02-02
- 文件: `crawler_ultimate.py`

---

## 一、核心功能概览

### 1.1 爬取模式
| 模式 | 说明 | 适用场景 |
|------|------|----------|
| 标准模式 | 点击笔记进入详情页提取完整数据 | 需要完整内容、评论、互动数据 |
| 极速模式 | 仅从列表页提取基础信息 | 快速批量采集封面图 |

### 1.2 爬取类型
- **关键词搜索**: 搜索指定关键词的笔记
- **主页推荐**: 爬取首页推荐内容
- **博主主页**: 爬取指定博主的全部笔记

---

## 二、数据提取逻辑

### 2.1 数据获取策略（重要！）

所有数据提取都使用 **URL中的noteId** 确保获取当前笔记数据，避免缓存污染：

```javascript
// 从URL获取当前笔记ID（最可靠）
const urlMatch = window.location.href.match(/explore\/([a-zA-Z0-9]+)/);
const noteId = urlMatch ? urlMatch[1] : state.note.currentNoteId;

// 使用noteId从__INITIAL_STATE__获取对应数据
const noteData = state.note.noteDetailMap[noteId];
```

### 2.2 提取的数据字段

| 字段 | 说明 | 提取方式 |
|------|------|----------|
| title | 标题 | JS + CSS选择器（弹窗内） |
| author | 作者 | JS + CSS选择器（弹窗内） |
| content | 正文内容 | CSS选择器 |
| tags | 标签 | CSS选择器 |
| publish_time | 发布时间 | CSS选择器 + 正则解析 |
| ip_region | IP地区 | 从时间字符串解析 |
| like_count | 点赞数 | JS(__INITIAL_STATE__) + DOM |
| collect_count | 收藏数 | JS(__INITIAL_STATE__) + DOM |
| comment_count | 评论数 | JS(__INITIAL_STATE__) + DOM |
| image_urls | 图片链接列表 | JS + CSS选择器 + 轮播切换 |
| video_url | 视频链接 | JS(__INITIAL_STATE__) |
| comments | 评论列表 | 滚动加载 + DOM解析 |

### 2.3 标题提取

```python
# 优先级：
# 1. JavaScript从弹窗DOM获取
# 2. 从__INITIAL_STATE__获取（使用URL中的noteId）
# 3. CSS选择器备用（限定在弹窗内）
# 4. 视频笔记用内容第一行作标题

title_selectors = [
    'css:.note-detail-mask .title',
    'css:[class*="noteContainer"] .title',
    'css:.note-content .title',
]
```

### 2.4 互动数据提取

```javascript
// 从__INITIAL_STATE__获取（使用URL中的noteId确保准确）
const noteData = state.note.noteDetailMap[noteId].note;
{
    likes: noteData.interactInfo.likedCount,
    collects: noteData.interactInfo.collectedCount,
    comments: noteData.interactInfo.commentCount
}

// 备用：从弹窗内的DOM获取
const modal = document.querySelector('.note-detail-mask');
const bar = modal.querySelector('.buttons.engage-bar-style');
bar.querySelector('.like-wrapper .count')  // 点赞
bar.querySelector('.collect-wrapper .count')  // 收藏
bar.querySelector('.chat-wrapper .count')  // 评论
```

---

## 三、图片处理逻辑

### 3.1 图片获取流程

```
1. 等待图片加载 (0.5s)
2. JS获取当前弹窗内的图片URL
3. 如果开启"获取全部图片"，切换轮播获取更多
4. 过滤表情包图片
5. 过滤Live图（只保留一张）
6. 批量下载
```

### 3.2 Live图过滤

Live图（动态图片）会被智能过滤，只保留一张静态版本：

```python
def _filter_live_images(image_urls):
    # 1. 提取URL基础部分（去掉参数和live标记）
    # 2. 按基础URL分组
    # 3. 每组只保留一张
    # 优先级：不含live的静态图 > .jpg/.png > 其他
```

**特征识别**：
- URL中包含 `live` 关键字
- 同一基础URL的不同格式变体
- `_live` 后缀标记

### 3.3 表情包过滤

```python
def _is_emoji_image(url):
    # 检测条件：
    # 1. URL关键词：emoji, sticker, emote等
    # 2. 小尺寸图片参数：/w/120, imageView2/2/w/200
    # 3. 表情包路径特征
```

### 3.4 图片存储结构

```
images/
└── 关键词_20260202_150000/
    ├── note_1_1770016000/
    │   ├── img_1.jpg          # 笔记图片
    │   ├── img_2.jpg
    │   ├── video.mp4          # 视频（如有）
    │   └── comments/          # 评论图片文件夹
    │       ├── comment_img_1.jpg
    │       └── comment_img_2.jpg
    ├── note_2_1770016000/
    │   └── ...
    └── 搜索结果.xlsx           # 导出的数据表
```

---

## 四、视频处理逻辑

### 4.1 视频检测

```python
# 多次尝试检测视频元素（延迟加载）
for _ in range(3):
    v = page.ele('xpath://video', timeout=0.3)
    if v:
        break
    time.sleep(0.2)
```

### 4.2 视频URL获取

```javascript
// 优先级：
// 1. __INITIAL_STATE__ (使用URL中的noteId)
// 2. 页面script标签正则匹配
// 3. video元素src属性

// 方法1：从状态获取
const video = noteData.note.video;
if (video.consumer?.originVideoKey) {
    return 'https://sns-video-bd.xhscdn.com/' + video.consumer.originVideoKey;
}

// 方法2：正则匹配
/"originVideoKey"\s*:\s*"([^"]+)"/
/"masterUrl"\s*:\s*"(https?:[^"]+)"/
```

---

## 五、评论处理逻辑

### 5.1 评论提取字段

| 字段 | 说明 |
|------|------|
| author | 评论者昵称 |
| content | 评论内容 |
| time | 评论时间 |
| ip | IP地区 |
| likes | 点赞数 |
| has_image | 是否有图片 |
| images | 评论图片URL列表 |

### 5.2 评论图片单独存储

评论中的图片会保存到单独的 `comments` 文件夹：

```python
comments_dir = os.path.join(note_folder, 'comments')
# 文件命名：comment_img_1.jpg, comment_img_2.jpg, ...
```

---

## 六、GUI功能说明

### 6.1 结果页面功能

| 功能 | 说明 |
|------|------|
| 斑马纹表格 | 奇偶行交替颜色 |
| 类型颜色 | 图文(蓝色)、视频(橙色) |
| 点击排序 | 点击表头按任意列排序 |
| 搜索筛选 | 关键词搜索 + 类型筛选 |
| 统计卡片 | 总计、图文数、视频数、总点赞 |
| 右键菜单 | 复制标题/作者/链接、打开原文、删除 |

### 6.2 详情面板

- **顶部**：标题 + 互动数据卡片（❤点赞 ⭐收藏 💬评论）
- **中部**：笔记内容 + 评论列表
- **底部**：媒体预览区（支持分页）

### 6.3 图片预览

- 更大的缩略图 (145x145px)
- 分页导航（上一页/下一页）
- 评论图片带蓝色边框标记
- 视频缩略图带播放按钮

### 6.4 快捷操作

| 按钮 | 功能 |
|------|------|
| 📂 | 打开图片文件夹 |
| ▶ | 播放视频 |
| 🔗 | 打开原文链接 |
| 📋 | 复制笔记内容 |
| 导出Excel | 导出当前表格数据 |
| 复制全部 | 复制数据到剪贴板 |

---

## 七、配置持久化

### 7.1 保存的设置

配置保存在 `data/settings.json`：

```json
{
  "keyword": "搜索关键词",
  "max_notes": 30,
  "scroll_times": 10,
  "parallel_downloads": 10,
  "download_images": true,
  "download_videos": true,
  "get_all_images": true,
  "get_content": true,
  "get_tags": true,
  "get_comments": true,
  "comments_count": 20,
  "crawl_mode": "standard",
  "crawl_type": "keyword",
  "export_format": "xlsx",
  "export_to_db": true
}
```

### 7.2 重要提示

**设置只有在正常关闭程序时才会保存！**

- ✅ 点击窗口X按钮关闭
- ❌ 直接关闭终端
- ❌ 强制结束进程

---

## 八、数据库结构

### 8.1 notes表

```sql
CREATE TABLE notes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    note_id TEXT UNIQUE,
    title TEXT,
    author TEXT,
    content TEXT,
    tags TEXT,
    publish_time TEXT,
    ip_region TEXT,
    like_count INTEGER,
    collect_count INTEGER,
    comment_count INTEGER,
    note_type TEXT,
    note_link TEXT,
    image_urls TEXT,
    video_url TEXT,
    comments TEXT,
    keyword TEXT,
    crawl_time TEXT
)
```

---

## 九、预设模式

| 预设 | download_images | get_all_images | download_videos | get_content | get_comments |
|------|-----------------|----------------|-----------------|-------------|--------------|
| 极速采集 | ✓ | ✗ | ✗ | ✗ | ✗ |
| 完整数据 | ✓ | ✓ | ✓ | ✓ | ✓ |
| 只下图片 | ✓ | ✓ | ✗ | ✗ | ✗ |
| 只下视频 | ✗ | - | ✓ | - | - |
| 只要文字 | ✗ | - | ✗ | ✓ | ✓ |

---

## 十、常见问题

### Q1: 为什么标题/点赞/图片都一样？
**原因**：数据从缓存获取，未使用当前笔记的noteId

**解决**：所有提取逻辑都已修复，使用URL中的noteId：
```javascript
const urlMatch = window.location.href.match(/explore\/([a-zA-Z0-9]+)/);
const noteId = urlMatch ? urlMatch[1] : state.note.currentNoteId;
```

### Q2: 视频无法下载？
**原因**：视频URL可能是blob格式或延迟加载

**解决**：
1. 增加等待时间
2. 多次检测视频元素
3. 多种URL获取方式备用

### Q3: Live图保存了多张？
**原因**：Live图有静态+动态两个版本

**解决**：添加了 `_filter_live_images()` 过滤，只保留一张静态版本

### Q4: 设置没有保存？
**原因**：程序非正常退出

**解决**：请通过点击窗口X按钮正常关闭程序

---

## 十一、更新日志

### 2026-02-02
- ✅ 修复标题提取缓存问题（使用URL中的noteId）
- ✅ 修复作者提取缓存问题
- ✅ 修复互动数据（点赞/收藏/评论）缓存问题
- ✅ 修复图片获取重复问题
- ✅ 修复视频URL获取问题
- ✅ 添加Live图过滤功能（只保留一张）
- ✅ 添加评论图片单独存储（comments文件夹）
- ✅ 优化GUI结果页面（斑马纹、排序、筛选、统计卡片）
- ✅ 添加右键菜单功能
- ✅ 添加图片预览分页
- ✅ 配置持久化功能
