1. Robots协议基础解析
Robots协议是网站与爬虫之间的"交通规则",它通过一个名为robots.txt的文本文件来规范爬虫的访问行为。这个文件通常位于网站的根目录下,比如https://example.com/robots.txt。我第一次接触这个协议时,曾误以为它是个复杂的配置文件,实际上它的语法出奇简单。
robots.txt的核心语法由几个关键指令组成:
- User-agent:指定规则适用的爬虫名称,使用"*"表示所有爬虫
- Disallow:禁止爬取的URL路径
- Allow:允许爬取的URL路径(优先级高于Disallow)
- Crawl-delay:两次请求之间的最小时间间隔(秒)
- Sitemap:网站地图的位置
举个例子,某电商网站的robots.txt可能这样写:
User-agent: * Disallow: /admin/ Disallow: /checkout/ Allow: /public/ Crawl-delay: 2 Sitemap: https://example.com/sitemap.xml这里有个常见误区:很多人以为robots.txt能完全阻止内容被抓取。实际上它更像是个"建议"文件,合规的爬虫会遵守,但恶意爬虫完全可以无视。要真正阻止内容被抓取,应该结合身份验证或noindex元标签。
2. Python解析robots.txt实战
Python标准库中的urllib.robotparser模块让robots.txt解析变得非常简单。下面我通过一个完整示例展示如何在实际项目中应用。
首先创建一个RobotFileParser实例并读取robots.txt:
from urllib.robotparser import RobotFileParser from urllib.parse import urljoin def init_robot_parser(url): rp = RobotFileParser() robots_url = urljoin(url, '/robots.txt') rp.set_url(robots_url) try: rp.read() except Exception as e: print(f"读取robots.txt失败: {e}") return None return rp使用时可以这样检查某个URL是否允许抓取:
def can_crawl(url, user_agent="MyBot"): rp = init_robot_parser(url) if rp is None: return True # 无法获取robots.txt时默认允许 return rp.can_fetch(user_agent, url)我在实际项目中遇到过几个坑需要特别注意:
- robots.txt读取失败时要做好异常处理
- 相对路径需要转换为绝对路径
- 对于大型网站,应该缓存解析结果避免重复请求
- 要正确处理Crawl-delay参数
一个更健壮的实现应该包含缓存和延迟控制:
from time import sleep import requests from datetime import datetime, timedelta class PoliteCrawler: def __init__(self): self.robot_parsers = {} self.last_visit = {} def check_robots(self, url, user_agent): base_url = f"{url.scheme}://{url.netloc}" if base_url not in self.robot_parsers: self.robot_parsers[base_url] = init_robot_parser(base_url) rp = self.robot_parsers[base_url] if not rp: return True # 检查爬取间隔 if base_url in self.last_visit: delay = rp.crawl_delay(user_agent) or 1 elapsed = (datetime.now() - self.last_visit[base_url]).total_seconds() if elapsed < delay: sleep(delay - elapsed) self.last_visit[base_url] = datetime.now() return rp.can_fetch(user_agent, url.geturl())3. 动态路径解析与匹配规则
robots.txt的路径匹配规则看似简单,但实际应用中有些细节容易踩坑。让我分享几个实战经验。
路径匹配规则:
- Disallow: /admin/ 会阻止/admin/下的所有内容
- Disallow: /temp$.html 阻止以temp.html结尾的URL
- Disallow: /*.php$ 阻止所有.php文件
- Allow: /public/ 允许/public/下的内容(即使上级目录被禁止)
我曾经遇到一个案例:某网站设置了Disallow: /search,但爬虫仍然抓取了/search/result页面,因为路径不完全匹配。正确的写法应该是Disallow: /search/。
处理动态参数时更要注意:
Disallow: /products?sort= # 阻止带sort参数的URL Disallow: /*?* # 阻止所有带参数的URL在Python中实现精确匹配需要处理这些规则:
def path_matches_rule(path, rule): if rule.endswith('$'): return path == rule[:-1] if rule.endswith('*'): return path.startswith(rule[:-1]) return path.startswith(rule)对于大型网站,建议使用专门的robots.txt解析库,比如reppy或robotexclusionrulesparser,它们能更准确地处理各种边缘情况。
4. 爬虫限速与合规策略
遵守Crawl-delay是爬虫合规的关键。根据我的经验,过于频繁的请求是网站封禁爬虫的最常见原因。
合理设置爬取间隔:
- 优先遵守robots.txt中的Crawl-delay
- 没有明确要求时,默认间隔建议2-5秒
- 对API接口的访问间隔应该更长(10秒以上)
- 夜间可以适当加快速度(如果网站流量低谷)
实现智能限速的代码示例:
import time from collections import defaultdict class RateLimiter: def __init__(self, default_delay=2): self.domain_timers = defaultdict(float) self.default_delay = default_delay def wait(self, domain): last_time = self.domain_timers.get(domain, 0) elapsed = time.time() - last_time if elapsed < self.default_delay: time.sleep(self.default_delay - elapsed) self.domain_timers[domain] = time.time()其他合规建议:
- 设置合理的User-Agent标识(包含联系方式)
- 处理HTTP 429状态码(太多请求)
- 监控被封禁的迹象(验证码、空响应)
- 避免在高峰时段爬取
我曾经帮客户优化过一个爬虫,通过以下调整将封禁率从30%降到几乎0:
- 将默认间隔从0.5秒提高到3秒
- 添加随机抖动(±1秒)
- 实现自动退避机制(遇到429时加倍等待时间)
- 添加详细的日志记录
5. 高级应用与边缘案例
在实际项目中,我们会遇到各种特殊场景需要处理。这里分享几个典型案例。
多User-agent处理: 大型网站可能对不同爬虫设置不同规则:
User-agent: Googlebot Allow: / User-agent: * Disallow: /这时我们的爬虫应该尽量使用通用User-agent,或者伪装成主流爬虫(需谨慎)。
处理robots.txt更新: 网站的robots.txt可能随时变更,好的爬虫应该:
- 定期重新获取robots.txt(比如每24小时)
- 监控Last-Modified和ETag头
- 对重要变更发出警报
无robots.txt的情况: 约15%的网站没有robots.txt,这时应该:
- 默认允许爬取
- 但仍要保持合理间隔
- 特别注意隐私内容(如/admin/等常见敏感路径)
处理robots.txt错误: 我曾见过各种格式错误的robots.txt,我们的代码需要:
- 容忍空文件
- 忽略未知指令
- 处理编码问题(强制UTF-8)
- 记录解析错误
最后分享一个真实案例:某新闻网站突然更改robots.txt禁止所有爬虫,导致我们的系统停止工作。解决方案是:
- 立即暂停爬取
- 联系网站管理员确认
- 获得书面许可后添加白名单机制
- 实现更精细的访问控制
爬虫开发不仅是技术问题,更需要考虑法律和道德因素。保持透明、尊重网站规则、控制访问频率,这样才能建立可持续的数据采集方案。