
Scrapy 爬虫仅处理 start_requests 中第一个 URL,其余被忽略,通常是因 Python 列表语法错误(缺少逗号)导致字符串隐式拼接,使 urls 实际只含一个超长 URL。本文详解问题定位、修复方法及最佳实践。
scrapy 爬虫仅处理 `start_requests` 中第一个 url,其余被忽略,通常是因 python 列表语法错误(缺少逗号)导致字符串隐式拼接,使 `urls` 实际只含一个超长 url。本文详解问题定位、修复方法及最佳实践。
在 Scrapy 中,start_requests() 方法是定义初始请求的入口。当需要从多个起始 URL 并发抓取时(例如分别爬取罗马、米兰、贝加莫三地餐厅列表),开发者常通过循环 yield scrapy.Request() 实现。但若代码中存在细微语法错误,极易导致“看似写了多个 URL,实则只请求第一个”的现象——这正是本案例的核心问题。
? 问题根源:Python 字符串隐式拼接
观察原始代码中的 urls 列表:
urls = [
'https://www.tripadvisor.it/Restaurants-g187791-Rome_Lazio.html'
'https://www.tripadvisor.it/Restaurants-g187849-Milan_Lombardy.html'
'https://www.tripadvisor.it/Restaurants-g187830-Bergamo_Province_of_Bergamo_Lombardy.html'
]
⚠️ 关键错误:三行 URL 之间缺少逗号分隔符。Python 会将相邻字符串字面量自动拼接为单个字符串(PEP 257 规定),等价于:
urls = [
'https://www.tripadvisor.it/Restaurants-g187791-Rome_Lazio.htmlhttps://www.tripadvisor.it/Restaurants-g187849-Milan_Lombardy.htmlhttps://www.tripadvisor.it/Restaurants-g187830-Bergamo_Province_of_Bergamo_Lombardy.html'
]
因此 for url in urls: 实际仅迭代一次,且该 URL 极大概率返回 404 或解析失败,后续城市自然无法进入爬取流程。
✅ 正确写法:补全逗号 + 增强健壮性
修复只需添加缺失的逗号,并建议同步优化以下几点:
- ✅ 显式声明 allowed_domains(提升去重与安全校验)
- ✅ 为请求添加 headers(规避反爬拦截,尤其 TripAdvisor 对 User-Agent 敏感)
- ✅ 使用 .getall() 替代 .extract()(Scrapy 2.0+ 推荐,更语义化)
- ✅ 移除冗余的 list(set())(Scrapy 默认启用 DUPEFILTER_CLASS,重复 URL 自动过滤)
修正后的 start_requests 示例:
import scrapy
class ReviewSpider2(scrapy.Spider):
name = 'reviews2'
allowed_domains = ['tripadvisor.it']
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
}
def start_requests(self):
urls = [
'https://www.tripadvisor.it/Restaurants-g187791-Rome_Lazio.html',
'https://www.tripadvisor.it/Restaurants-g187849-Milan_Lombardy.html',
'https://www.tripadvisor.it/Restaurants-g187830-Bergamo_Province_of_Bergamo_Lombardy.html',
]
for url in urls:
yield scrapy.Request(
url=url,
callback=self.parse_restaurants,
headers=self.headers,
# 可选:添加 errback 处理请求失败
# errback=self.handle_failure
)
def parse_restaurants(self, response):
# 安全提取:使用 getall() + 过滤空值
restaurant_paths = response.xpath(
"//div[contains(@data-test,'_list_item')]//a[starts-with(@href,'/Restaurant_Review')]/@href"
).getall()
for path in filter(None, restaurant_paths):
full_url = f"https://www.tripadvisor.it{path}"
yield response.follow(full_url, callback=self.parse_restaurant)
⚠️ 注意事项与进阶建议
- 调试技巧:在 start_requests 中加入 self.logger.info(f"Starting with URL: {url}"),或直接 print(url),可快速验证 URL 是否按预期生成。
- 反爬应对:TripAdvisor 对高频请求敏感,建议设置 DOWNLOAD_DELAY = 2(settings.py 中)并启用 AUTOTHROTTLE_ENABLED = True。
- 链接提取健壮性:原 XPath 过于复杂且易断裂,推荐简化为 //a[starts-with(@href, '/Restaurant_Review')]/@href,并用 response.follow() 自动处理相对路径。
- 错误处理:务必为 parse_restaurant 添加 if response.status != 200: 判断,避免因个别餐厅页面失效中断整个流程。
遵循以上修正与规范,Scrapy 将真正并发调度所有起始 URL,实现多城市餐厅数据的稳定、高效采集。










