Извлечение данных Twitter с помощью Tweepy - PullRequest
0 голосов
/ 17 марта 2020

Есть ли какая-то явная причина, по которой я могу вытянуть твиты в CSV-файл, если это кто-то другой, кроме Дональда Трампа, но когда я пытаюсь использовать его в экранном имени realDonaldTrump, он возвращает только максимум 200 твитов?

Здесь вы можете найти код. (Например, с JLo он отлично работает ...)


def get_all_tweets(screen_name):
    # Twitter only allows access to a users most recent 3240 tweets with this method

    # authorize twitter, initialize tweepy
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_key, access_secret)
    api = tweepy.API(auth)

    # initialize a list to hold all the tweepy Tweets
    alltweets = []

    # make initial request for most recent tweets (200 is the maximum allowed count)
    new_tweets = api.user_timeline(screen_name=screen_name, count=200, tweet_mode='extended')

    # save most recent tweets
    alltweets.extend(new_tweets)

    # save the id of the oldest tweet less one
    oldest = alltweets[-1].id - 1

    # keep grabbing tweets until there are no tweets left to grab
    while len(new_tweets) > 0:
        print
        "getting tweets before %s" % (oldest)

        # all subsiquent requests use the max_id param to prevent duplicates
        new_tweets = api.user_timeline(screen_name=screen_name, count=200, max_id=oldest, tweet_mode='extended')

        # save most recent tweets
        alltweets.extend(new_tweets)

        # update the id of the oldest tweet less one
        oldest = alltweets[-1].id - 1

        print
        "...%s tweets downloaded so far" % (len(alltweets))

    # transform the tweepy tweets into a 2D array that will populate the csv
    outtweets = [[tweet.id_str, tweet.created_at, tweet.full_text.replace("\n","")] for tweet in alltweets]

    # write the csv
    with open('%s_tweets.csv' % screen_name, 'w') as f:
        writer = csv.writer(f)
        writer.writerow(["id", "created_at", "full_text"])
        writer.writerows(outtweets)

    pass


if __name__ == '__main__':
    # pass in the username of the account you want to download
    get_all_tweets("realDonaldTrump")

1 Ответ

0 голосов
/ 17 марта 2020

Ваша реализация работает; однако ваше приложение Twitter API, скорее всего, будет ограничено. Прочитайте о ограничении скорости , установленном в API Twitter.

В общем, когда вы сканируете данные из стороннего API, как это, вы хотите сохранить свои результаты где-нибудь надежным (в вашем случае в файле CSV в файловой системе, что может быть вполне приемлемо для вашей проблемы), поэтому вы можете снова запросить API, чтобы получить любые данные, которые вы, возможно, не получили ни в каких предыдущих запросах.

Я предоставлю Приведенный ниже наивный пример того, как вы можете изменить дизайн своего приложения, чтобы вести себя таким образом. В двух словах, при каждом выполнении приведенный ниже пример будет изменять любой ранее существующий CSV, так что он добавляет результаты, более новые, чем самая новая известная запись в CSV, и добавляет результаты, которые старше, чем самая старая известная запись в CSV.

Мой пример ниже также демонстрирует, как использовать курсоры Tweepy для итерации по страницам в твиттере.

import csv
from dataclasses import dataclass
from pathlib import Path
import logging
import os
import sys
import tweepy


@dataclass
class TweetIdRange:
    count: int
    newest_id: int
    oldest_id: int


def get_tweets_file_id_range(tweets_file_name: Path) -> TweetIdRange:
    id_range = TweetIdRange(count=0, newest_id=None, oldest_id=None)
    with open(tweets_file_name) as tweets_file:
        reader = csv.reader(tweets_file)
        try:
            next(reader)
        except StopIteration:
            raise RuntimeError(f'Tweets file ({tweets_file_name}) does not contain any rows; '
                               f'expected at least one header row')
        try:
            row = next(reader)
            row_id = int(row[0])
            id_range.newest_id = row_id
            id_range.oldest_id = row_id
            id_range.count += 1
        except StopIteration:
            pass
        else:
            for row in reader:
                id_range.count += 1
                row_id = int(row[0])
                if row_id < id_range.oldest_id:
                    id_range.oldest_id = row_id
                if row_id > id_range.newest_id:
                    id_range.newest_id = row_id
        return id_range


def write_tweets(tweets_file_name: Path, screen_name: str, since_id: int = None, max_id: int = None):
    user_timeline_options = {
        'count': 100,
        'tweet_mode': 'extended',
        'screen_name': screen_name,
        'since_id': since_id, # >
        'max_id': max_id # <=
    }
    tweet_count = 0
    with open(tweets_file_name, 'w') as tweets_file:
        writer = csv.writer(tweets_file)
        for page in tweepy.Cursor(api.user_timeline, **user_timeline_options).pages():
            tweets = [[tweet.id_str, tweet.created_at, tweet.full_text.replace('\n', '')] for tweet in page]
            tweet_count += len(tweets)
            writer.writerows(tweets)
    return tweet_count


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)

    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_key, access_secret)
    api = tweepy.API(auth)

    screen_name = sys.argv[1] if len(sys.argv) > 1 else None
    if not screen_name:
        print('error: missing required screen name positional argument', file=sys.stderr)
        sys.exit(1)

    tweets_file_name = Path(f'{screen_name}_tweets.csv')

    if not tweets_file_name.exists():
        logging.info('%s: initializing new file', tweets_file_name)
        with open(tweets_file_name, 'w') as existing_tweets_file:
            writer = csv.writer(existing_tweets_file)
            writer.writerow(['id', 'created_at', 'full_text'])

    tweets_id_range = get_tweets_file_id_range(tweets_file_name)
    logging.info('%s: tweet ID range (before): count: %s; newest: %s; oldest: %s',
                 tweets_file_name, tweets_id_range.count, tweets_id_range.newest_id, tweets_id_range.oldest_id)

    newest_tweets_file_name = tweets_file_name.with_suffix('.newest' + ''.join(tweets_file_name.suffixes))
    new_tweet_count = write_tweets(newest_tweets_file_name, screen_name, since_id=tweets_id_range.newest_id)
    logging.info('%s: prepending %s new tweets', tweets_file_name, new_tweet_count)

    oldest_tweets_file_name = tweets_file_name.with_suffix('.oldest' + ''.join(tweets_file_name.suffixes))
    if tweets_id_range.oldest_id:
        old_tweet_count = write_tweets(oldest_tweets_file_name, screen_name, max_id=tweets_id_range.oldest_id - 1)
        logging.info('%s: appending %s old tweets', tweets_file_name, old_tweet_count)

    swap_tweets_file_name = tweets_file_name.with_suffix('.swap' + ''.join(tweets_file_name.suffixes))

    with open(swap_tweets_file_name, 'w') as swap_file, \
         open(tweets_file_name, 'r') as existing_file:
        swap_file.write(existing_file.readline())

        with open(newest_tweets_file_name, 'r') as newest_tweets_file:
            for line in newest_tweets_file:
                swap_file.write(line)

        for line in existing_file:
            swap_file.write(line)

        if oldest_tweets_file_name.exists():
            with open(oldest_tweets_file_name, 'r') as oldest_tweets_file:
                for line in oldest_tweets_file:
                    swap_file.write(line)

    swap_tweets_file_name.rename(tweets_file_name)
    os.remove(newest_tweets_file_name)
    if oldest_tweets_file_name.exists():
        os.remove(oldest_tweets_file_name)

    tweets_id_range = get_tweets_file_id_range(tweets_file_name)
    logging.info('%s: tweet ID range (after): count: %s; newest: %s; oldest: %s',
                 tweets_file_name, tweets_id_range.count, tweets_id_range.newest_id, tweets_id_range.oldest_id)

Вы заметите, что последовательные прогоны этого будут возвращать больше данных, пока вы не нажмете конец того, что может предоставить API Twitter, например,

▶ python test.py realDonaldTrump
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (before): count: 350; newest: 1239685852093169664; oldest: 1235005879226961924
INFO:root:realDonaldTrump_tweets.csv: prepending 0 new tweets
INFO:root:realDonaldTrump_tweets.csv: appending 1799 old tweets
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (after): count: 2149; newest: 1239685852093169664; oldest: 1214517113437720576
▶ python test.py realDonaldTrump
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (before): count: 2149; newest: 1239685852093169664; oldest: 1214517113437720576
INFO:root:realDonaldTrump_tweets.csv: prepending 0 new tweets
INFO:root:realDonaldTrump_tweets.csv: appending 1045 old tweets
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (after): count: 3194; newest: 1239685852093169664; oldest: 1203103574781317121
▶ python test.py realDonaldTrump
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (before): count: 3194; newest: 1239685852093169664; oldest: 1203103574781317121
INFO:root:realDonaldTrump_tweets.csv: prepending 0 new tweets
INFO:root:realDonaldTrump_tweets.csv: appending 0 old tweets
INFO:root:realDonaldTrump_tweets.csv: tweet ID range (after): count: 3194; newest: 1239685852093169664; oldest: 1203103574781317121

Этот пример не предназначен для предоставления готовой к реализации реализации; однако, это должно дать вам несколько новых моментов для размышления, и это должно помочь вам преодолеть проблему, изложенную в вашем вопросе.

...