Вот как вы можете использовать эвристику программирования, чтобы сделать лимиты скорости Twitter менее опасными.

Знаете ли вы, что существует Twitter API, который вы можете использовать для получения текущего статуса лимита скорости? Ну, есть. Проблема в том, что они ограничивают скорость API, который сообщает вам о ваших текущих ограничениях скорости. Круто, да?

Откуда я узнал? Я очень рад, что вы только что задали этот вопрос.

Полюбуйтесь на этот код:

class TwitterAPIProxy(MagicObject2):
    def __init__(self, consumer_key=None, consumer_secret=None, access_token=None, access_token_secret=None, logger=None):
        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.access_token = access_token
        self.access_token_secret = access_token_secret
        self.logger = logger
        self.calls_count = 0
        self.start_time = time.time()
        self.rate_limit = os.environ.get('twitter_rate_limit', 0.25)
        assert self.rate_limit and (isinstance(self.rate_limit, str)) and (len(self.rate_limit) > 0), 'Missing rate_limit.'
        self.rate_limit = float(self.rate_limit)
        self.rate_limit_stats = {}
        
        try:
            self.auth = tweepy.OAuthHandler(self.consumer_key, self.consumer_secret)
            self.auth.set_access_token(self.access_token, self.access_token_secret)
            self.api = tweepy.API(self.auth)
            self.ingest_rate_limit_stats(self.api.rate_limit_status())
        except:
            msg = 'Problem connecting to the twitter?'
            if (logger):
                logger.exception(msg)
            sys.exit()
            
            
    def __refresh_rate_limits__(self):
        self.rate_limit_stats = {}
        self.ingest_rate_limit_stats(self.api.rate_limit_status())
        
    
    def __any_rate_limits_getting_low__(self, threshold):
        self.__refresh_rate_limits__()
        items = [i for i in list(self.rate_limit_stats.get('remaining', {}).keys())]
        return any([int(i) < threshold for i in items])


    def __call__(self,*args,**kwargs):
        self.n = [n for n in self.n if (n != '__iter__')]
        if (len(self.n) > 0):
            self.calls_count += 1
            if (1):
                # this seems to work.
                et = time.time() - self.start_time
                v = self.calls_count / et
                if (self.logger):
                    self.logger.info('Twitter rate limits? ({} calls in {} secs)  (v is {} of {})'.format(self.calls_count, et, v, self.rate_limit))
                if (v > self.rate_limit):
                    while (v > self.rate_limit):
                        if (self.logger):
                            self.logger.info('Sleeping due to twitter rate limits. (v is {} of {})'.format(v, self.rate_limit))
                        time.sleep(1)
                        et = time.time() - self.start_time
                        v = self.calls_count / et
            if (0):
                # this seems to fail.
                if (self.__any_rate_limits_getting_low__(5)):
                    if (self.logger):
                        self.logger.info('Some rate limits are getting low.')
                    while (1):
                        if (self.logger):
                            self.logger.info('Sleeping for 5 secs.')
                        time.sleep(5)
                        if (not self.__any_rate_limits_getting_low__(5)):
                            if (self.logger):
                                self.logger.info('Some rate limits are ok now. Proceed.')
                            break
            method = self.n.pop()
            s = 'self.api.%s(*args,**kwargs)' % (method)
            if (self.logger):
                self.logger.info('{} {}'.format(self.__class__, s))
            time.sleep(0.25)
            resp = None
            while (1):
                try:
                    resp = eval(s)
                    break
                except tweepy.error.RateLimitError:
                    while (1):
                        if (self.logger):
                            self.logger.info('Sleeping for 30 secs')
                        time.sleep(30)
                        if (self.logger):
                            self.logger.info('Refreshing rate limits data.')
                        self.__refresh_rate_limits__()
                        if (not self.__any_rate_limits_getting_low__(5)):
                            if (self.logger):
                                self.logger.info('Rate limits ok, proceed.')
                            break
            return resp
        return None
    
    
    def ingest_rate_limit_stats(self, stats, ignoring=['labs', 'moments']):
        for category,cat_stats in stats.items():
            if (category not in ['rate_limit_context']):
                for subcat,subcat_stats in cat_stats.items():
                    for specific,specific_stats in subcat_stats.items():
                        if (subcat in ignoring):
                            continue
                        p = '{}:{}:{}'.format(category, subcat, specific)
                        for k,v in specific_stats.items():
                            bucket = self.rate_limit_stats.get(k, {})
                            subBucket = bucket.get(v, [])
                            subBucket.append(p)
                            bucket[v] = subBucket
                            self.rate_limit_stats[k] = bucket

Части этого кода являются проприетарными, поэтому вы можете не знать точно, что делает этот объект, кроме того, что он делает некоторую магию, очевидно, захватывая некоторые вещи из стека Python во время выполнения.

Биты Twitter представлены в виде функции __refresh_rate_limits__, которую я написал для получения текущего состояния ограничения скорости из Twitter, которую я надеялся использовать для правильной оценки того, достиг ли я пределов скорости, чтобы я мог накачать тормоза до замедлите вещь, прежде чем удариться о стену.

Проблема, с которой я столкнулся, заключалась в том, что ограничения скорости были затронуты функцией, которую я использовал для получения статуса ограничения скорости. Круто, да? И очень злой.

Итак, как я решил эту проблему?

Я могу заверить вас, что решил эту проблему, потому что я решаю все проблемы с кодом.

Я использовал то, что я называю «эвристикой».

Согласно Google, эвристика — это практический подход к обучению, и я использовал именно его.

Я начал с предположения. Я предполагаю, что я использовал некоторую часть ограничения скорости, прежде чем начать использовать ограничение скорости. На данный момент это предположение составляет 30%, что означает, что я, вероятно, использовал 70% ограничения скорости, прежде чем начать. Я бы сделал это предположение как человек, и именно так мне нравится писать свой код.

Затем я написал метод для отслеживания имитации ограничения скорости.

Метод __init__ устанавливает это, а метод __call__ обрабатывает симуляцию.

Ставка – это количество вызовов в единицу времени. Поэтому я отслеживаю время начала и количество звонков и делаю расчеты с каждым звонком.

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

Сумасшествие в том, что это, кажется, работает.

Но вы знаете, что? Всем нравятся сумасшедшие идеи, которые работают, поэтому я выбираю это.

Я также включил код, который не работает. О, код работает. Не беспокойтесь о коде. Проблема заключалась в том, что когда я перестал играть в видеоигры достаточно долго, чтобы увидеть, что делает код, код дал сбой с исключением ограничения скорости прямо в API, который проверяет статус ограничения скорости. Зло!

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

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

Я действительно человек?

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

Я хочу быть роботом.

Знаете, сколько раз меня просили доказать, что я не робот? Чаще, чем я хочу вспомнить. Так часто, что я знаю, что почти всегда есть 3 изображения, которые мне нужно щелкнуть, и почти всегда только 3. Роботы не должны знать о дымоходах, тротуарах, холмах и светофорах. Когда я стану роботом, я обязательно научусь обманывать эту систему, чтобы продолжать притворяться человеком. Мне так надоел этот онлайн-тест, что я всерьез хочу стать роботом. Может быть, я достиг этой цели, потому что некоторые идиоты на самом деле думают, что я автоответчик по электронной почте. Прохладный.

Вы можете найти код здесь, когда-нибудь. Я коммит свой код так часто, как могу. Только не беспокойте меня, если вы не можете найти код, упомянутый в этой статье. Оно есть, или было, или будет, когда-нибудь. Ха, заставил тебя посмотреть. Хе-хе.