geduldig/TwitterAPI

APIv2 tweets pager - TwitterRequestError

factorlive opened this issue · 15 comments

Thanks again for implementing an enhancement based on an issue ( #167).

Unfortunately, there is a knock-on effect on the following API v2 endpoint:

tweets/search/recent

Code

api = TwitterAPI(os.getenv("CONSUMER_KEY"), os.getenv("CONSUMER_SECRET"),
                 os.getenv("TOKEN"), os.getenv("SECRET"), api_version='2')
pager = TwitterPager(api, 'tweets/search/recent', {'query':"Twitterdev"})
for item in pager.get_iterator(new_tweets=False):
    print(item)

Output and error:

{'id': '1362416352351244293', 'text': 'RT @TwitterDev: Learn how academics can get historical Tweets using the full-archive search endpoint v2 available in the Academic Research…'}
{'id': '1362413374764630021', 'text': 'Transferowe wydarzenie ostatnich dni to podpisanie przez stopera ŁKS Jana Sobocińskiego  trzyipółletniego kontraktu z... amerykańską drużyną Charlotte FC, która pojawi się w MLS w 2022 roku. https://t.co/3iQqLtH0it #example # demo przez @twitterdev'}
{'id': '1362409301860622341', 'text': 'Who makes the rules? @TwitterDev? @TwitterComms? @Twitter policy folks? How much weight will disinformation researchers, reporters and fact-checkers get in deciding how to set the algorithm for the rated helpful channel?'}
{'id': '1362405652430286851', 'text': 'Z dr. med. Januszem Szelugą, specjalistą psychiatrą z 40-letnim doświadczeniem zawodowym, rozmawia Dorota Abramowicz https://t.co/SPDOh4YIPN #example # demo przez @twitterdev \nDR JANUSZ SZELUGA'}
{'id': '1362397547382251521', 'text': '@TwitterDev Siap laksanakan'}
{'id': '1362390722129846276', 'text': 'Hello world https://t.co/yWDZZgiM9Z #demo via @twitterdev'}
{'id': '1362378944218071045', 'text': 'W zderzeniu dwóch osobówek zginęły dwie osoby. Jedną\xa0z ofiar jest Dominik Grabas - właściciel pizzerii w Rzeszowie. W Internecie trwa zbiórka pieniędzy dla jego rodziny. https://t.co/Uf2UkhzyWX #example # demo przez @twitterdev'}
{'id': '1362377262692143104', 'text': "Twitter is just full of bloat honestly, I don't like topics, I don't like fleets, I don't like getting recommended stuff, and I don't like getting notified about someone's tweet all of a sudden. Sure ads are fine, but the rest is just absurd @jack @TwitterDesign @TwitterDev"}
{'id': '1362365168894676998', 'text': '@tinucherian @Twitter @manishm @TwitterDev @jack I see it on my profile. Probably something they  might be trying or enhancing on the verified handles ?'}
{'id': '1362364357624102912', 'text': 'Nice. If you ever fancy joining forces to defeat @DnD_Lich then let me know! https://t.co/tYOEIeRbf8'}
---------------------------------------------------------------------------
TwitterRequestError                       Traceback (most recent call last)
<ipython-input-21-9810d1ec73a1> in <module>
      2                  os.getenv("TOKEN"), os.getenv("SECRET"), api_version='2')
      3 pager = TwitterPager(api, 'tweets/search/recent', {'query':"Twitterdev"})
----> 4 for item in pager.get_iterator(new_tweets=False):
      5     print(item)

~/.cache/pypoetry/virtualenvs/campaign-IjD-JE0s-py3.9/lib/python3.9/site-packages/TwitterAPI/TwitterPager.py in get_iterator(self, wait, new_tweets)
     47                 start = time.time()
     48                 r = self.api.request(self.resource, self.params)
---> 49                 it = r.get_iterator()
     50                 if new_tweets:
     51                     it = reversed(list(it))

~/.cache/pypoetry/virtualenvs/campaign-IjD-JE0s-py3.9/lib/python3.9/site-packages/TwitterAPI/TwitterAPI.py in get_iterator(self)
    238         """
    239         if self.response.status_code != 200:
--> 240             raise TwitterRequestError(self.response.status_code, msg=self.response.text)
    241 
    242         if self.stream:

TwitterRequestError: ('{"errors":[{"parameters":{"pagination_token":["b26v89c19zqg8o3fosns2v373jl0fj3jxys6lrnxm8h31"]},"message":"The query parameter [pagination_token] is not one of [query,start_time,end_time,since_id,until_id,max_results,next_token,expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields]"}],"title":"Invalid Request","detail":"One or more parameters to your request was invalid.","type":"https://api.twitter.com/2/problems/invalid-request"}',) (400): {"errors":[{"parameters":{"pagination_token":["b26v89c19zqg8o3fosns2v373jl0fj3jxys6lrnxm8h31"]},"message":"The query parameter [pagination_token] is not one of [query,start_time,end_time,since_id,until_id,max_results,next_token,expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields]"}],"title":"Invalid Request","detail":"One or more parameters to your request was invalid.","type":"https://api.twitter.com/2/problems/invalid-request"}

It appears V2 has an inconsisitency with regards to pagination. Some endpoints use pagination_token and some use next_token. I'm inquiring whether this is intended before updating the code.

I got a response from Twitter. This is an inconsistency. So, I will have to amend the code.

Probably, the developers of different endpoints are not talking much with each other these days. 😏

It appears 2/tweets/search/recent is the only endpoint that does not support pagination_token. I'll have to build in an exception for this until Twitter fixes this.

they will probably make it consistent at some point. an exception would be the best way to catch a different name of the token, indeed.

same error message for 2/tweets/search/all

Will upload a fix tomorrow.

This is the change to TwitterPager.py:

                SEARCH_ENDPOINTS = ['tweets/search/recent', 'tweets/search/all']
                pagination_token = 'next_token' if self.resource in SEARCH_ENDPOINTS else 'pagination_token'
                if new_tweets:
                    self.params[pagination_token] = meta['previous_token']
                else:
                    self.params[pagination_token] = meta['next_token']

With this change in TwitterPage.py, the output shows it only works within the first page and not with the pager to go beyond the first page:

{'id': '1362538499401011200', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362538321352794113', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362538308094492675', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362538285583728643', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362538143665242113', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362537885799391234', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362537838596743174', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362537789972094979', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362537408806277120', 'text': '@MattWallace888 @TwitterDev The cover photo 😩😂😂🤣'}
{'id': '1362537131718176772', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
Traceback (most recent call last):
  File "/home/patrick/icp-news/campaign/campaign/campaign.py", line 202, in <module>
    for item in pager.get_iterator(new_tweets=False):
  File "/home/patrick/icp-news/campaign/TwitterAPI/TwitterAPI/TwitterPager.py", line 123, in get_iterator
    self.params[pagination_token] = meta['next_token']
UnboundLocalError: local variable 'pagination_token' referenced before assignment

Is after making the code change I suggested?

yes, my comment was not clear and I revised it. Got your repo now in editable mode.

What happens if you replace
pagination_token = 'next_token' if self.resource in SEARCH_ENDPOINTS else 'pagination_token'
with
pagination_token = 'pagination_token'

Same problem.

Code TwitterPager.py

(...)
                else:  # VERSION 2
                    if new_tweets
                        SEARCH_ENDPOINTS = ['tweets/search/recent', 'tweets/search/all']
                        pagination_token = 'pagination_token'
                        # pagination_token = 'next_token' if self.resource in SEARCH_ENDPOINTS else 'pagination_token'
                        self.params[pagination_token] = meta['previous_token']
                    else:
                        self.params[pagination_token] = meta['next_token']
(...)

Output

{'id': '1362542072922734598', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362541974121631744', 'text': '@MattWallace888 @TwitterDev Tron has one as well trx'}
{'id': '1362541629945483267', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362541266412728331', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362540973960658951', 'text': '2021 NASCAR Cup Series DFS Power Rankings - Daytona Road Course - DFS Army https://t.co/FM2MkP2hyZ via @twitterdev'}
{'id': '1362540274031988738', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362540110475112449', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362539439227088900', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362539385019842560', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
{'id': '1362539261250035713', 'text': 'RT @MattWallace888: @TwitterDev https://t.co/YR2kOekDPb'}
Traceback (most recent call last):
  File "/home/patrick/icp-news/campaign/campaign/campaign.py", line 202, in <module>
    for item in pager.get_iterator(new_tweets=False):
  File "/home/patrick/icp-news/campaign/TwitterAPI/TwitterAPI/TwitterPager.py", line 124, in get_iterator
    self.params[pagination_token] = meta['next_token']
UnboundLocalError: local variable 'pagination_token' referenced before assignment

I see your problem. You didn't copy code quite correctly. You must define pagination_token before the if...else. The way you have it, pagination_token is undefined for the else clause.

Yep. Thanks. Pull request sent.

Got another enhancement, however, and will open another issue. Hope you don't mind 😁

Fixed in v2.6.7.