Использование Tweepy для прослушивания потока и поиска твитов. Как остановить предыдущий поиск и слушать только новый поток?
Я использую колбу и Tweepy для поиска живых твитов. На переднем конце у меня есть пользовательский текстовый ввод и кнопка под названием "Поиск". В идеале, когда пользователь вводит поисковый запрос и нажимает кнопку "Поиск", Tweepy должен прослушать новый поисковый запрос и остановить предыдущий поток поисковых запросов. При нажатии на кнопку "поиск" выполняется следующая функция:
@app.route('/search', methods=['POST'])
# gets search-keyword and starts stream
def streamTweets():
search_term = request.form['tweet']
search_term_hashtag = '#' + search_term
# instantiate listener
listener = StdOutListener()
# stream object uses listener we instantiated above to listen for data
stream = tweepy.Stream(auth, listener)
if stream is not None:
print "Stream disconnected..."
stream.disconnect()
stream.filter(track=[search_term or search_term_hashtag], async=True)
redirect('/stream') # execute '/stream' sse
return render_template('index.html')
на /stream
маршрут, который выполняется во второй до последней строки в приведенном выше коде в следует:
@app.route('/stream')
def stream():
# we will use Pub/Sub process to send real-time tweets to client
def event_stream():
# instantiate pubsub
pubsub = red.pubsub()
# subscribe to tweet_stream channel
pubsub.subscribe('tweet_stream')
# initiate server-sent events on messages pushed to channel
for message in pubsub.listen():
yield 'data: %snn' % message['data']
return Response(stream_with_context(event_stream()), mimetype="text/event-stream")
мой код работает нормально, в том смысле, что он запускает новый поток и ищет данный термин всякий раз, когда нажимается кнопка "Поиск", но он не останавливает предыдущий поиск. Например, если мой первый поисковый термин был "NYC", а затем я хотел найти другой термин, скажем" Лос-Анджелес", он даст мне результаты как для" NYC", так и для" Los Angeles", что не то, что я хочу. Я хочу, чтобы обыскали только "Лос-Анджелес". Как это исправить? Другими словами, как я остановить предыдущий поток? Я просмотрел предыдущие темы, и я знаю, что я должен использовать stream.disconnect()
, но я не уверен, как реализовать это в мой код. Любая помощь или вклад были бы весьма признательны. Большое спасибо!!
3 ответов
Ниже приведен код, который отменит старые потоки при создании нового потока. Он работает, добавляя новые потоки в глобальный список, а затем вызывая stream.disconnect()
для всех потоков в списке при создании нового потока.
diff --git a/app.py b/app.py
index 1e3ed10..f416ddc 100755
--- a/app.py
+++ b/app.py
@@ -23,6 +23,8 @@ auth.set_access_token(access_token, access_token_secret)
app = Flask(__name__)
red = redis.StrictRedis()
+# Add a place to keep track of current streams
+streams = []
@app.route('/')
def index():
@@ -32,12 +34,18 @@ def index():
@app.route('/search', methods=['POST'])
# gets search-keyword and starts stream
def streamTweets():
+ # cancel old streams
+ for stream in streams:
+ stream.disconnect()
+
search_term = request.form['tweet']
search_term_hashtag = '#' + search_term
# instantiate listener
listener = StdOutListener()
# stream object uses listener we instantiated above to listen for data
stream = tweepy.Stream(auth, listener)
+ # add this stream to the global list
+ streams.append(stream)
stream.filter(track=[search_term or search_term_hashtag],
async=True) # make sure stream is non-blocking
redirect('/stream') # execute '/stream' sse
то, что это не решает проблему управления сеансами. При текущей настройке поиск одним пользователем повлияет на поиск всех пользователей. Этого можно избежать, предоставив пользователям некоторый идентификатор и сохранив их потоки вместе с их идентификатор. Самый простой способ сделать это, скорее всего, использоватьсессии поддержка. Вы также можете сделать это с помощью requestId
как предложил Пьер. В любом случае вам также понадобится код, чтобы заметить, когда пользователь закрыл страницу и закрыть их поток.
отказ от ответственности: я ничего не знаю о Tweepy, но это, похоже, проблема дизайна.
вы пытаетесь добавить состояние в RESTful API? У вас может быть проблема с дизайном. Как ответил JRichardSnape, ваш API не должен заботиться об отмене запроса; это должно быть сделано в интерфейсе. Я имею в виду, что в javascript / AJAX / etc вызывая эту функцию, добавьте другой вызов, к новой функции
@app.route('/cancelSearch', methods=['POST'])
С "POST", который имеет условия поиска. Так пока у вас нет состояния, вы не можете сделать это безопасно в асинхронном вызове:представьте, что кто-то другой делает то же поиск в то же время затем отмена одного отменит оба (помните, что у вас нет состояния, поэтому вы не знаете, кого вы отменяете). Возможно!--9-->вам нужно состояние С вашим дизайном.
если вы должны продолжать использовать это и не против нарушения правила "без гражданства", добавьте" состояние " к вашему запросу. В этом случае все не так плохо, потому что ты может запустить поток и назвать его с userId, а затем убить поток каждый новый поиск
def streamTweets():
search_term = request.form['tweet']
userId = request.form['userId'] # If your limit is one request per user at a time. If multiple windows can be opened and you want to follow this limit, store userId in a cookie.
#Look for any request currently running with this ID, and cancel them
кроме того, вы можете вернуть requestId
, который вы затем будете держать в интерфейсе, можете вызвать cancelSearch?requestId=$requestId
. В cancelSearch вам нужно будет найти ожидающий запрос (похоже, что это в tweepy, поскольку вы не используете свои собственные потоки) и отключить его.
из любопытства я просто смотрел, что происходит, когда вы ищете в Google, и он использует запрос GET. Иметь посмотрите (инструменты отладки - > сеть; затем введите текст и посмотрите автозаполнение). Google использует токен, отправляемый с каждым запросом (каждый раз, когда вы что-то вводите)). Это не означает, что он используется для этого, но это в основном то, что я описал. если вы не хотите сеанс, то используйте уникальный идентификатор.
Ну, я решил его с помощью метода таймера, но все же я ищу pythonic way.
from streamer import StreamListener
def stream():
hashtag = input
#assign each user an ID ( for pubsub )
StreamListener.userid = random_user_id
def handler(signum, frame):
print("Forever is over")
raise Exception("end of time")
def main_stream():
stream = tweepy.Stream(auth, StreamListener())
stream.filter(track=track,async=True)
redirect(url_for('map_stream'))
def close_stream():
# this is for closing client list in redis but don't know it's working
obj = redis.client_list(tweet_stream)
redis_client_list = obj[0]['addr']
redis.client_kill(redis_client_list)
stream = tweepy.Stream(auth, StreamListener())
stream.disconnect()
import signal
signal.signal(signal.SIGALRM, handler)
signal.alarm(300)
try:
main_stream()
except Exception:
close_stream()
print("function terminate")