Использование 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")