472 lines
21 KiB
Markdown
472 lines
21 KiB
Markdown
# 文章采集方案详解
|
||
|
||
本文档详细介绍个人博客 CMS 系统中的三种文章采集方案,包括技术选型、实现原理、各平台适配策略,以及前端交互流程。
|
||
|
||
## 一、总体架构
|
||
|
||
系统提供 **三种采集入口**,对应不同的技术策略和适用场景:
|
||
|
||
| 采集方案 | 前端入口 | 后端路由 | 核心引擎 | 适用场景 |
|
||
|---------|---------|---------|---------|---------|
|
||
| **通用网页采集**(方案一) | "采集文章" | `/api/crawl/` | `curl_cffi` + `Playwright` | 传统网站:人人都是产品经理、博客园、菜鸟教程等 |
|
||
| **微信公众号采集**(方案二) | "公众号采集" | `/api/wechat-crawl/` | `Playwright` + `playwright_stealth` | 微信公众号文章(`mp.weixin.qq.com`) |
|
||
| **通用采集**(方案三) | "通用采集" | `/api/universal-crawl/` | `curl_cffi` + `Playwright` | 新一代技术社区:InfoQ、掘金、CSDN、51CTO、博客园等 |
|
||
|
||
三种方案的前端交互流程完全一致(输入 URL → 预览 → 编辑 → 保存入库),区别仅在于后端采集引擎和平台适配策略。
|
||
|
||
## 二、方案一:通用网页采集(`crawl.py`)
|
||
|
||
这是系统最早实现的采集方案,采用**多引擎组合**策略,根据目标站点的特征选择最合适的请求方式。
|
||
|
||
### 2.1 技术栈
|
||
|
||
- **HTTP 请求**:`curl_cffi`(默认,带浏览器指纹伪装)+ `requests`(备选)
|
||
- **浏览器渲染**:`Playwright`(用于 SPA/JS 渲染型站点)
|
||
- **HTML 解析**:`BeautifulSoup`(`lxml` 加速)
|
||
- **Markdown 转换**:`markdownify`(备选)+ 自研 `html_to_markdown`(主用)
|
||
|
||
### 2.2 核心流程
|
||
|
||
```
|
||
1. 提取标题 → extract_title_from_html(html)
|
||
2. 判断站点类型 → is_spa_site(html) ? SPA : 传统站点
|
||
3. 获取页面 HTML → curl_cffi / Playwright
|
||
4. 提取主要内容 → extract_main_content(html) / convert_spa_html_to_markdown()
|
||
5. 修复编码乱码 → fix_encoding(text)
|
||
6. HTML 转 Markdown → html_to_markdown(html, url)
|
||
7. 提取文章图片 → extract_images(html, url)
|
||
8. 下载图片到本地 → download_image() / download_all_images()
|
||
9. 替换 Markdown 中的图片 URL 为本地路径
|
||
10. 保存到数据库
|
||
```
|
||
|
||
### 2.3 引擎选择策略
|
||
|
||
系统根据目标域名自动判断使用哪个引擎获取 HTML:
|
||
|
||
```python
|
||
# 强制使用 Playwright 的域名(SPA / JS 渲染 / 反爬严格)
|
||
browser_domains = [
|
||
'mp.weixin.qq.com', # 微信公众号
|
||
'juejin.cn', # 掘金
|
||
'zhihu.com', # 知乎
|
||
'segmentfault.com', # SegmentFault
|
||
'infoq.cn', # InfoQ
|
||
]
|
||
|
||
# 默认使用 curl_cffi(高性能、带浏览器指纹)
|
||
# 备选 requests(纯文本站点)
|
||
```
|
||
|
||
### 2.4 平台专用适配
|
||
|
||
`extract_main_content()` 内置了多个平台的 CSS 选择器优先级列表:
|
||
|
||
| 平台 | 选择器 | 说明 |
|
||
|------|--------|------|
|
||
| 人人都是产品经理 | `.article--content.grap` | 文章正文区域 |
|
||
| 博客园 | `#cnblogs_post_body` | 博客正文容器 |
|
||
| 菜鸟教程 | `.article-intro` | 教程内容区域 |
|
||
| 掘金 | `article` | 文章标签 |
|
||
| 通用 | `article`, `main`, `.post-content`, `.article-content`, `.entry-content`, `#content` | 回退选择器 |
|
||
|
||
如果 BeautifulSoup 解析失败,系统会回退到**正则表达式匹配**。
|
||
|
||
### 2.5 上层业务逻辑
|
||
|
||
#### 2.5.1 编码修复(`fix_encoding`)
|
||
|
||
处理 UTF-8 内容被错误解码为 Latin-1 的情况(常见于老旧网站或代理转发),支持**单层修复**和**双重编码修复**:
|
||
|
||
```python
|
||
# 检测乱码特征:é, è, Ã, Â, Ã¥, â€, æ, å, è, § 等
|
||
garbled_patterns = ['é', 'è', 'Ã', 'Â', 'Ã¥', 'ü', 'ö', 'ä', 'ß', 'â€', 'æ', 'å', 'è', '§']
|
||
|
||
# 单层修复:latin1 → utf-8
|
||
text.encode('latin1').decode('utf-8')
|
||
|
||
# 双重编码修复:utf-8 → latin1 → utf-8 → latin1 → utf-8
|
||
text.encode('utf-8').decode('latin1').encode('latin1').decode('utf-8')
|
||
```
|
||
|
||
#### 2.5.2 数学公式修复(`fix_math_formulas`)
|
||
|
||
检测 Markdown 中混排的 LaTeX 数学公式,进行规范化包裹:
|
||
|
||
- **块级公式**(含 `\begin{array}`, `\sum_`, `\int_`, `\frac`, `\prod`, `\lim`, `\mathbf`, `\alpha`, `\beta`, `\gamma` 等)→ 用 `$$...$$` 包裹
|
||
- **行内公式**(含 `_{`, `^{`, `\mathbf`, `\alpha`, `\sum`, `\int`, `\frac`, `\sqrt`, `\lim`, `\prod`, `\binom`, `\overline`, `\underline`, `\widehat`, `\widetilde`, `\mathcal`, `\mathbb` 等)→ 用 `\(...\)` 包裹
|
||
- 清理已渲染的 Unicode 数学运算符
|
||
- 规范化双重反斜杠为单重
|
||
|
||
#### 2.5.3 HTML → Markdown 转换(`html_to_markdown`)
|
||
|
||
自研转换器,支持以下标签:
|
||
|
||
- `<img>` → ``,自动将相对 URL 转为绝对 URL
|
||
- `<h1>`~`<h6>` → `#` ~ `######`
|
||
- `<p>` → 段落文本
|
||
- `<pre><code>` → 代码块(自动识别 `language-xxx` 类名)
|
||
- `<code>` → 行内代码
|
||
- `<strong>`, `<b>` → `**粗体**`
|
||
- `<em>`, `<i>` → `*斜体*`
|
||
- 修复混合内容问题(HTTPS 页面中的 HTTP 图片自动升级)
|
||
- 处理 HTML 实体(` `, `<`, `>` 等)
|
||
|
||
#### 2.5.4 图片下载(`download_image`)
|
||
|
||
- 支持 `curl_cffi` 和 `requests` 双引擎下载
|
||
- 限制最大 10MB,超时 15 秒
|
||
- 生成唯一文件名:`{timestamp}_{hash[:8]}{ext}`
|
||
- 保存到 `uploads/` 目录
|
||
- 保存后返回本地 URL 映射表
|
||
|
||
### 2.6 已知局限
|
||
|
||
- 对 InfoQ 的 `.ProseMirror` 容器支持不够精细,会混入导航噪声
|
||
- 对掘金、CSDN、51CTO 等平台无专用提取逻辑,正文可能夹杂大量 UI 元素
|
||
- 对知乎无登录态支持,无法采集
|
||
|
||
---
|
||
|
||
## 三、方案二:微信公众号文章采集(`wechat_crawl.py`)
|
||
|
||
专门针对微信公众号文章(`mp.weixin.qq.com`)设计的采集方案,处理微信特有的反爬机制和页面结构。
|
||
|
||
### 3.1 技术栈
|
||
|
||
- **浏览器引擎**:`Playwright` + `playwright_stealth`(反检测)
|
||
- **HTML 解析**:`BeautifulSoup`
|
||
- **Markdown 转换**:`markdownify` + 自研微信专用修复函数
|
||
|
||
### 3.2 核心流程
|
||
|
||
```
|
||
1. Playwright 打开微信文章 URL(wait_until="networkidle", 60s 超时)
|
||
2. 注入反检测脚本(隐藏 webdriver、伪造插件列表、模拟 chrome.runtime)
|
||
3. 启用 playwright_stealth(如果可用)
|
||
4. 提取标题(优先 page.title(),回退到 #activity_name)
|
||
5. 提取公众号名称(#js_name)
|
||
6. 提取正文 HTML(#js_content)
|
||
7. 提取封面图(og:image meta)
|
||
8. 提取文章内图片(data-src 属性)
|
||
9. 微信专用 HTML 修复
|
||
10. 转换为 Markdown
|
||
11. 预览 → 下载图片(带 Referer 防盗链) → 入库
|
||
```
|
||
|
||
### 3.3 微信专用处理
|
||
|
||
#### 3.3.1 data-src 修复(`fix_wechat_images`)
|
||
|
||
微信文章使用 `data-src` 存放真实图片 URL,`src` 往往是 SVG 占位图或空值:
|
||
|
||
```python
|
||
for img in soup.find_all('img'):
|
||
data_src = img.get('data-src', '')
|
||
src = img.get('src', '')
|
||
# data-src 是真实 URL,且 src 不是有效的 http URL
|
||
if data_src and data_src.startswith('http') and (
|
||
not src or src.startswith('data:image') or not src.startswith('http')
|
||
):
|
||
img['src'] = data_src
|
||
# 清理无意义的占位属性
|
||
del img['data-src']
|
||
```
|
||
|
||
#### 3.3.2 代码块修复(`fix_wechat_code_blocks`)
|
||
|
||
微信代码块使用 `<pre class="code-snippet__js">` + `<code>` + `<span class="code-snippet__line">` 结构,需要按行重组:
|
||
|
||
```python
|
||
for pre in soup.find_all('pre', class_=re.compile('code-snippet')):
|
||
code_tag = pre.find('code')
|
||
lines = []
|
||
for span in code_tag.find_all('span', class_='code-snippet__line'):
|
||
lines.append(span.get_text(separator='', strip=False))
|
||
code_tag.clear()
|
||
for line in lines:
|
||
code_tag.append(NavigableString(line + '\n'))
|
||
```
|
||
|
||
#### 3.3.3 空段落清理(`remove_empty_paragraphs`)
|
||
|
||
移除没有文本内容也没有图片的空 `<p>` 标签。
|
||
|
||
#### 3.3.4 微信图片防盗链绕过
|
||
|
||
微信图片服务器(`mmbiz.qpic.cn`)对直接访问会返回 403,必须在请求头中带上 `Referer`:
|
||
|
||
```python
|
||
headers = {
|
||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
||
"Referer": "https://mp.weixin.qq.com/", # 关键!
|
||
}
|
||
```
|
||
|
||
下载时通过 `curl_cffi` 或 `requests` 发送带 Referer 的请求,成功后将图片保存到本地。
|
||
|
||
### 3.4 测试结果
|
||
|
||
微信公众号文章采集**测试通过**,标题、正文、代码块、图片均能正常采集。腾讯的反爬策略主要集中在异常流量检测,Playwright + stealth 组合可以稳定绕过。
|
||
|
||
---
|
||
|
||
## 四、方案三:通用采集(`universal_crawl.py`)
|
||
|
||
这是为**新一代技术社区**专门设计的采集方案,核心理念是**"专用选择器提取 + 复用上层业务逻辑"**。
|
||
|
||
### 4.1 设计目标
|
||
|
||
- 针对各平台页面结构差异大、通用提取效果差的问题
|
||
- 为每个主流平台编写专用 CSS 选择器,精准提取正文
|
||
- 复用 `crawl.py` 中已验证成熟的上层业务逻辑(编码修复、数学公式、HTML→Markdown、图片下载)
|
||
- 保持与方案一/方案二完全一致的前端交互流程
|
||
|
||
### 4.2 技术栈
|
||
|
||
- **HTTP 请求**:`curl_cffi`(默认)+ `Playwright`(SPA / 反爬严格站点)
|
||
- **HTML 解析**:`BeautifulSoup`(`lxml` 加速)
|
||
- **上层逻辑**:全部从 `crawl.py` 复用:`extract_title_from_html`、`fix_encoding`、`fix_math_formulas`、`html_to_markdown`、`extract_images`、`extract_images_from_markdown`、`download_image`、`download_all_images`
|
||
|
||
### 4.3 引擎选择策略
|
||
|
||
```python
|
||
browser_domains = [
|
||
'mp.weixin.qq.com', # 微信公众号
|
||
'juejin.cn', # 掘金(JS 渲染)
|
||
'zhihu.com', # 知乎(未登录无法采集)
|
||
'segmentfault.com', # SegmentFault(WAF 拦截)
|
||
'infoq.cn', # InfoQ(JS 渲染)
|
||
'51cto.com', # 51CTO(JS 渲染)
|
||
]
|
||
```
|
||
|
||
对于非 browser_domains 的站点,默认使用 `curl_cffi` 获取 HTML,性能更好。
|
||
|
||
### 4.4 Playwright 反爬配置
|
||
|
||
通用采集的 Playwright 配置比方案一更完善:
|
||
|
||
```python
|
||
browser = await p.chromium.launch(
|
||
headless=True,
|
||
args=[
|
||
"--disable-blink-features=AutomationControlled",
|
||
"--disable-web-security",
|
||
"--disable-features=IsolateOrigins,site-per-process",
|
||
"--disable-dev-shm-usage",
|
||
"--no-sandbox",
|
||
]
|
||
)
|
||
context = await browser.new_context(
|
||
user_agent="...Chrome/120.0.0.0 Edg/120.0.0.0...",
|
||
viewport={"width": 1920, "height": 1080},
|
||
locale="zh-CN",
|
||
timezone_id="Asia/Shanghai",
|
||
extra_http_headers={
|
||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
||
"Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',
|
||
"Sec-Ch-Ua-Mobile": "?0",
|
||
"Sec-Ch-Ua-Platform": '"Windows"',
|
||
"Sec-Fetch-Dest": "document",
|
||
"Sec-Fetch-Mode": "navigate",
|
||
"Sec-Fetch-Site": "none",
|
||
"Sec-Fetch-User": "?1",
|
||
"Upgrade-Insecure-Requests": "1",
|
||
},
|
||
)
|
||
```
|
||
|
||
对于知乎,采用**先访问首页建立 Cookie,再跳转目标页面**的策略:
|
||
|
||
```python
|
||
if 'zhihu.com' in url:
|
||
await page.goto("https://www.zhihu.com", wait_until="domcontentloaded", timeout=15000)
|
||
await asyncio.sleep(2)
|
||
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
|
||
await asyncio.sleep(3)
|
||
# 等待正文加载
|
||
await page.wait_for_selector(".Post-RichTextContainer, .RichContent-inner", timeout=10000)
|
||
```
|
||
|
||
### 4.5 平台专用适配
|
||
|
||
#### 4.5.1 专用内容提取
|
||
|
||
| 平台 | 主选择器 | 备选选择器 | 说明 |
|
||
|------|---------|-----------|------|
|
||
| **InfoQ** | `.ProseMirror` | `article` | InfoQ 使用 ProseMirror 编辑器渲染正文,包含干净的结构化内容 |
|
||
| **掘金** | `article.article` | `.article-viewer`, `#article-root` | 掘金文章正文在 `<article class="article">` 中 |
|
||
| **CSDN** | `#content_views` | `.article_content` | CSDN 正文主容器,排除侧边栏、评论等噪声 |
|
||
| **博客园** | `#cnblogs_post_body` | `.postBody` | 博客园经典正文容器 |
|
||
| **51CTO** | `.article-content` | `article` | 51CTO 文章正文区域 |
|
||
|
||
#### 4.5.2 专用标题提取
|
||
|
||
各平台 `<title>` 标签通常包含站点后缀,需要清洗:
|
||
|
||
| 平台 | 清洗规则 |
|
||
|------|---------|
|
||
| **InfoQ** | 去掉 `" - InfoQ"` 后缀 |
|
||
| **掘金** | 去掉 `" - 掘金"` 后缀 |
|
||
| **CSDN** | 去掉 `"-CSDN博客"` / `" - CSDN博客"` 后缀 |
|
||
| **博客园** | 去掉 `" - 博客园"` 后缀 |
|
||
| **51CTO** | 去掉 `"-51CTO.COM"` 后缀 |
|
||
|
||
#### 4.5.3 图片过滤
|
||
|
||
各平台正文容器中常常混入 UI 图标(收藏、点赞、logo、二维码等),需要过滤:
|
||
|
||
| 平台 | 过滤规则 |
|
||
|------|---------|
|
||
| **CSDN** | 跳过 `newHeart2023`、`tobarCollect`、`tobarCollection`、`copyright`、`avatar`、`blogv2/dist/pc/img` 等关键词的图片 |
|
||
| **博客园** | 跳过 `assets.cnblogs.com/logo`、`assets.cnblogs.com/icons`、`skins/custom/images/logo` |
|
||
| **51CTO** | 跳过公众号矩阵二维码(`oss/202302`)、软考二维码(`oss/202408`)、顶部横幅(`oss/202506/06`)、APP 下载图(`oss/202302/07`) |
|
||
|
||
### 4.6 测试结果汇总
|
||
|
||
| 平台 | 测试链接 | 结果 | 正文长度 | 图片数 | 说明 |
|
||
|------|---------|------|---------|--------|------|
|
||
| **微信公众号** | `mp.weixin.qq.com/s/...` | ✅ 正常 | ~6000 | 24 | Playwright + stealth 稳定采集,防盗链通过 |
|
||
| **InfoQ** | `infoq.cn/article/...` | ✅ 正常 | ~9000 | 2 | `.ProseMirror` 精准提取,无导航噪声 |
|
||
| **掘金** | `juejin.cn/post/...` | ✅ 正常 | ~6253 | 26 | `article.article` 精准提取,JS 渲染正常 |
|
||
| **CSDN** | `blog.csdn.net/...` | ✅ 正常 | ~9678 | 91 | `#content_views` 精准提取,过滤 UI 图标 |
|
||
| **博客园** | `cnblogs.com/...` | ✅ 正常 | ~17092 | 17 | `#cnblogs_post_body` 精准提取,过滤 logo |
|
||
| **51CTO** | `51cto.com/article/...` | ✅ 正常 | ~14177 | 36 | `.article-content` 精准提取,过滤广告二维码 |
|
||
| **知乎** | `zhuanlan.zhihu.com/...` | ❌ 失败 | — | — | 强制登录,未登录跳转登录页,无法采集 |
|
||
| **SegmentFault** | `segmentfault.com/...` | ❌ 失败 | — | — | 雷池 WAF 拦截,返回 "客户端异常,请确认您是合法用户" |
|
||
|
||
#### 详细说明
|
||
|
||
**✅ 微信公众号**
|
||
- 使用 Playwright + `playwright_stealth` 模拟真实浏览器
|
||
- 注入反检测脚本隐藏 `navigator.webdriver`
|
||
- 图片使用 `data-src` 修复,下载时带 `Referer: https://mp.weixin.qq.com/` 绕过防盗链
|
||
- 代码块、空段落、标题均处理正常
|
||
|
||
**✅ InfoQ**
|
||
- InfoQ 页面使用 JS 渲染,必须走 Playwright
|
||
- 通用提取会把导航栏、推荐阅读等混入正文(37265 字符)
|
||
- 使用 `.ProseMirror` 专用选择器后,正文精简到 9000 字符,完全无噪声
|
||
|
||
**✅ 掘金**
|
||
- 掘金是 SPA,必须走 Playwright
|
||
- 通用提取会混入头部导航和底部推荐
|
||
- 使用 `article.article` 专用选择器后,正文从导航处开始,干净整洁
|
||
|
||
**✅ CSDN**
|
||
- CSDN 页面为服务端渲染,可用 curl_cffi 获取
|
||
- 通用提取会混入收藏按钮、版权声明、专栏信息、相关推荐等
|
||
- 使用 `#content_views` 专用选择器 + UI 图标过滤后,正文从第一张图开始
|
||
|
||
**✅ 博客园**
|
||
- 博客园页面为服务端渲染,可用 curl_cffi 获取
|
||
- 通用提取混入博客园 logo、会员周边、搜索栏、用户头像等
|
||
- 使用 `#cnblogs_post_body` 专用选择器 + logo 过滤后,正文干净
|
||
|
||
**✅ 51CTO**
|
||
- 51CTO 使用 JS 渲染,必须走 Playwright
|
||
- 通用提取混入首页导航、AI 社区、博客学堂、公众号矩阵二维码等
|
||
- 使用 `.article-content` 专用选择器 + 二维码过滤后,正文从编辑信息开始
|
||
|
||
**❌ 知乎**
|
||
- 知乎对所有未登录的浏览器请求强制跳转登录页
|
||
- 即使先访问首页建立 Cookie、使用 stealth、调整 viewport 均无效
|
||
- 这是知乎的反爬策略,与采集工具无关,必须提供登录态 Cookie 才能解决
|
||
|
||
**❌ SegmentFault**
|
||
- SegmentFault 部署了**雷池 WAF**(SafeLine Web Application Firewall)
|
||
- 无论 curl_cffi(多种浏览器指纹)还是 Playwright(先访问首页建立信任)均被拦截
|
||
- 返回 HTTP 468 + "安全检测能力由 雷池 WAF 驱动 / 客户端异常,请确认您是合法用户"
|
||
- 可能需要更换代理 IP 或使用更高级的设备指纹模拟才能绕过
|
||
|
||
### 4.7 扩展新平台的方法
|
||
|
||
要为新平台添加支持,只需在 `universal_crawl.py` 中完成四步:
|
||
|
||
1. **判断域名**:在 `fetch()` 中添加 `is_xxx = 'xxx.com' in url`
|
||
2. **提取标题**:编写 `_extract_xxx_title(html)` 函数,清洗 `<title>` 后缀
|
||
3. **提取正文**:编写 `_extract_xxx_content(html)` 函数,返回精准容器的 HTML
|
||
4. **过滤图片**(可选):编写 `_filter_xxx_images(images)` 函数,过滤 UI 图标
|
||
5. **接入主流程**:在 `fetch()` 的标题提取、正文转换、图片过滤三个分支中加入新平台
|
||
|
||
不需要修改 `html_to_markdown`、`fix_encoding`、`fix_math_formulas` 等上层逻辑,它们对所有平台通用。
|
||
|
||
---
|
||
|
||
## 五、三种方案对比
|
||
|
||
| 维度 | 方案一(通用网页) | 方案二(公众号) | 方案三(通用采集) |
|
||
|------|------------------|----------------|------------------|
|
||
| **目标站点** | 人人都是产品经理、博客园、菜鸟教程 | 微信公众号 | InfoQ、掘金、CSDN、博客园、51CTO |
|
||
| **核心引擎** | curl_cffi / Playwright | Playwright + stealth | curl_cffi / Playwright |
|
||
| **平台适配** | 通用选择器(CSS + 正则回退) | 微信专用(data-src、代码块、防盗链) | 各平台专用选择器 |
|
||
| **编码修复** | ✅ | ❌(微信无此问题) | ✅(复用方案一) |
|
||
| **数学公式** | ✅ | ❌(微信无此问题) | ✅(复用方案一) |
|
||
| **图片下载** | ✅ 通用 | ✅ 微信防盗链 | ✅(复用方案一) |
|
||
| **SPA 支持** | ✅ | ✅(必须) | ✅ |
|
||
| **扩展性** | 修改通用选择器列表 | 固定(仅微信) | 新增平台只需四步 |
|
||
|
||
---
|
||
|
||
## 六、前端交互流程
|
||
|
||
三种采集方案的前端页面结构完全一致(`CrawlEdit.jsx` / `WechatCrawlEdit.jsx` / `UniversalCrawlEdit.jsx`),交互流程为:
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ 步骤1:输入 URL │
|
||
│ ├─ 文本框输入文章链接 │
|
||
│ └─ "采集预览" 按钮 │
|
||
├─────────────────────────────────────────┤
|
||
│ 步骤2:预览采集结果 │
|
||
│ ├─ 显示文章标题(可编辑) │
|
||
│ ├─ 显示 Markdown 正文(可编辑) │
|
||
│ ├─ 显示图片列表(可删除) │
|
||
│ └─ 侧边栏显示文章元信息 │
|
||
├─────────────────────────────────────────┤
|
||
│ 步骤3:编辑与调整 │
|
||
│ ├─ 修改标题 │
|
||
│ ├─ 修改正文(Markdown 编辑器) │
|
||
│ ├─ 删除不需要的图片 │
|
||
│ └─ 选择文章分类 │
|
||
├─────────────────────────────────────────┤
|
||
│ 步骤4:保存入库 │
|
||
│ ├─ "保存文章" 按钮 │
|
||
│ ├─ 后端下载图片到本地 │
|
||
│ ├─ 替换 Markdown 中的图片 URL │
|
||
│ └─ 写入 SQLite 数据库 │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
API 调用时序:
|
||
|
||
```
|
||
POST /api/{scheme}/preview → 采集并返回预览数据(标题、正文、图片列表)
|
||
POST /api/{scheme}/save → 确认保存(下载图片、替换 URL、入库)
|
||
```
|
||
|
||
其中 `{scheme}` 对应三种方案:`crawl`(方案一)、`wechat-crawl`(方案二)、`universal-crawl`(方案三)。
|
||
|
||
---
|
||
|
||
## 七、总结与建议
|
||
|
||
### 7.1 使用建议
|
||
|
||
| 场景 | 推荐方案 |
|
||
|------|---------|
|
||
| 微信公众号文章 | 方案二(公众号采集) |
|
||
| InfoQ、掘金、CSDN、博客园、51CTO | 方案三(通用采集) |
|
||
| 人人都是产品经理、菜鸟教程 | 方案一(通用网页采集) |
|
||
| 其他未知站点 | 先尝试方案三,失败再尝试方案一 |
|
||
|
||
### 7.2 未来优化方向
|
||
|
||
1. **知乎登录态支持**:通过 Cookie 池或扫码登录获取知乎登录态,解决知乎采集问题
|
||
2. **SegmentFault 绕过**:尝试更换出口 IP 或更高级的设备指纹模拟
|
||
3. **自动平台识别**:通过页面特征自动判断站点类型,减少人工选择采集方案的步骤
|
||
4. **智能摘要生成**:接入大模型 API,对采集内容自动生成摘要和标签
|
||
5. **更多平台适配**:根据使用需求,逐步添加开源中国(oschina)、简书(jianshu)等平台
|