# 暗网采集服务部署笔记 > CentOS + Tor + Privoxy + Flask(python3 直接启动) ## 环境信息 | 项目 | 值 | |------|-----| | 云平台 | 华为云 | | 部署主机 | `ocai-node-05` | | 公网 IP | `124.243.188.109` | | 内网 IP | `192.168.0.131`(同 VPC 内访问) | | 代码目录 | `/opt/crawl/darkweb_api` | | 入口文件 | `darkweb_api_3.py` | | 服务 | 端口 | 说明 | |------|------|------| | Tor (SOCKS5) | `9050` | 暗网隧道;动态采集直连此端口 | | Privoxy (HTTP) | `19095` | 静态采集走此代理 | | Flask API | `8000` | 采集接口 `/crawl` | **端口对应关系(三处必须一致):** | 组件 | 配置文件 | 关键项 | 值 | |------|----------|--------|-----| | Tor | `/etc/tor/torrc` | `SOCKSPort` | `127.0.0.1:9050` | | Privoxy | `/etc/privoxy/config` | `listen-address` | `127.0.0.1:19095` | | Privoxy | `/etc/privoxy/config` | `forward-socks5t` | `/ 127.0.0.1:9050 .` | | API 代码 | `darkweb_api_3.py` | 静态代理 / 动态代理 | `19095` / `9050` | ## API 接口说明 **作用**:接收一个目标 URL(明网或 `.onion` 暗网),由服务器经 Tor 代理抓取页面内容,以 JSON 返回 HTML 源码。调用方无需在本机配置 Tor / 代理。 | 项目 | 说明 | |------|------| | 地址 | `POST http://:8000/crawl` | | Content-Type | `application/json` | | 采集模式 | **静态**(默认):`requests` + Privoxy;**动态**:Playwright + Tor,适合搜索页 / JS 渲染页 | **输入(JSON Body):** | 参数 | 必填 | 默认值 | 说明 | |------|------|--------|------| | `url` | 是 | — | 要采集的目标地址 | | `is_dynamic` | 否 | `false` | `true` 启用动态采集(Playwright) | | `headers` | 否 | `{}` | 自定义请求头 | | `cookies` | 否 | `{}` | 自定义 Cookie | | `referer` | 否 | — | Referer 头 | | `static_timeout_s` | 否 | `60` | 静态采集超时(秒) | | `dynamic_timeout_ms` | 否 | `90000` | 动态页面加载超时(毫秒) | | `wait_selector` | 否 | — | 动态模式下等待的 CSS 选择器 | | `wait_selector_timeout_ms` | 否 | `30000` | 选择器等待超时(毫秒) | **输出(JSON):** 成功(HTTP 200): ```json { "code": 200, "msg": "success", "mode": "静态", "data": { "status_code": 200, "url": "http://xxx.onion/", "content": "...页面源码..." } } ``` 失败: | HTTP 状态码 | `code` | `msg` 示例 | |-------------|--------|------------| | 400 | 400 | `请发送 JSON 格式` / `url 不能为空` | | 502 | 502 | `代理连接失败,请检查服务状态` | | 504 | 504 | `静态请求超时` / `动态请求超时` | | 500 | 500 | `系统异常: ...` | **Python 调用示例:** ```python import requests resp = requests.post( "http://124.243.188.109:8000/crawl", json={"url": "http://xxx.onion/", "is_dynamic": True}, timeout=300, ) data = resp.json() html = data["data"]["content"] # msg == "success" 时取用 ``` --- # 快速上手(最常用) ## 关键配置(首次部署必做) > Privoxy 默认监听 `8118`,必须改成 `19095` 并转发到 Tor `9050`;改完后还要处理文件权限。 ### 1. 配置 Tor(`/etc/tor/torrc`) ```bash sudo vim /etc/tor/torrc ``` 确认存在以下行(没有就加上,有重复端口配置以这条为准): ``` SOCKSPort 127.0.0.1:9050 ``` ```bash sudo systemctl restart tor sudo netstat -tlnp | grep 9050 # 应看到 tor 监听 9050 ``` ### 2. 配置 Privoxy(`/etc/privoxy/config`) ```bash sudo vim /etc/privoxy/config ``` **① 注释掉默认监听端口**(搜索 `listen-address`,把 `8118` 那行注释掉): ``` # listen-address 127.0.0.1:8118 # listen-address [::1]:8118 ``` **② 新增监听端口**(HTTP 代理,供 API 静态采集使用): ``` listen-address 127.0.0.1:19095 ``` **③ 新增转发规则**(把 HTTP 代理流量转给 Tor SOCKS5,末尾 `.` 不能漏): ``` forward-socks5t / 127.0.0.1:9050 . ``` **④ 修复权限**(`sudo vim` 改完后面属主会变成 root,Privoxy 会启动失败): ```bash sudo chown -R privoxy:privoxy /etc/privoxy/ ``` **⑤ 若 SELinux 拦截 19095 端口**(启动失败或端口未监听时): ```bash sudo setenforce 0 ``` ```bash sudo systemctl restart privoxy sudo netstat -tlnp | grep 19095 # 应看到 privoxy 监听 19095 ``` ### 3. 确认 API 代码端口一致 `darkweb_api_3.py` 中应与上面配置对应(一般默认已正确,改过 Privoxy 端口时需同步修改): ```python STATIC_PROXY_PORT = "19095" # 对应 Privoxy listen-address DYNAMIC_PROXY_SERVER = "socks5://127.0.0.1:9050" # 对应 Tor SOCKSPort ``` --- ## 启动服务 ```bash # 1. 启动基础代理(一般已装好,重启即可) sudo systemctl start tor sudo systemctl start privoxy # 2. 启动 API cd /opt/crawl/darkweb_api nohup python3 darkweb_api_3.py > ./logs/api_stdout.log 2>&1 & ``` ## 验证命令 ```bash # 服务与端口 sudo systemctl status tor privoxy ps -ef | grep darkweb_api_3 sudo netstat -tlnp | grep -E '9050|19095|8000' # Tor 直连 curl --socks5-hostname 127.0.0.1:9050 --max-time 60 http://httpbin.org/ip # Privoxy 转发 curl -x http://127.0.0.1:19095 --max-time 60 http://httpbin.org/ip # 暗网连通 curl -x http://127.0.0.1:19095 -L --max-time 120 \ "http://darkzqtmbdeauwq5mzcmgeeuhet42fhfjj4p5wbak3ofx2yqgecoeqyd.onion/" # API 静态采集 curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http://darkzqtmbdeauwq5mzcmgeeuhet42fhfjj4p5wbak3ofx2yqgecoeqyd.onion/"}' \ http://127.0.0.1:8000/crawl # API 动态采集 curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http://darkzqtmbdeauwq5mzcmgeeuhet42fhfjj4p5wbak3ofx2yqgecoeqyd.onion/search?q=a", "is_dynamic": true}' \ http://127.0.0.1:8000/crawl ``` ## 一键体检 ```bash echo "=== Tor ===" && curl -s --socks5-hostname 127.0.0.1:9050 --max-time 30 http://httpbin.org/ip && \ echo -e "\n=== Privoxy ===" && curl -s -x http://127.0.0.1:19095 --max-time 30 http://httpbin.org/ip && \ echo -e "\n=== API ===" && curl -s -X POST -H "Content-Type: application/json" \ -d '{"url":"http://httpbin.org/ip"}' http://127.0.0.1:8000/crawl | head -c 200 ``` ## 启停与排障 ```bash # 采集超时?先重启 Tor(最常见修复方式) sudo systemctl restart tor sudo systemctl restart privoxy # 查看 Tor 是否正常建电路 journalctl -u tor -n 20 --no-pager | grep -iE 'bootstrap|circuit' # API 进程管理 ps -ef | grep darkweb_api_3 kill cd /opt/crawl/darkweb_api && nohup python3 darkweb_api_3.py > ./logs/api_stdout.log 2>&1 & ``` ## 外部调用 ```bash # 其他机器通过公网 IP 调 API(推荐) curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http:///"}' \ http://124.243.188.109:8000/crawl # 同 VPC 内网 curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http:///"}' \ http://192.168.0.131:8000/crawl ``` --- # 详细部署说明 ## 一、核心架构 ``` 客户端 (Python / Curl / 其他服务器) ↓ HTTP 请求 Flask API (:8000) ──静态采集──→ Privoxy (:19095) │ ↓ SOCKS5 └──动态采集 (Playwright) ──────────→ Tor (:9050) ↓ 加密隧道 暗网 (.onion) ``` - **Tor**:建立暗网加密隧道,提供 SOCKS5 接口(`9050`)。 - **Privoxy**:将 HTTP 代理请求翻译为 SOCKS5 后转发给 Tor。 - **Flask API**:对外提供 `/crawl` 接口;静态走 Privoxy,动态由 Playwright 直连 Tor。当前以 `python3` 直接启动。 --- ## 二、安装与配置 Tor Tor 不在 CentOS 默认源中,需先添加 EPEL: ```bash sudo yum install epel-release -y sudo yum install tor -y ``` 编辑 `/etc/tor/torrc`,确认 SOCKS 监听端口: ``` SOCKSPort 127.0.0.1:9050 ``` ```bash sudo systemctl start tor sudo systemctl enable tor sudo systemctl restart tor sudo netstat -tlnp | grep 9050 # 确认 9050 已监听 ``` --- ## 三、安装与配置 Privoxy ```bash sudo yum install privoxy -y ``` 编辑 `/etc/privoxy/config`,**三项必改**: | 步骤 | 操作 | 说明 | |------|------|------| | ① | 注释默认 `listen-address ... 8118` | 避免仍监听旧端口 | | ② | 新增 `listen-address 127.0.0.1:19095` | HTTP 代理监听端口 | | ③ | 新增 `forward-socks5t / 127.0.0.1:9050 .` | 转发到 Tor,末尾 `.` 不能漏 | 完整示例: ``` # listen-address 127.0.0.1:8118 # listen-address [::1]:8118 listen-address 127.0.0.1:19095 forward-socks5t / 127.0.0.1:9050 . ``` 改完权限并启动: ```bash sudo chown -R privoxy:privoxy /etc/privoxy/ sudo systemctl start privoxy sudo systemctl enable privoxy sudo systemctl restart privoxy sudo systemctl status privoxy sudo netstat -tlnp | grep 19095 ``` --- ## 四、常见问题 ### 权限错误(Permission Denied) `sudo vim` 改配置后文件属主变为 `root`,Privoxy 以 `privoxy` 用户运行时会读不到配置: ```bash sudo chown -R privoxy:privoxy /etc/privoxy/ ls -l /etc/privoxy/config # 确认属主为 privoxy ``` ### SELinux 拦截自定义端口 CentOS 默认只允许 Privoxy 监听 `8118`,改用 `19095` 可能被拦截: ```bash sudo setenforce 0 # 临时关闭,验证后可按需配置 SELinux 策略 ``` ### Tor 进程在跑但采集超时 日志出现 `0 circuits open` 或 Privoxy 请求 `127.0.0.1:19095` 超时: ```bash sudo systemctl restart tor journalctl -u tor -f # 观察是否出现 Bootstrapped 100% ``` 若重启后仍卡在 Bootstrap 5%,检查系统时间(`timedatectl`)和华为云安全组出站规则;仍不通再考虑配置 Tor Bridge。 --- ## 五、API 服务部署 ### 接口作用 `/crawl` 是暗网采集服务的唯一入口。客户端传入目标 URL,服务端代为完成: 1. **静态模式**(默认):通过 Privoxy → Tor 发 HTTP 请求,返回页面 HTML。 2. **动态模式**(`is_dynamic: true`):启动无头浏览器经 Tor 访问,支持 JS 渲染、自动滚动,返回渲染后的 HTML。 适用于:暗网页面采集、搜索结果显示、需登录 Cookie / 自定义请求头的场景。部分站点在代码内置了采集策略,会自动切换动态模式。 ### 请求格式 ``` POST /crawl Host: 124.243.188.109:8000 Content-Type: application/json ``` ```json { "url": "http://darkzqtmbdeauwq5mzcmgeeuhet42fhfjj4p5wbak3ofx2yqgecoeqyd.onion/search?q=a", "is_dynamic": true, "headers": {"User-Agent": "Mozilla/5.0 ..."}, "cookies": {"session": "xxx"}, "referer": "http://xxx.onion/", "static_timeout_s": 120, "dynamic_timeout_ms": 90000, "wait_selector": "body", "wait_selector_timeout_ms": 30000 } ``` ### 响应格式 **成功:** ```json { "code": 200, "msg": "success", "mode": "动态", "data": { "status_code": 200, "url": "http://xxx.onion/search?q=a", "content": "..." } } ``` | 字段 | 说明 | |------|------| | `code` | 业务状态码,200 表示成功 | | `msg` | `success` 或错误描述 | | `mode` | 实际使用的采集模式:`静态` / `动态` | | `data.status_code` | 目标站点 HTTP 状态码 | | `data.url` | 请求的原始 URL | | `data.content` | 页面 HTML 源码(主要取用字段) | **失败:** ```json {"code": 504, "msg": "静态请求超时"} ``` ### 代码目录 ```bash cd /opt/crawl/darkweb_api ls darkweb_api_3.py ``` 代码内代理配置(一般无需修改): - 静态代理:`127.0.0.1:19095`(Privoxy) - 动态代理:`socks5://127.0.0.1:9050`(Tor) ### 启动方式 前台运行(调试用): ```bash cd /opt/crawl/darkweb_api python3 darkweb_api_3.py ``` 后台运行(生产常用): ```bash cd /opt/crawl/darkweb_api nohup python3 darkweb_api_3.py > ./logs/api_stdout.log 2>&1 & ``` ### 调用示例 **curl:** ```bash # 静态采集 curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http:///"}' \ http://127.0.0.1:8000/crawl # 动态采集 curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http:///", "is_dynamic": true}' \ http://127.0.0.1:8000/crawl ``` **Python:** ```python import requests api_url = "http://124.243.188.109:8000/crawl" payload = { "url": "http:///", "is_dynamic": True, } resp = requests.post(api_url, json=payload, timeout=300) result = resp.json() if result.get("msg") == "success": html = result["data"]["content"] else: print("失败:", result.get("msg")) ``` **其他服务器(公网):** ```bash curl -X POST -H "Content-Type: application/json" \ -d '{"url": "http:///"}' \ http://124.243.188.109:8000/crawl ``` --- ## 六、运维速查 ```bash # 服务状态 sudo systemctl status tor privoxy ps -ef | grep darkweb_api_3 # 端口占用 sudo netstat -tlnp | grep -E '9050|19095|8000' # 日志 tail -f /opt/crawl/darkweb_api/logs/api_stdout.log tail -f /opt/crawl/darkweb_api/logs/*.log sudo journalctl -u tor -f sudo journalctl -u privoxy -f ```