Metadata-Version: 2.1
Name: dlm-li-cat
Version: 0.1.0
Summary: LinkedIn Search CLI - 基于 Typer 的 LinkedIn 搜索功能管理和执行工具
Author-email: Chandler <275737875@qq.com>
License: MIT
Requires-Python: >=3.7
Description-Content-Type: text/markdown
Requires-Dist: typer >=0.9.0
Requires-Dist: rich >=12.0.0
Requires-Dist: django <2,>=1.11
Requires-Dist: redis <5,>=4.0
Requires-Dist: requests >=2.31.0
Requires-Dist: pandas >=1.0
Requires-Dist: pymysql <2,>=1.0
Requires-Dist: django-redis <5,>=4.0

# dlm-li-cat

LinkedIn Search CLI Tool — 基于 Typer 构建的命令行应用程序，专门用于 LinkedIn 搜索功能的管理和执行。

## 项目概述

`dlm-cli` 是一个命令行工具，提供完整的 LinkedIn 搜索任务管理和执行能力。主要包含以下功能：

- **Cookie 管理** — 展示、测试、缓存 LinkedIn Cookie，确保搜索任务有可用账号
- **LinkedIn 搜索** — 根据关键词、地点、公司、职位等条件，遍历 268 个中文姓氏，批量搜索 LinkedIn 用户
- **项目信息查看** — 实时查看数据库中的 Cookie 状态、搜索词数量、候选人总数等统计信息
- **姓氏初始化** — 将配置文件中的姓氏列表写入 Redis，供搜索任务使用

工具使用 Rich 库提供彩色格式化输出、进度条和状态面板，用户体验友好。

## 环境要求

| 要求 | 说明 |
|------|------|
| **Python** | >= 3.7（推荐 3.7 ~ 3.11） |
| **操作系统** | Windows / macOS / Linux |
| **Django** | 1.11.x（项目依赖） |
| **MySQL** | 可访问的 MySQL 数据库（存储候选人数据） |
| **Redis** | 可访问的 Redis 服务（缓存 Cookie 和搜索状态） |
| **环境变量** | 需通过环境变量配置数据库和 Redis 连接信息（见下文） |

## 安装步骤

### 1. 克隆项目

```bash
git clone <repository_url>
```

项目根目录结构应包含：

```
.
├── dlm-cli/              # dlm-cli 包目录（内嵌 linkedin API 模块）
├── web/                  # Django Web 应用（数据模型，运行时依赖）
├── utils/                # 工具模块（独立使用）
├── script/               # 原有脚本（独立使用）
├── django_settings.py    # Django 配置
└── requirements.txt      # Python 依赖
```

### 2. 安装系统依赖

确保已安装 MySQL 客户端库和 Redis 服务（如果是远程连接则无需本地安装）。

对于 Windows，安装 `pymysql` 即可满足 MySQL 连接需求，已在依赖中处理。

### 3. 使用 pip 安装

#### 方式一：从源码安装（开发模式推荐）

```bash
# 使用 --no-build-isolation 避免 SSL 问题
pip install --no-build-isolation -e dlm-cli
```

此方式将包以可编辑模式安装到当前 Python 环境，修改源码即时生效。

#### 方式二：从 PyPI 安装（待发布）

```bash
pip install dlm-li-cat
```

### 4. 安装项目依赖

在项目根目录执行：

```bash
pip install -r requirements.txt
```

`requirements.txt` 包含以下核心依赖：

| 包 | 版本 | 用途 |
|------|---------|---------|
| Django | 1.11.29 | Web 框架、ORM、数据模型 |
| redis | 4.3.1 | Redis 缓存操作 |
| pymysql | >=1.0,<2.0 | MySQL 数据库驱动 |
| django-redis | >=4.0,<5.0 | Django Redis 缓存后端 |
| requests | 2.31.0 | HTTP 请求（LinkedIn API） |

### 5. 安装 typer 和 rich

`dlm-cli` 依赖 `typer` 和 `rich` 两个核心库。如果 pip 安装因 SSL 问题失败，推荐使用 conda 安装：

```bash
# 使用 conda-forge 通道安装（推荐，可避免 SSL 问题）
conda install -c conda-forge typer rich
```

或使用 pip：

```bash
pip install typer rich
```

### 6. 在 conda 环境中安装

```bash
# 创建 conda 环境
conda create -n linkedin_search python=3.7
conda activate linkedin_search

# 安装核心依赖（优先使用 conda）
conda install -c conda-forge typer rich

# 安装项目依赖
pip install -r requirements.txt

# 安装 dlm-li-cat
pip install --no-build-isolation -e dlm-cli
```

> **注意**：在 conda 环境中，如果遇到 pip SSL 错误，请优先使用 `conda install -c conda-forge` 安装包，而非 pip。

### 7. 配置环境变量

配置数据库和 Redis 连接信息（详见下方[配置说明](#%E9%85%8D%E7%BD%AE%E8%AF%B4%E6%98%8E)）。

## 配置说明

> **安全提醒**：`django_settings.py` 中的数据库密码和 Redis 密码等敏感信息已改为从环境变量读取。
> **配置文件不会被打包到 dlm-cli 分发包中**，仅作为运行时的依赖文件存在于项目根目录。

---

### 一、环境变量配置总览

项目使用**环境变量**来管理所有敏感配置信息（数据库密码、Redis 密码等），而非将凭据硬编码在代码中。这样做有以下好处：

- **安全性**：敏感信息不会进入代码仓库或分发包
- **灵活性**：同一份代码可针对不同环境（开发、测试、生产）使用不同配置
- **可维护性**：修改配置无需改动代码，只需调整环境变量

项目提供了 `.env.template` 模板文件（位于项目根目录），其中列出了所有可配置的环境变量及其默认值。

---

### 二、完整使用教程

#### 步骤 1：复制模板文件

```bash
# Windows（PowerShell / CMD）
copy .env.template .env

# Linux / macOS
cp .env.template .env
```

执行后项目根目录会出现一个 `.env` 文件，这是你的**本地配置**文件，包含所有可修改的连接参数。

---

#### 步骤 2：编辑 `.env` 文件

用任意文本编辑器（记事本、VS Code、Notepad++ 等）打开 `.env` 文件，按实际环境修改配置值。

> **下面对每个配置项进行详细说明：**

##### MySQL 数据库配置

```ini
# === MySQL 数据库配置 ===
# 数据库主机地址（IP 或域名）
# - 如果 MySQL 在本机：127.0.0.1 或 localhost
# - 如果 MySQL 在远程服务器：填写服务器 IP 地址
DLM_DB_HOST=127.0.0.1

# 数据库端口
# - MySQL 默认端口为 3306，如未修改则无需改动
DLM_DB_PORT=3306

# 数据库名称
# - 连接的具体数据库名，默认为 dlm
DLM_DB_NAME=dlm

# 数据库用户名
# - 用于连接 MySQL 的用户名，默认为 dlm
DLM_DB_USER=dlm

# 数据库密码（必填）
# - 请务必修改为实际的 MySQL 密码
DLM_DB_PASSWORD=your_db_password
```

##### Redis 配置

```ini
# === Redis 配置 ===
# Redis 服务器地址
# - 如果 Redis 在本机：127.0.0.1 或 localhost
# - 如果 Redis 在远程服务器：填写服务器 IP 地址
DLM_REDIS_HOST=127.0.0.1

# Redis 密码（必填）
# - 请务必修改为实际的 Redis 密码
DLM_REDIS_PASSWORD=your_redis_password
```

##### 可选配置项

```ini
# === Django 安全配置（可选） ===
# Django SECRET_KEY，用于加密签名
# - 不设置则使用 django_settings.py 中的内置默认值
# - 生产环境建议设置一个随机字符串
# DLM_SECRET_KEY=your-secret-key-here

# === dlm-cli 项目路径（可选） ===
# 如果未设置，dlm-cli 会自动探测或在命令中通过 --project-dir 指定
# DLM_CLI_PROJECT_DIR=D:\LLMCode\2026\20260517
```

---

#### 步骤 3：加载环境变量

编辑并保存 `.env` 文件后，在执行 dlm-cli 命令**之前**，需要先将这些变量加载到当前终端会话中。

##### Windows PowerShell（推荐）

```powershell
# 方式一：一键加载整个 .env 文件（推荐）
Get-Content .env | ForEach-Object {
    if ($_ -match "^([^#=]+)=(.+)$") {
        [Environment]::SetEnvironmentVariable($matches[1], $matches[2], "Process")
    }
}
```

执行后验证是否加载成功：

```powershell
# 查看已设置的环境变量
$env:DLM_DB_HOST
$env:DLM_DB_PASSWORD
```

##### Windows 命令提示符（CMD）

```cmd
:: 逐条设置（每条命令分别执行）
set DLM_DB_HOST=127.0.0.1
set DLM_DB_PORT=3306
set DLM_DB_NAME=dlm
set DLM_DB_USER=dlm
set DLM_DB_PASSWORD=你的数据库密码
set DLM_REDIS_HOST=127.0.0.1
set DLM_REDIS_PASSWORD=你的Redis密码
```

##### Linux / macOS（Bash）

```bash
# 方式一：逐条导出
export DLM_DB_HOST=127.0.0.1
export DLM_DB_PORT=3306
export DLM_DB_NAME=dlm
export DLM_DB_USER=dlm
export DLM_DB_PASSWORD=你的数据库密码
export DLM_REDIS_HOST=127.0.0.1
export DLM_REDIS_PASSWORD=你的Redis密码

# 方式二：使用 source 加载 .env 文件（需确保 .env 格式为 export KEY=VALUE）
# 先在 .env 中每行前加 export，然后执行：
# source .env
```

##### 设置系统级持久环境变量（Windows）

如果希望每次打开终端都自动加载，可将变量添加到系统环境变量中：

```powershell
# PowerShell（以管理员身份运行）
[Environment]::SetEnvironmentVariable("DLM_DB_HOST", "127.0.0.1", "Machine")
[Environment]::SetEnvironmentVariable("DLM_DB_PASSWORD", "你的数据库密码", "Machine")
```

> **提示**：设置系统级变量后需要**重新打开终端**才能生效。

---

#### 步骤 4：验证配置是否生效

加载环境变量后，运行 `info` 命令查看项目统计信息，以确认连接正常：

```bash
dlm-cli info
```

如果配置正确，会显示类似以下输出：

```
┌─────────────────────────── 📊 项目信息 ───────────────────────────┐
│ 指标                     │ 数值                                   │
├───────────────────────────┼────────────────────────────────────────┤
│ 项目路径                 │ D:\LLMCode\2026\20260517               │
│ Cookie 总数              │ 153                                    │
│ ...                      │ ...                                    │
└────────────────────────────────────────────────────────────────────┘
┌──────────────────┐
│ ✅ 环境正常      │
└──────────────────┘
```

如果连接失败，会显示详细的错误信息，帮助定位问题。

---

### 三、环境变量与 django_settings.py 对应关系

以下表格展示了每个环境变量与 `django_settings.py` 中代码的精确对应关系：

| 环境变量 | 对应代码位置 | 代码示例 |
|----------|------------|----------|
| `DLM_DB_HOST` | `DATABASES['default']['HOST']` | `_env('DLM_DB_HOST', '127.0.0.1')` |
| `DLM_DB_PORT` | `DATABASES['default']['PORT']` | `int(_env('DLM_DB_PORT', '3306'))` |
| `DLM_DB_NAME` | `DATABASES['default']['NAME']` | `_env('DLM_DB_NAME', 'dlm')` |
| `DLM_DB_USER` | `DATABASES['default']['USER']` | `_env('DLM_DB_USER', 'dlm')` |
| `DLM_DB_PASSWORD` | `DATABASES['default']['PASSWORD']` | `_env('DLM_DB_PASSWORD', '123456')` |
| `DLM_REDIS_HOST` | `REDIS_HOST` 和 `CACHES['default']['LOCATION']` | `_env('DLM_REDIS_HOST', '127.0.0.1')` |
| `DLM_REDIS_PASSWORD` | `REDIS_PASSWORD` 和 `CACHES['default']['OPTIONS']['PASSWORD']` | `_env('DLM_REDIS_PASSWORD', '123456')` |
| `DLM_SECRET_KEY` | `SECRET_KEY` | `_env('DLM_SECRET_KEY', '...默认值...')` |

核心读取逻辑（`django_settings.py` 中的 `_env` 函数）：

```python
def _env(key, default):
    """读取环境变量，若未设置则返回默认值"""
    return os.environ.get(key, default)
```

---

### 四、环境变量优先级规则

配置的读取优先级如下（从高到低）：

```
高优先级   操作系统环境变量（export/set 设置）
            ↓
            .env.template 中的默认值（源码自带）
低优先级   硬编码回退值（django_settings.py 中的 _env() 第二个参数）
```

**具体说明：**

1. **最高优先级** — 如果通过 `export`（Linux/macOS）或 `set`（Windows）设置了系统环境变量，则该值会被优先使用
2. **中间优先级** — 如果系统环境变量未设置，则使用 `_env()` 函数第二参数指定的默认值
3. **回退机制** — 如果环境变量未设置且未提供默认值，`os.environ.get()` 返回 `None`

**配置生效示例：**

| 场景 | `DLM_DB_HOST` 环境变量 | `_env()` 默认值 | 实际使用的值 |
|------|----------------------|-----------------|-------------|
| 显式设置环境变量 | `192.168.1.100` | `127.0.0.1` | `192.168.1.100` ✅ |
| 未设置环境变量 | 未设置 | `127.0.0.1` | `127.0.0.1` ✅ |
| 设置空值 | （空字符串） | `127.0.0.1` | （空字符串）⚠️ |

> **注意**：如果环境变量被设置为空字符串，则 `os.environ.get()` 会返回空字符串而非默认值。请确保设置的值非空。

---

### 五、环境变量清单

| 环境变量 | 对应配置项 | 默认值 | 必需 | 类型 | 详细说明 |
|----------|-----------|--------|------|------|---------|
| `DLM_DB_HOST` | MySQL 主机地址 | `127.0.0.1` | 否 | IP/域名 | 数据库服务器地址，本地为 `127.0.0.1`，远程填 IP |
| `DLM_DB_PORT` | MySQL 端口 | `3306` | 否 | 整数 | 数据库端口号，默认 3306 |
| `DLM_DB_NAME` | 数据库名 | `dlm` | 否 | 字符串 | 要连接的数据库名称 |
| `DLM_DB_USER` | 数据库用户 | `dlm` | 否 | 字符串 | 数据库登录用户名 |
| `DLM_DB_PASSWORD` | 数据库密码 | `123456` | **是** | 字符串 | 数据库登录密码，**生产环境必须修改** |
| `DLM_REDIS_HOST` | Redis 主机地址 | `127.0.0.1` | 否 | IP/域名 | Redis 服务器地址 |
| `DLM_REDIS_PASSWORD` | Redis 密码 | `123456` | **是** | 字符串 | Redis 认证密码，**生产环境必须修改** |
| `DLM_SECRET_KEY` | Django 密钥 | 内置默认值 | 否 | 字符串 | Django 签名密钥，生产环境建议自定义 |
| `DLM_CLI_PROJECT_DIR` | 项目根路径 | 自动探测 | 否 | 目录路径 | dlm-cli 查找 django_settings.py 的目录 |

---

### 六、安全注意事项

#### 1. 不要将 `.env` 文件提交到版本控制

`.env` 文件包含敏感信息，应确保被 `.gitignore` 忽略。项目根目录的 `.gitignore` 应包含以下规则：

```
# 环境变量配置（包含敏感信息）
.env
```

> 如果项目尚未创建 `.gitignore`，请手动创建并添加上面这行。

#### 2. 模板与配置分离

- `.env.template` — 配置文件模板，**可以**提交到版本控制，作为新手的配置参考
- `.env` — 实际配置文件（从模板复制而来），**不应**提交到版本控制

#### 3. 定期更新凭据

- 数据库密码和 Redis 密码应定期更换
- LinkedIn Cookie（LI_AT 和 JSESSIONID）通常有效期为 1 年左右，需定期更新
- 建议每季度使用 `dlm-cli cookies test` 检查 Cookie 有效性

#### 4. 不同环境使用不同配置

- **开发环境**：推荐使用本地 `127.0.0.1` 的 MySQL 和 Redis
- **生产环境**：使用远程服务器地址，通过环境变量注入凭据
- 通过切换环境变量值即可切换环境，无需修改代码

#### 5. 权限控制

- 确保 `.env` 文件的权限仅为当前用户可读（Linux/macOS 下执行 `chmod 600 .env`）
- 不要在不安全的网络中使用明文密码
- 如需在脚本中使用凭据，优先使用环境变量而非硬编码

---

### 七、配置效果示例

#### 场景 A：本地开发配置

```ini
# .env 文件
DLM_DB_HOST=127.0.0.1
DLM_DB_PORT=3306
DLM_DB_NAME=dlm_dev
DLM_DB_USER=root
DLM_DB_PASSWORD=local_dev_password
DLM_REDIS_HOST=127.0.0.1
DLM_REDIS_PASSWORD=
```

**效果**：连接到本地数据库 `dlm_dev`，Redis 无密码，适合本地开发和调试。

#### 场景 B：远程服务器配置

```ini
# .env 文件
DLM_DB_HOST=127.0.0.1
DLM_DB_PORT=3306
DLM_DB_NAME=dlm
DLM_DB_USER=dlm
DLM_DB_PASSWORD=production_password
DLM_REDIS_HOST=127.0.0.1
DLM_REDIS_PASSWORD=redis_production_password

# 指定项目路径
DLM_CLI_PROJECT_DIR=/opt/linkedin_search
```

**效果**：连接到远程服务器上的数据库和 Redis，适合生产环境部署。

---

### 八、项目路径探测

`dlm-cli` 会自动从当前工作目录向上查找包含 `django_settings.py` 的项目根目录。如果自动探测失败，可通过以下方式指定：

1. **`--project-dir` / `-d` 参数**：在每个命令中手动指定项目路径
2. **`DLM_CLI_PROJECT_DIR` 环境变量**：设置后无需每次指定

```bash
# 方式一：参数指定
dlm-cli --project-dir D:\LLMCode\2026\20260517 info

# 方式二：环境变量（推荐）
set DLM_CLI_PROJECT_DIR=D:\LLMCode\2026\20260517
dlm-cli info
```

### 姓氏列表

配置文件中的 `FAMILY_NAMES` 定义了搜索时遍历的姓氏（共 268 个），包括单姓和复姓（如欧阳、百里、第五等）。可通过 `dlm-cli init-names` 命令同步到 Redis。

> **提示**：姓氏列表定义在 `django_settings.py` 的 `FAMILY_NAMES` 常量中，如需修改可在该文件中调整。

## 使用方法

### 全局帮助

```bash
dlm-cli --help
```

输出：

```
 Usage: dlm-cli [OPTIONS] COMMAND [ARGS]...

 dlm-cli - LinkedIn Search CLI Tool

 提供完整的 LinkedIn 搜索任务管理和执行能力。

 Options:
   -V, --version              显示版本信息
   -d, --project-dir TEXT     项目根目录路径（默认自动探测）[envvar: DLM_CLI_PROJECT_DIR]
   --help                     Show this message and exit.

 Commands:
   cookies      Cookie 管理：展示、测试、更新缓存
   init-names   初始化 Redis 中的家族姓氏列表
   info         显示项目环境信息
   search       执行 LinkedIn 搜索任务
```

### 查看版本

```bash
dlm-cli --version
```

输出：

```
dlm-cli v0.1.0
```

### `dlm-cli info` — 显示项目信息

查看项目的数据库连接状态、Redis 状态、Cookie 数量等关键统计信息。

```bash
dlm-cli info
```

示例输出：

```
┌─────────────────────────── 📊 项目信息 ───────────────────────────┐
│ 指标                     │ 数值                                   │
├───────────────────────────┼────────────────────────────────────────┤
│ 项目路径                 │ D:\LLMCode\2026\20260517               │
│ Cookie 总数              │ 153                                    │
│ 有效 Cookie              │ 139                                    │
│ 搜索词数                 │ 2577                                   │
│ 候选人总数               │ 1,344,180                              │
│ 待执行任务               │ 19597                                  │
│ 已完成任务               │ 31146                                  │
└────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ ✅ 环境正常  │
│ Django 配置、Redis 连接、数据库连接均正常。│
└──────────────┘
```

### `dlm-cli cookies show` — 显示 Cookie 列表

显示数据库中所有 Cookie 的详细信息，包括用户名、LinkedIn 名称、状态、活跃度等。

```bash
# 显示所有 Cookie（默认包含无效的）
dlm-cli cookies show

# 仅显示无效 Cookie
dlm-cli cookies show --show-invalid

# 以 JSON 格式输出
dlm-cli cookies show --json
```

参数说明：

| 参数 | 缩写 | 类型 | 说明 |
|------|------|------|------|
| `--show-invalid` | | flag | 显示已失效的 Cookie（默认全部显示） |
| `--json` | `-j` | flag | 以 JSON 格式输出 |

示例输出（表格格式）：

```
┌────────────────────────────── 📋 Cookie 列表 ──────────────────────────────┐
│ ID │ 用户名       │ LinkedIn 名称      │ 状态(模型) │ 活跃    │ 创建日期    │
├────┼──────────────┼────────────────────┼────────────┼─────────┼─────────────┤
│ 1  │ chandler     │ Chandler Liu       │ ✅ 有效    │ ✅      │ 2025-06-15  │
│ 2  │ alice        │ Alice Wang         │ ❌ 无效    │ ✅      │ 2025-07-20  │
│ 3  │ bob          │ Bob Zhang          │ ✅ 有效    │ ✅      │ 2025-08-01  │
│ ...│ ...          │ ...                │ ...        │ ...     │ ...         │
└────────────────────────────────────────────────────────────────────────────┘
总计: 153 条 Cookie 记录
```

### `dlm-cli cookies test` — 测试 Cookie 有效性

逐一测试数据库中 Cookie 的有效性，通过网络请求验证 LinkedIn 账号状态。

```bash
# 测试所有 Cookie
dlm-cli cookies test

# 测试指定 ID 的 Cookie
dlm-cli cookies test --cookie-ids 1,2,3

# 调整并发数
dlm-cli cookies test --concurrency 20
```

参数说明：

| 参数 | 缩写 | 类型 | 默认值 | 说明 |
|------|------|------|--------|------|
| `--cookie-ids` | `-i` | TEXT | 全部 | 指定要测试的 Cookie ID，逗号分隔 |
| `--concurrency` | `-c` | INT | 10 | 并发测试线程数（1~50） |

示例输出：

```
🔍 开始测试 153 个 Cookie 的有效性...

测试 Cookie... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 153/153

┌──────────────────────────── 📊 Cookie 测试结果 ────────────────────────────┐
│ ID │ 用户名       │ LinkedIn 名称      │ 状态    │ 错误信息                    │
├────┼──────────────┼────────────────────┼─────────┼─────────────────────────────┤
│ 1  │ chandler     │ Chandler Liu       │ ✅ 有效  │                             │
│ 2  │ alice        │ Alice Wang         │ ❌ 无效  │ 接口返回状态码: 401         │
│ 3  │ bob          │ Bob Zhang          │ ✅ 有效  │                             │
└────────────────────────────────────────────────────────────────────────────┘
┌───────────────────────────── 📈 测试汇总 ─────────────────────────────┐
│  ✅ 有效: 139    ❌ 无效: 14    总计: 153                            │
└───────────────────────────────────────────────────────────────────────┘
```

### `dlm-cli cookies update` — 更新 Redis 缓存

将验证通过的 Cookie 安全地存储到 Redis 缓存中，供搜索任务使用。

```bash
# 先测试再缓存（默认行为）
dlm-cli cookies update

# 跳过测试，直接缓存所有 Cookie
dlm-cli cookies update --skip-test

# 指定要缓存的 Cookie
dlm-cli cookies update --cookie-ids 1,3,5

# 调整并发测试数
dlm-cli cookies update --concurrency 20
```

参数说明：

| 参数 | 缩写 | 类型 | 默认值 | 说明 |
|------|------|------|--------|------|
| `--cookie-ids` | `-i` | TEXT | 全部有效 | 指定要缓存的 Cookie ID，逗号分隔 |
| `--skip-test` | | flag | False | 跳过有效性测试，直接缓存 |
| `--concurrency` | `-c` | INT | 10 | 并发测试线程数（1~50） |

更新完成后，Redis 中会存储两个 Key：

| Redis Key | 类型 | 说明 |
|-----------|------|------|
| `linkedin_cookies` | List | 有效 Cookie ID 列表（按序存储） |
| `COOKIES_LIST_SET` | Set | 有效 Cookie ID 集合 |

示例输出：

```
┌─────────── 📦 缓存更新结果 ───────────┐
│                                       │
│  ✅ 缓存更新完成                      │
│                                       │
│  • 有效 Cookie 数: 139                │
│  • Redis Key (list): linkedin_cookies │
│  • Redis Key (set):  COOKIES_LIST_SET │
└───────────────────────────────────────┘
```

### `dlm-cli search` — 执行 LinkedIn 搜索

执行完整的 LinkedIn 搜索任务，遍历姓氏列表搜索候选人。

```bash
# 基本用法（必须指定关键词）
dlm-cli search --keyword "Nvidia"

# 完整参数
dlm-cli search \
    --keyword "Software Engineer" \
    --location 103644278 \
    --job-title "Engineer OR Scientist" \
    --company "Nvidia" \
    --university "Stanford" \
    --family-name "wang" \
    --network F \
    --user-id 2 \
    --max-results 1000
```

参数说明：

| 参数 | 缩写 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|------|--------|------|
| `--keyword` | `-k` | TEXT | **是** | - | 搜索关键词，如公司名、职位名 |
| `--location` | `-l` | TEXT | 否 | 空 | 地理位置编码，如 `103644278`（美国） |
| `--job-title` | `-t` | TEXT | 否 | 空 | 职位关键词，支持 `OR` 逻辑 |
| `--company` | `-c` | TEXT | 否 | 空 | 公司名称 |
| `--university` | `-u` | TEXT | 否 | 空 | 学校名称 |
| `--family-name` | `-f` | TEXT | 否 | 空 | 指定搜索的姓氏，留空则遍历全部 |
| `--network` | `-n` | TEXT | 否 | 空 | 人脉范围：`F`=好友，`O`=非好友，留空=所有人 |
| `--user-id` | | INT | 否 | 2 | 用户 ID，用于关联搜索数据 |
| `--max-results` | `-m` | INT | 否 | 1000 | 每个姓氏最多获取的结果数 |

> **注意**：`--network` 参数仅接受 `F`（好友）、`O`（非好友）或留空（所有人）。

搜索流程：

1. 初始化 Django 和 Redis 连接
2. 将姓氏列表写入 Redis（如果尚未初始化，可先运行 `dlm-cli init-names`）
3. 从 Redis 读取可用 Cookie 列表
4. 格式化搜索参数
5. 遍历姓氏，对每个姓氏执行搜索
6. 创建 `SearwordChineseTask` 任务记录
7. 搜索进度自动保存，支持断点续搜

示例输出：

```
┌────────────────────────── 📋 搜索参数 ──────────────────────────┐
│  🔍 LinkedIn 搜索任务                                           │
│                                                                 │
│  • 关键词:    Nvidia                                             │
│  • 地点:      103644278                                         │
│  • 职位:      (空)                                              │
│  • 公司:      (空)                                              │
│  • 大学:      (空)                                              │
│  • 姓氏:      (空)                                              │
│  • 人脉:      (空)                                              │
└─────────────────────────────────────────────────────────────────┘

✅ 已加载 268 个姓氏到 Redis
✅ 找到 139 个可用 Cookie

执行搜索... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 268/268

  ✅ 姓氏 'wang': 页面 5, 总数 45 (1/268)
  ✅ 姓氏 'li': 页面 8, 总数 72 (2/268)
  ✅ 姓氏 'zhang': 页面 3, 总数 28 (3/268)
  ...

┌─────────────────────────── 📊 搜索结果 ──────────────────────────┐
│  ✅ 搜索任务完成                                                 │
│                                                                  │
│  • 搜索词:     keyword:Nvidia_CN_NAME                            │
│  • 待执行任务: 356                                               │
│  • 已完成任务: 0                                                 │
│  • 姓氏范围:   268 个                                            │
└──────────────────────────────────────────────────────────────────┘
```

### `dlm-cli init-names` — 初始化姓氏列表

将 `django_settings.py` 中的 `FAMILY_NAMES` 姓氏列表写入 Redis，供搜索任务遍历使用。

```bash
dlm-cli init-names
```

示例输出：

```
┌────────────── 📝 初始化完成 ──────────────┐
│                                           │
│  ✅ 家族姓氏已成功写入 Redis              │
│                                           │
│  • Redis Key: name                        │
│  • 姓氏数量:  268 个                      │
│  • 范围:      wang ~ zeng                │
└───────────────────────────────────────────┘
```

## 快捷键（别名）

`dlm-cli` 提供了两个入口点命令：

```bash
# 完整命令
dlm-cli [command] [options]

# 简短别名
dlm [command] [options]
```

两个命令完全等价，可以根据习惯选择使用。

```bash
dlm info
dlm cookies show
dlm search --keyword "Google"
```

## 故障排除

### 1. 找不到项目配置文件

**错误信息**：`❌ 无法找到项目配置文件`

**解决方案**：

```bash
# 在项目根目录下执行
cd D:\LLMCode\2026\20260517

# 或指定项目路径
dlm-cli --project-dir D:\LLMCode\2026\20260517 info

# 或设置环境变量
set DLM_CLI_PROJECT_DIR=D:\LLMCode\2026\20260517
```

### 2. Django 导入失败

**错误信息**：`❌ Django 导入失败: No module named django`

**解决方案**：

```bash
# 确认已激活正确的 Python 环境
python --version

# 安装依赖
pip install -r requirements.txt
```

### 3. Redis 连接失败

**错误信息**：`❌ Redis 连接失败`

**解决方案**：

- 确认 Redis 服务是否正常运行
- 检查 `django_settings.py` 中的 `REDIS_HOST` 和 `REDIS_PASSWORD` 配置是否正确
- 确认网络可以访问 Redis 服务器（如果是远程 Redis）

### 4. MySQL 连接失败

**错误信息**：`❌ django.db.utils.OperationalError: (2003, "Can't connect to MySQL server")`

**解决方案**：

- 确认 MySQL 服务是否正常运行
- 检查 `django_settings.py` 中的数据库配置
- 确认网络可以访问 MySQL 服务器
- 确保 `pymysql` 已安装：`pip install pymysql`

### 5. Cookie 测试全部失败

**错误信息**：`❌ 无效: 153` 等全部失败结果

**解决方案**：

- 检查 LinkedIn 账号是否过期（LI_AT 和 JSESSIONID 通常在 1 年后过期）
- 确认 Redis 中存在 Cookie 数据
- 尝试重新登录 LinkedIn 并更新 Cookie

### 6. 搜索无结果

**问题描述**：搜索完成后待执行任务数为 0

**可能原因**：

- Cookie 已失效，无法通过 LinkedIn API 获取数据
- 搜索条件过于严格，无匹配用户
- LinkedIn API 返回空结果

### 7. conda 环境下 pip SSL 错误

**错误信息**：`Could not fetch URL https://pypi.org/simple/... There was a problem confirming the ssl certificate`

**解决方案**：

```bash
# 优先使用 conda 安装
conda install -c conda-forge <package_name>

# 或使用 --trusted-host 参数
pip install <package_name> --trusted-host pypi.org --trusted-host files.pythonhosted.org
```

### 8. 模块未找到

**错误信息**：`ModuleNotFoundError: No module named 'dlm_cli'`

**解决方案**：

```bash
# 确认已安装 dlm-li-cat
pip list | findstr dlm-li-cat  # Windows
# 或
pip list | grep dlm-cli     # Linux/macOS

# 如果未安装，执行
pip install --no-build-isolation -e dlm-cli
```

### 9. "No pyvenv.cfg file" 错误

**错误信息**：`Fatal error in launcher: Unable to create process using '"..."' No pyvenv.cfg file`

**解决方案**：

- 该错误通常由损坏的虚拟环境引起
- 删除项目目录下的 `venv/` 目录（如果存在）
- 确保使用正确的 Python 环境执行命令

## 注意事项

### 数据库连接

- 本项目使用远程 MySQL 数据库，请确保网络连接稳定
- 候选人表 `CandidateList` 已有超过 134 万条记录，查询和写入时注意性能
- 不要在高峰时段执行大规模搜索任务，避免对数据库造成压力

### Redis 连接

- Redis 用于存储 Cookie 缓存和搜索进度状态
- 搜索过程中会在 Redis 中记录当前进度，支持断点续搜
- 如果 Redis 连接中断，搜索任务将无法继续
- 搜索完成后 Redis 中的进度数据会被保留，如需重新搜索需手动清除

### Cookie 管理

- LinkedIn Cookie（LI_AT 和 JSESSIONID）通常有效期为 1 年左右
- 建议定期使用 `dlm-cli cookies test` 检查 Cookie 有效性
- 使用 `dlm-cli cookies update` 将有效 Cookie 同步到 Redis
- 搜索任务会自动从 Redis 随机选取 Cookie，避免单个账号请求过于频繁

### 搜索任务

- 搜索会遍历 268 个姓氏，每个姓氏都会发起一次请求
- 请求间隔固定为 2 秒，避免触发 LinkedIn 反爬机制
- 搜索结果以 `SearwordChineseTask` 记录保存，状态包括"待执行"和"已完成"
- 如果搜索词已存在且进度为 100%，工具会自动跳过，避免重复搜索
- 支持断点续搜：如果搜索中断，重新执行会从上次位置继续

### conda 环境

- 推荐在 conda 环境中运行本项目，便于管理依赖
- 安装依赖时，优先使用 `conda install -c conda-forge`，pip 仅作为备用
- 使用 `pip install` 时若遇 SSL 错误，可使用 `--no-build-isolation` 参数

### 安全

- `django_settings.py` 中的数据库和 Redis 密码已改为从环境变量读取（`DLM_DB_PASSWORD`、`DLM_REDIS_PASSWORD`），默认值为占位符
- **敏感配置不会被打包到 dlm-cli 分发包中**，仅本地 `django_settings.py` 包含默认值
- 不要将修改后的 `.env` 文件提交到版本控制系统（已在 `.gitignore` 中忽略）
- LinkedIn Cookie 涉及账号安全，请勿泄露
- 提交代码前请检查 `django_settings.py` 是否仍包含真实密码，如有则替换为环境变量读取方式

## 项目结构

```
dlm-cli/
├── pyproject.toml                # 项目构建配置
├── README.md                     # 本文档
└── src/
    └── dlm_cli/
        ├── __init__.py           # 包初始化，版本号
        ├── core.py               # Django/Redis 初始化、项目发现
        ├── main.py               # Typer CLI 入口、子命令注册
        ├── cookie.py             # Cookie 管理（展示、测试、缓存）
        ├── search.py             # LinkedIn 搜索功能
        └── linkedin/             # LinkedIn API 核心模块（内嵌子包）
            ├── __init__.py
            ├── core.py           # LinkedIn Voyager API 封装
            ├── location_list.py  # 地理位置编码列表
            └── parse_resume.py   # 简历解析工具
```

## 开发

### 本地开发安装

```bash
# 以可编辑模式安装，修改源码后无需重新安装
pip install --no-build-isolation -e dlm-cli
```

### 构建分发包

```bash
# 安装构建工具
pip install build

# 构建源码包和 wheel 包
python -m build dlm-cli

# 构建产物在 dlm-cli/dist/ 目录下
```
