Scrapy异常时入队新URL及HTTP/HTTPS自动适配技术咨询
Scrapy爬虫异常处理与协议自动切换方案
嘿,针对你提到的两个Scrapy问题,我给你整理了具体的实现方案,直接看代码和解释就行:
一、爬虫发生异常时,把新URL加入请求队列
在Scrapy里,我们可以给每个Request绑定一个errback回调函数——这个函数专门用来处理请求失败的情况。当请求触发异常时,errback会被调用,我们就在这个函数里构造新的URL,然后yield出新的Request对象,Scrapy就会自动把这个新URL加入请求队列继续处理了。
给你写个示例代码,一看就懂:
import scrapy from scrapy.spidermiddlewares.httperror import HttpError from twisted.internet.error import DNSLookupError, ConnectionRefusedError, TimeoutError class MySpider(scrapy.Spider): name = "myspider" def start_requests(self): # 初始请求,绑定错误回调 yield scrapy.Request( url="https://example.com/target", callback=self.parse, errback=self.handle_error, meta={"original_url": "https://example.com/target"} # 传递原始URL,方便后续处理 ) def parse(self, response): # 正常解析页面的逻辑 self.logger.info(f"成功拿到响应: {response.url}") def handle_error(self, failure): failed_url = failure.request.url self.logger.error(f"请求炸了: {failed_url}") # 可以针对性捕获不同异常(可选,也可以直接处理所有异常) if failure.check(HttpError): self.logger.error(f"HTTP错误,状态码: {failure.value.response.status}") elif failure.check(DNSLookupError): self.logger.error("DNS解析失败,可能域名写错了?") elif failure.check(ConnectionRefusedError): self.logger.error("连接被拒绝,服务器可能拦截了?") elif failure.check(TimeoutError): self.logger.error("请求超时了,服务器反应太慢") # 构造新URL,这里示例是加个重试参数,你可以根据需求改 new_url = f"{failed_url}?retry=1" # 生成新请求,继续加入队列 yield scrapy.Request( url=new_url, callback=self.parse, errback=self.handle_error, meta={"original_url": failed_url} )
二、自动切换HTTP/HTTPS协议的完整实现
针对你给出的代码片段,我帮你完善了逻辑:优先尝试HTTPS请求,一旦失败就自动切换成HTTP再试,还加了重复失败URL的过滤,避免死循环。直接看完整代码:
import scrapy from scrapy.spidermiddlewares.httperror import HttpError from twisted.internet.error import DNSLookupError, ConnectionRefusedError, TimeoutError class MySpider(scrapy.Spider): name = "myspider" def __init__(self, category=None): self.failed_urls = [] # 记录已经处理过的失败URL,防止重复请求 def start_requests(self): # 初始优先发起HTTPS请求,这里可以扩展成多个URL target_urls = ['https://www.wsiltv.com/random'] for url in target_urls: yield scrapy.Request( url=url, callback=self.parse, errback=self.switch_to_http, meta={"original_protocol": "https"} # 标记初始用的是HTTPS ) def parse(self, response): self.logger.info(f"搞定!成功获取响应: {response.url}") # 这里写你的页面解析逻辑,比如提取数据、跟进链接之类的 pass def switch_to_http(self, failure): request = failure.request original_url = request.url # 先检查这个URL是不是已经处理过了,避免重复切换协议 if original_url in self.failed_urls: self.logger.warning(f"这个URL已经失败过了,跳过: {original_url}") return self.failed_urls.append(original_url) self.logger.error(f"HTTPS请求失败: {original_url},马上切换成HTTP试试") # 把HTTPS替换成HTTP,生成新请求 if original_url.startswith("https://"): http_url = original_url.replace("https://", "http://", 1) yield scrapy.Request( url=http_url, callback=self.parse, errback=self.handle_final_failure, # HTTP请求也失败的话,走最终错误处理 meta={"original_protocol": "http"} ) else: self.logger.error(f"不是HTTPS请求,没法切换协议: {original_url}") def handle_final_failure(self, failure): failed_http_url = failure.request.url self.logger.error(f"HTTP请求也凉了: {failed_http_url},放弃处理这个URL了") # 这里可以加后续处理,比如把失败URL写到文件里,方便后续排查 # with open('failed_urls.txt', 'a') as f: # f.write(f"{failed_http_url}\n")
这个逻辑很清晰:先试HTTPS,失败就切HTTP;如果HTTP也失败,就记录日志或者做其他收尾工作。而且用self.failed_urls列表存已经处理过的失败URL,不会重复请求同一个URL,避免浪费资源。
内容的提问来源于stack exchange,提问作者user1330974
相关产品推荐
相关产品推荐

