第16讲 可落地的爬虫合规清单:32项检查点,从代码到业务

0 阅读3分钟

学习目标

跟随架构师律师马原的脚步读完这一讲,你能:

  1. 逐项检查爬虫项目的合规状态
  2. 在代码层面实现自动化合规检查
  3. 建立团队的合规文化

技术原理

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. 新业务上线前经过法务审查