13 KiB
暗网采集服务部署笔记
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://<IP>: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):
{
"code": 200,
"msg": "success",
"mode": "静态",
"data": {
"status_code": 200,
"url": "http://xxx.onion/",
"content": "<html>...页面源码...</html>"
}
}
失败:
| HTTP 状态码 | code |
msg 示例 |
|---|---|---|
| 400 | 400 | 请发送 JSON 格式 / url 不能为空 |
| 502 | 502 | 代理连接失败,请检查服务状态 |
| 504 | 504 | 静态请求超时 / 动态请求超时 |
| 500 | 500 | 系统异常: ... |
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并转发到 Tor9050;改完后还要处理文件权限。
1. 配置 Tor(/etc/tor/torrc)
sudo vim /etc/tor/torrc
确认存在以下行(没有就加上,有重复端口配置以这条为准):
SOCKSPort 127.0.0.1:9050
sudo systemctl restart tor
sudo netstat -tlnp | grep 9050 # 应看到 tor 监听 9050
2. 配置 Privoxy(/etc/privoxy/config)
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 会启动失败):
sudo chown -R privoxy:privoxy /etc/privoxy/
⑤ 若 SELinux 拦截 19095 端口(启动失败或端口未监听时):
sudo setenforce 0
sudo systemctl restart privoxy
sudo netstat -tlnp | grep 19095 # 应看到 privoxy 监听 19095
3. 确认 API 代码端口一致
darkweb_api_3.py 中应与上面配置对应(一般默认已正确,改过 Privoxy 端口时需同步修改):
STATIC_PROXY_PORT = "19095" # 对应 Privoxy listen-address
DYNAMIC_PROXY_SERVER = "socks5://127.0.0.1:9050" # 对应 Tor SOCKSPort
启动服务
# 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 &
验证命令
# 服务与端口
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
一键体检
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
启停与排障
# 采集超时?先重启 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 <PID>
cd /opt/crawl/darkweb_api && nohup python3 darkweb_api_3.py > ./logs/api_stdout.log 2>&1 &
外部调用
# 其他机器通过公网 IP 调 API(推荐)
curl -X POST -H "Content-Type: application/json" \
-d '{"url": "http://<onion-url>/"}' \
http://124.243.188.109:8000/crawl
# 同 VPC 内网
curl -X POST -H "Content-Type: application/json" \
-d '{"url": "http://<onion-url>/"}' \
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:
sudo yum install epel-release -y
sudo yum install tor -y
编辑 /etc/tor/torrc,确认 SOCKS 监听端口:
SOCKSPort 127.0.0.1:9050
sudo systemctl start tor
sudo systemctl enable tor
sudo systemctl restart tor
sudo netstat -tlnp | grep 9050 # 确认 9050 已监听
三、安装与配置 Privoxy
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 .
改完权限并启动:
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 用户运行时会读不到配置:
sudo chown -R privoxy:privoxy /etc/privoxy/
ls -l /etc/privoxy/config # 确认属主为 privoxy
SELinux 拦截自定义端口
CentOS 默认只允许 Privoxy 监听 8118,改用 19095 可能被拦截:
sudo setenforce 0 # 临时关闭,验证后可按需配置 SELinux 策略
Tor 进程在跑但采集超时
日志出现 0 circuits open 或 Privoxy 请求 127.0.0.1:19095 超时:
sudo systemctl restart tor
journalctl -u tor -f # 观察是否出现 Bootstrapped 100%
若重启后仍卡在 Bootstrap 5%,检查系统时间(timedatectl)和华为云安全组出站规则;仍不通再考虑配置 Tor Bridge。
五、API 服务部署
接口作用
/crawl 是暗网采集服务的唯一入口。客户端传入目标 URL,服务端代为完成:
- 静态模式(默认):通过 Privoxy → Tor 发 HTTP 请求,返回页面 HTML。
- 动态模式(
is_dynamic: true):启动无头浏览器经 Tor 访问,支持 JS 渲染、自动滚动,返回渲染后的 HTML。
适用于:暗网页面采集、搜索结果显示、需登录 Cookie / 自定义请求头的场景。部分站点在代码内置了采集策略,会自动切换动态模式。
请求格式
POST /crawl
Host: 124.243.188.109:8000
Content-Type: application/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
}
响应格式
成功:
{
"code": 200,
"msg": "success",
"mode": "动态",
"data": {
"status_code": 200,
"url": "http://xxx.onion/search?q=a",
"content": "<!DOCTYPE html>..."
}
}
| 字段 | 说明 |
|---|---|
code |
业务状态码,200 表示成功 |
msg |
success 或错误描述 |
mode |
实际使用的采集模式:静态 / 动态 |
data.status_code |
目标站点 HTTP 状态码 |
data.url |
请求的原始 URL |
data.content |
页面 HTML 源码(主要取用字段) |
失败:
{"code": 504, "msg": "静态请求超时"}
代码目录
cd /opt/crawl/darkweb_api
ls darkweb_api_3.py
代码内代理配置(一般无需修改):
- 静态代理:
127.0.0.1:19095(Privoxy) - 动态代理:
socks5://127.0.0.1:9050(Tor)
启动方式
前台运行(调试用):
cd /opt/crawl/darkweb_api
python3 darkweb_api_3.py
后台运行(生产常用):
cd /opt/crawl/darkweb_api
nohup python3 darkweb_api_3.py > ./logs/api_stdout.log 2>&1 &
调用示例
curl:
# 静态采集
curl -X POST -H "Content-Type: application/json" \
-d '{"url": "http://<onion-url>/"}' \
http://127.0.0.1:8000/crawl
# 动态采集
curl -X POST -H "Content-Type: application/json" \
-d '{"url": "http://<onion-url>/", "is_dynamic": true}' \
http://127.0.0.1:8000/crawl
Python:
import requests
api_url = "http://124.243.188.109:8000/crawl"
payload = {
"url": "http://<onion-url>/",
"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"))
其他服务器(公网):
curl -X POST -H "Content-Type: application/json" \
-d '{"url": "http://<onion-url>/"}' \
http://124.243.188.109:8000/crawl
六、运维速查
# 服务状态
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