使用Tweepy捕获Twitter流API时WantReadError无法捕获的问题
解决Tweepy流API中OpenSSL异常捕获与自动重启问题
这个问题的核心是:你遇到的OpenSSL.SSL.WantReadError是在Tweepy的后台工作线程中抛出的,而不是在创建Stream对象的主线程里,所以你之前在twitterStream = tweepy.Stream(...)外面加的try-except根本捕获不到它。下面是具体的解决方案,包含异常捕获、自动重启和递增退避逻辑:
第一步:导入必要模块
首先要引入OpenSSL模块,这样才能明确捕获那个特定异常,同时解决IDE的"undefined name"报错:
import tweepy import OpenSSL import time import logging # 配置日志,方便追踪问题 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)
第二步:自定义StreamListener(保留你的业务逻辑)
先确保你的自定义监听器正常工作,比如:
class MyStreamListener(tweepy.StreamListener): def on_status(self, status): # 替换成你处理推文的逻辑 logger.info(f"收到推文:{status.text[:50]}...") def on_error(self, status_code): logger.error(f"API错误,状态码:{status_code}") # 429是限流,返回False会断开连接,交给外层逻辑重启 if status_code == 429: return False return True
方案一:阻塞模式下的外层循环捕获(简单直接)
如果使用is_async=False(默认就是这个值,Stream会阻塞主线程运行),你可以在外层写一个无限循环,每次尝试启动流,捕获所有异常后重启,同时实现递增退避:
def start_stream_with_backoff(auth, listener): backoff_time = 1 # 初始等待时间 max_backoff = 30 # 最大等待时间,避免无限递增 while True: try: logger.info("正在启动Twitter流...") stream = tweepy.Stream(auth=auth, listener=listener) # 替换成你的过滤条件,比如track、follow等 stream.filter(track=["python"], is_async=False) except OpenSSL.SSL.WantReadError as e: logger.error(f"捕获到WantReadError:{e},即将重启流...") except Exception as e: logger.error(f"发生未预期错误:{e},即将重启流...") finally: # 递增退避:每次等待时间翻倍,不超过最大值 logger.info(f"等待{backoff_time}秒后重启...") time.sleep(backoff_time) backoff_time = min(backoff_time * 2, max_backoff)
调用这个函数启动流:
if __name__ == "__main__": # 替换成你的Twitter API密钥 auth = tweepy.OAuthHandler("你的CONSUMER_KEY", "你的CONSUMER_SECRET") auth.set_access_token("你的ACCESS_TOKEN", "你的ACCESS_TOKEN_SECRET") listener = MyStreamListener() start_stream_with_backoff(auth, listener)
方案二:异步模式下重写Stream的_run方法(更灵活)
如果需要用is_async=True让流在后台线程运行,你需要重写Tweepy的Stream类的_run方法,在子线程内部捕获异常并处理重启:
class RestartableStream(tweepy.Stream): def __init__(self, auth, listener, backoff_time=1, max_backoff=30): super().__init__(auth=auth, listener=listener) self.backoff_time = backoff_time self.max_backoff = max_backoff self.logger = logging.getLogger(__name__) def _run(self): while True: try: # 调用父类的_run方法,也就是原来的流运行逻辑 super()._run() except OpenSSL.SSL.WantReadError as e: self.logger.error(f"线程内捕获到WantReadError:{e},即将重启流...") except Exception as e: self.logger.error(f"线程内发生未预期错误:{e},即将重启流...") finally: # 同样应用递增退避 self.logger.info(f"等待{self.backoff_time}秒后重启...") time.sleep(self.backoff_time) self.backoff_time = min(self.backoff_time * 2, self.max_backoff)
使用这个自定义Stream类:
if __name__ == "__main__": auth = tweepy.OAuthHandler("你的CONSUMER_KEY", "你的CONSUMER_SECRET") auth.set_access_token("你的ACCESS_TOKEN", "你的ACCESS_TOKEN_SECRET") listener = MyStreamListener() stream = RestartableStream(auth=auth, listener=listener) stream.filter(track=["python"], is_async=True) # 如果需要主线程保持运行,可以加个无限循环 while True: time.sleep(3600)
关键说明
- 为什么之前的
try-except没用?因为错误发生在Stream启动后的后台线程中,而不是创建Stream对象的瞬间,所以必须在异常发生的线程上下文里捕获,或者用外层循环包裹阻塞式的filter()调用。 - 递增退避逻辑:避免在网络不稳定时频繁请求Twitter API,防止被限流或拉黑,等待时间从1秒开始翻倍,直到30秒后保持最大值。
- 日志记录:一定要保留日志,方便你追踪每次重启的原因和时间,排查问题。
内容的提问来源于stack exchange,提问作者user1235777
相关产品推荐
相关产品推荐

