学习目标
跟随架构师律师马原的脚步读完这一讲,你能:
- 逐项检查爬虫项目的合规状态
- 在代码层面实现自动化合规检查
- 建立团队的合规文化
技术原理
Git Pre-commit Hook 合规检查
#!/usr/bin/env python3
"""
.crawler-lint.py
放在 .git/hooks/pre-commit 中,提交前自动检查爬虫代码
"""
import re
import sys
from pathlib import Path
CRAWLER_LINT_RULES = {
'user_agent_faking': {
'pattern': r'User-Agent.*Mozilla',
'severity': 'error',
'message': '禁止使用伪造的浏览器 User-Agent',
'fix': '使用诚实身份声明,格式: YourBot/1.0 (+url; email)',
},
'hardcoded_secret': {
'pattern': r'(api[_-]?key|secret|password)\s*=\s*['"]\w+',
'severity': 'warning',
'message': '发现硬编码的密钥/密码',
'fix': '使用环境变量或密钥管理服务',
},
'no_rate_limit': {
'pattern': r'requests.get(.*)(?!.*time.sleep)',
'severity': 'warning',
'message': '请求后没有延迟,可能违反频率限制',
'fix': '添加 time.sleep() 或实现自适应限速',
},
'sensitive_data_logging': {
'pattern': r'(password|token|secret).*(print|log|write)',
'severity': 'error',
'message': '敏感信息可能被记录到日志',
'fix': '日志中脱敏处理敏感信息',
},
}
def lint_file(filepath):
"""检查单个文件"""
issues = []
content = Path(filepath).read_text()
for rule_name, rule in CRAWLER_LINT_RULES.items():
matches = re.finditer(rule['pattern'], content, re.IGNORECASE)
for match in matches:
# 检查是否是注释行
line_start = content.rfind('\n', 0, match.start()) + 1
line = content[line_start:match.start()].strip()
if line.startswith('#') or line.startswith('//'):
continue
issues.append({
'rule': rule_name,
'severity': rule['severity'],
'message': rule['message'],
'fix': rule['fix'],
'line': content[:match.start()].count('\n') + 1,
})
return issues
def main():
# 检查暂存区中的 Python 文件
import subprocess
result = subprocess.run(
['git', 'diff', '--cached', '--name-only'],
capture_output=True, text=True
)
files = [f.strip() for f in result.stdout.split('\n') if f.strip().endswith('.py')]
all_issues = []
for filepath in files:
issues = lint_file(filepath)
all_issues.extend(issues)
if all_issues:
print(f"\n发现 {len(all_issues)} 个爬虫合规问题:\n")
errors = [i for i in all_issues if i['severity'] == 'error']
warnings = [i for i in all_issues if i['severity'] == 'warning']
for issue in errors:
print(f"❌ ERROR: {issue['message']}")
print(f" 修复建议: {issue['fix']}\n")
for issue in warnings:
print(f"⚠ WARNING: {issue['message']}")
print(f" 修复建议: {issue['fix']}\n")
if errors:
print("提交被拒绝:存在 error 级别的问题")
sys.exit(1)
else:
print("✅ 爬虫合规检查通过")
sys.exit(0)
if __name__ == '__main__':
main()
完整合规清单
# 爬虫合规检查清单(32项)
## 一、爬取前检查(8项)
- [ ] 1. 已读取目标站点的 robots.txt
- [ ] 2. 已检查 robots.txt 中的 Disallow 规则
- [ ] 3. 已阅读目标站点的用户协议
- [ ] 4. 用户协议中没有明确禁止爬虫的条款(或已获授权)
- [ ] 5. 目标数据不需要登录,或登录账号来源合法
- [ ] 6. 目标数据中不包含个人信息
- [ ] 7. 目标数据不受著作权保护(或已获得授权)
- [ ] 8. 优先使用官方 API 而非直接爬取页面
## 二、爬取中控制(8项)
- [ ] 9. User-Agent 使用诚实身份声明(含联系信息)
- [ ] 10. 不伪造 User-Agent、device_id、IP 地址
- [ ] 11. 请求频率控制在 1 req/sec 以下
- [ ] 12. 遵守目标站点的 Crawl-delay 声明
- [ ] 13. 正确处理 HTTP 错误码(429, 403, 503)
- [ ] 14. 遇到封禁自动退避,不强行突破
- [ ] 15. 不在高峰时段大量抓取
- [ ] 16. 每次请求记录完整日志(时间、URL、状态码)
## 三、数据存储与使用(8项)
- [ ] 17. 数据按类型分类存储(个人信息/非个人信息)
- [ ] 18. 个人信息已做去标识化处理
- [ ] 19. 数据存储已加密
- [ ] 20. 访问权限最小化控制
- [ ] 21. 数据使用不超出原始授权范围
- [ ] 22. 未将数据分享给第三方(或已获单独同意)
- [ ] 23. 设定了数据保留期限
- [ ] 24. 到期数据自动清理
## 四、业务模式审查(4项)
- [ ] 25. 产品不对原平台构成"实质性替代"
- [ ] 26. 数据输出有实质性增值(分析/报告/建模)
- [ ] 27. 数据来源已标注
- [ ] 28. 有应对法律纠纷的预案
## 五、组织层面(4项)
- [ ] 29. 指定了数据合规负责人
- [ ] 30. 每半年做一次法律风险评估
- [ ] 31. 对员工进行了数据合规培训
- [ ] 32. 新业务上线前经过法务审查