отправлено из My Twitter Tweet Client.

Это вторая часть моего личного проекта, который включает в себя разработку клиента Twitter, который позволяет мне только твитить. Чтобы прочитать часть 1, нажмите здесь.

Прежде чем мы начнем с сегодняшнего журнала, давайте рассмотрим несколько вещей, которые мы сделали вчера:

  • Создал мозговой штурм «Twitter Client» с использованием модели разработки Waterfall.
  • Создал приложение на странице разработчика в Твиттере.
  • Сгенерированные токены API и потребительские ключи
  • Создал файл JSON с помощью скрипта Python.

Итак, наша повестка дня на сегодня — закончить программу и отправить мой первый твит из Twitter-клиента! Для этого мне нужно ответить на один важный вопрос: как отправить POST-запрос в Twitter с помощью Python?

HTTP- и POST-запрос

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

Короче говоря, HTTP (протокол передачи гипертекста) — это используемый протокол, который позволяет обмениваться данными между клиентом и сервером с использованием протокола запрос-ответ. Чтобы запросить ответ от сервера, в основном есть два метода:

  • GET: метод GET используется для запроса данных с сервера.
  • POST: метод POST используется для отправки данных на сервер.

Чтобы сделать этот HTTP-запрос в Python, мы можем использовать несколько библиотек, таких как httplib, urllib (помните эту форму API ISS?) и requests. По-видимому, самая элегантная и простая библиотека — это requests, поэтому мы будем использовать ее для отправки метода POST из нашего приложения Python.

Прежде чем писать код, убедитесь, что вы скачали библиотеку с помощью pip install requests.

Теперь в библиотеке requests есть метод post(), который позволяет нам отправлять запрос POST. Его синтаксис следующий:

requests.post(url, params={key: value}, args)

POST API Твиттера

Чтобы дополнить наши знания о том, как работает метод POST в Python, давайте посмотрим, что Twitter приготовил для нас, разработчиков. Быстрый поиск в их документации говорит нам, что способ твитнуть сообщение о статусе — через POST statuses/update — это обновит текущий статус аутентифицирующего пользователя, также известный как Tweeting.

При использовании этого API есть два предостережения, несоблюдение которых приведет к ошибке. Во-первых, твит, который будет опубликован, не должен быть дубликатом предыдущих твитов пользователя. В противном случае сервер выдаст ошибку 403. Это хороший шаг, чтобы удалить потенциальный спам. Во-вторых, существует ограничение, до которого пользователь может отправлять твиты с помощью этого API: 300 твитов каждые 3 часа. Если пользователь попытается твитнуть, даже если его лимит достигнут, сервер выдаст ошибку 403.

URL-адрес ресурса: https://api.twitter.com/1.1/statuses/update.json . Это будет URL-адрес, который я должен указать в своем методе requests.post(). Формат ответа должен быть представлен в формате JSON, а это означает, что мне придется преобразовать словарь в JSON — то, что я когда-либо делал в предыдущих проектах.

Вот пример запроса:

curl -XPOST 
  --url 'https://api.twitter.com/1.1/statuses/update.json?status=hello' 
  --header 'authorization: OAuth
  oauth_consumer_key="oauth_customer_key",
  oauth_nonce="generated_oauth_nonce",
  oauth_signature="generated_oauth_signature",
  oauth_signature_method="HMAC-SHA1",
  oauth_timestamp="generated_timestamp",
  oauth_token="oauth_token",
  oauth_version="1.0"'

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

Из их списка параметров единственный параметр, который я заполню, — это status, так как я не буду отвечать ни на какие твиты, отправлять анимированные GIF-файлы или сообщать свое местоположение с твитом.

В их примере запроса в документации упоминалось следующее

Чтобы получить сгенерированные oauth_nonce, oauth_token и oauth_signature, вы можете использовать инструмент REST, например Insomnia или Postman.

Поскольку это включает слово «oauth», которое является открытым стандартом для делегирования доступа (поскольку, опять же, это клиентское приложение Twitter будет делегатом при отправке моего твита в качестве альтернативы тому, что мне нужно сначала войти на веб-сайт Twitter), я считаю, что мне следует решить проблему аутентификации заранее.

Аутентификация API Твиттера

В целях аутентификации Twitter использует следующее: OAuth 1.0a и OAuth2.0 Bearer Token. Аутентификация OAuth 1.0a обычно используется для делегирования пользователя приложению; это означает, что приложение будет делать запросы API от имени учетной записи Twitter. С другой стороны, токен носителя OAuth 2.0 — это токен, предназначенный только для приложения, то есть в нем не участвуют пользователи. Эта аутентификация обычно используется разработчиками, которым нужен доступ только для чтения к общедоступной информации Twitter. Для этого приложения мы будем использовать OAuth 1.0a.

ОАут 1.0а

Многие конечные точки в разработке Twitter используют метод OAuth 1.0a для выполнения запроса API от имени пользователя. Таким образом, если разработчик зарегистрировал приложение Twitter, это приложение может делать запросы к API от имени любых пользователей Twitter, которые уже разрешили соответствующую аутентификацию в приложении.

Используя OAuth 1.0a, приложение должно будет подписывать каждый запрос API несколькими сгенерированными ключами и токенами в заголовке авторизации. Этими ключами и токенами являются oauth_consumer_key, oauth_consumer_secret, oauth_token и oauth_token_secret. Как упоминалось в предыдущей статье, ключ/секрет потребителя используется для проверки приложения разработчика Twitter при отправке запроса API, тогда как ключ/секрет маркера используется для конкретного пользователя; он подтверждает мое заявление в качестве делегата для моей учетной записи Twitter.

Выполнение запросов от имени пользователей

При создании подписи вам понадобится набор токенов доступа, представляющих пользователя, от имени которого вы собираетесь делать запрос. Вы можете создать набор токенов доступа, который представляет учетную запись Twitter, которой принадлежит приложение разработчика Twitter, на странице сведений о приложении, но если вы хотите сделать запрос от имени другой учетной записи Twitter, владелец этой учетной записи должен предоставить вам доступ. войдя в свою учетную запись в рамках 3-этапного потока OAuth. Результатом этого процесса является набор токенов доступа (oauth_token и oauth_token_secret), которые можно использовать для выполнения запроса OAuth 1.0a.

Получив эти ключи и токены, вы можете либо создать подпись с нуля, либо использовать инструмент — Postman или Insomnia — для отправки запроса конечной точке, для которой требуется OAuth 1.0a.

Поскольку этот проект станет ступенькой в ​​моей карьере разработчика программного обеспечения, я выберу создание подписи с нуля. Более того, поскольку я не собираюсь делать запрос от имени другой учетной записи Twitter, я не буду проходить трехсторонний поток OAuth.

Кроме того, обратите внимание, что токены доступа пользователей являются конфиденциальными и должны очень тщательно охраняться. Когда токены доступа генерируются, пользователь, которого они представляют, доверяет вашему приложению, чтобы обеспечить их безопасность. Если безопасность как ключей API, так и токенов доступа пользователя будет нарушена, приложение потенциально может раскрыть доступ к личной информации и функциям учетной записи. На самом деле, есть прибыльные боты, которые удаляют репозитории GitHub за любые случайные коммиты чьих-то ключей API — страшно!

Создание подписи (с нуля)

В этом разделе рассказывается, как создать подпись OAuth 1.0a HMAC-SHA1 для HTTP-запроса. Эта подпись подойдет для передачи в Twitter API в составе авторизованного запроса. Конечным результатом создания подписи является ключ oauth_signature.

Например, необработанный запрос будет выглядеть следующим образом:

POST /1.1/statuses/update.json?include_entities=true HTTP/1.1
Accept: */*
Connection: close
User-Agent: OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Content-Length: 76
Host: api.twitter.com
status=Hello%20World

Чтобы создать подпись, начните с определения метода HTTP и URL-адреса. В большинстве случаев метод HTTP будет либо POST, либо GET. Поскольку мы, по сути, отправляем запрос твита на сервер Твиттера, мы собираемся использовать метод POST.

Для URL-адреса убедитесь, что он находится в своей базовой форме — не должно быть никаких строк запроса или хэш-параметров. Также убедитесь, что часть URL-адреса https:// соответствует URL-адресу запроса, отправленного через API. В моем случае я буду использовать https://api.twitter.com/statuses/update.json.

Затем соберите все параметры, включенные в запрос. Эти параметры могут быть либо частью URL-адреса в виде строки запроса, либо в виде тела запроса. Поскольку я использую POST-статусы Twitter/update.json, единственный запрос, который я отправлю, — status.

В дополнение к параметрам запроса в подпись необходимо включить каждый параметр oauth_* . Для изготовления ключа oauth_signature требуется oauth_*:

  • oauth_consumer_key определяет, какое приложение делает запрос. Это эквивалентно потребительскому ключу API, который можно получить на портале разработчиков Twitter.
  • oauth_nonce — это уникальный токен, который ваше приложение должно генерировать для каждого уникального запроса. Twitter будет использовать это значение, чтобы определить, был ли запрос отправлен несколько раз. Значение этого запроса генерируется путем base64 кодирования 32 байтов случайных данных и удаления всех символов, отличных от слов, но любой подход, который создает относительно случайную буквенно-цифровую строку, должен будь в порядке здесь.
  • oauth_signature_method, используемый Твиттером, — HMAC-SHA1. Это значение должно использоваться для любого авторизованного запроса, отправляемого в API Twitter.
  • oauth_timestamp указывает, когда был создан запрос. Это значение должно быть количеством секунд, прошедших с момента создания запроса в эпоху Unix, и должно легко генерироваться в большинстве языков программирования.
  • oauth_token представляет собой разрешение пользователя на совместное использование доступа к своей учетной записи с вашим приложением. Это даст приложению соответствующие разрешения делегировать пользователю публикацию его твитов. Этот токен можно найти на портале разработчиков Twitter.
  • oauth_version всегда должно быть равно 1.0 для любого запроса, отправляемого в API Twitter.

До сих пор я понимал, что было бы несколько ненужно пытаться заново изобретать все колесо, особенно когда речь идет о таких вещах, как шифрование base64 или манипулирование строками токена. Риски слишком высоки, и я должен найти другую альтернативу, которая не будет слишком упрощенной, но при этом я смогу хорошо понять и понять поток, стоящий за API Twitter.

Пробуем с request библиотекой Python

Вместо того, чтобы изобретать велосипед, позвольте мне попробовать другой подход. Согласно этому веб-сайту Stack Overflow, пользователь рекомендовал использовать библиотеку request вместо того, чтобы изобретать велосипед с помощью hmac — он утверждал, что создание правильной базовой строки OAuth и тому подобное не стоит головной боли, когда есть гораздо более простой метод. Используя request, этот пользователь смог заставить API работать. Ниже приведен его код:

import request
from request_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_crendentials.json'
auth = OAuth1('APP_KEY', 'APP_SECRET', 'USEROAUTH_TOKEN', 'USER_SECRET')
requests.get(url, auth=auth)

Теперь, из его кода выше, я заметил, что его метод requests.get() включает параметр с именем auth. Сначала я не придал этому подходу особого значения, так как не знал, что requests.post() также имеет параметр auth . Тем не менее, просмотрев веб-страницу с подробными параметрами requests.post(), я в какой-то мере уверился, что могу получить авторизацию через API.

Готовлю мой код

Прежде чем я начну, позвольте мне сказать вам, что это азартная игра — думать, что описанный выше метод сработает. На самом деле, пока я отлаживал код из-за какой-то невидимой ошибки, я думал, что запустить аутентификацию через auth не получится. Однако последовательность снова оказалась восторжествовавшей. Вот дневниковая запись о том, что я сделал для работы над этим проектом.

Узнайте, как импортировать файлы JSON

Вот сценарий: я уже создал подкаталог в своем основном каталоге проекта (следуя хорошей практике программирования Python), в котором будут храниться API и потребительские ключи/секреты. В предыдущей статье я назвал каталог auth, но изменил его на data, так как auth кажется более подходящим в качестве имени файла, а не имени каталога.

Думая о том, как импортировать файл JSON, я вспомнил свою предыдущую статью, в которой я написал описание того, как структурировать свой проект программирования. После просмотра я понял, что операторы from и import будут работать только для модулей и методов. Более того, это также означало, что мне нужно больше пересмотреть заявления об импорте.

Тем не менее оказывается, что есть несколько разных способов импортировать файл JSON. В частности, работа, связанная с файлом Python и JSON, обычно включает в себя написание словаря в Python и преобразование его в стандарт JSON или преобразование файла JSON в словарь Python.

Один из способов добиться этого — использовать команду with. Я использовал следующий фрагмент кода:

with open('data/twitter_credential.json') as json_file:
    auth_codes = json.load(json_file)

Приведенный выше код откроет файл twitter_credential.json и сохранит его сущность во временной переменнойjson_file. После этого я создал еще одну переменную с именем auth_codes, которая будет иметь значение json_file.

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

Аутентификация пользователя

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

В любом случае, как упоминалось ранее, важно, чтобы мои файлы JSON хранились в переменной. Это потому, что когда я открывал twitter_credential.json, не было основного имени для переменной, которое я мог бы использовать для вызова отдельного ключа и значений словаря. Итак, после инициализации переменной auth_codes я могу получить доступ к ключам и получить внутри них нужные значения — которые тоже являются ключами, только на этот раз API-ключами.

Для вызова аутентификации требуется библиотека request_oauthlib. Это позволит нам легко выполнять запросы аутентификации OAuth 1. Для этого документация запроса имеет следующую структуру:

import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/verify_credentials.json
auth = OAuth1('APP_KEY', 'APP_SECRET', 'OAUTH_TOKEN', 'OAUTH_SECRET')
request.get(url, auth=auth)

Приведенный выше код действительно полезен, так как он проливает свет на использование verify_credentials.json, но я объясню это позже.

Теперь важно отметить аргументы, которые требуются для OAuth1(). Там требуются APP_KEY, APP_SECRET, OAUTH_TOKEN и OAUTH_SECRET. Это точно такой же код, который мы смогли сгенерировать на нашей странице разработчика в Твиттере. Эти коды также являются тем же кодом, который был вставлен в файл twitter_credentials.json.

Таким образом, создав переменную auth_codes, я могу получить доступ к каждому из четырех ключей/секретов API и поместить их в соответствующие позиции. Это стало возможным благодаря тому, что auth_codes теперь представлены в виде словаря, где вызов его ключа покажет его значение. Итак, мой код выглядел примерно так:

auth = OAuth1(auth_codes['CONSUMER_KEY'], auth_codes['CONSUMER_SECRET'], auth_codes['ACCESS_TOKEN'], auth_codes['ACCESS_SECRET'])

Еще одна причина, по которой важно использовать auth_codes и ссылаться на словарь, созданный из файла twitter_credential.json, заключается в обеспечении безопасности. Запись фактической строки ключа и секрета в отдельный файл уменьшит вероятность случайной фиксации закрытых кодов в системе контроля версий, такой как GitHub или BitBucket. Кроме того, написание его вышеописанным способом просто делает код более сложным и чистым.

Отправка POST-запроса

После того, как мы закончили с кодом аутентификации и файлом JSON, пришло время отправить наш POST-запрос на API-серверы Twitter!

Как упоминалось выше, мы можем отправить запрос HTTP POST через Python, используя requests.post(), добавив при этом несколько других подходящих параметров. Поскольку это простое приложение, я добавлю только три параметра: url, params и auth.

Чтобы понять params, мне нужно сначала объяснить url. Из примера запроса Twitter я заметил, что сообщение о статусе публикуется в виде строки запроса:

curl -XPOST
  --url 'https://api.twitter.com/1.1/statuses/update.json?status=hello'

Помните, что строка запроса является частью URL-адреса, который присваивает значение определенным параметрам. В этом случае присваивается параметр status, которому будет присвоено значение hello.

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

К счастью, в документации requests есть раздел, который может решить эту проблему. Используя аргумент ключевого слова param, я могу передать значение переменной в строку запроса URL. Вот пример из документации:

payload = {'key1': 'value1', 'key2': 'value2'}
r = request.get('https://httpbin.org/get/', params=payload)
print(r.url)
>>> https://httpbin.org/get/key2=value2&key1=value1

Имея это в виду, если бы я хотел разделить строку запроса https://api.twitter.com/1.1/statuses/update.json?status=hello, мне пришлось бы использовать словарь ключа status и значения hello в переменной, которая будет помещена в аргумент params. Однако, чтобы убедиться, что у меня есть ввод для params, я создам еще одну переменную, которая будет спрашивать пользователя, что он хочет твитнуть. Код выглядит следующим образом:

ask_tweet = input("What's happening today? \n")
tweet = {'status': ask_tweet}
r = request.post(url, params = tweet, auth = auth)
print(r.status_code)

Первая переменная, ask_tweet, — это переменная, которая будет содержать значение ввода пользователя, когда его спросят: «Что сегодня происходит?» Затем это значение будет помещено в словарь с именем tweet, где первый ключ — status (необходимый параметр для публикации твита), а значение — ask_tweet, то есть твит, который мы запросили заранее.

Затем мы можем получить нашу функцию метода POST, где есть три аргумента: url, params и auth. Сам URL уже должен быть помещен в переменную с именем url, созданную в начале исходного кода; его значение должно быть https://api.twitter.com/1.1/statuses/update.json. Далее параметрами будет словарь tweet, у которого есть ключ status и значение ask_tweet. Наконец, параметр auth заполнит требования аутентификации. Над этим уже будет работать OAuth1(), и пользователю не следует беспокоиться о создании какого-либо причудливого ключа подписи OAuth или чего-то еще.

Наконец, у нас есть симпатичный тестер, который расскажет нам, как прошел наш код. Если print(r.status_code) возвращает значение 200, то все в порядке. Если нет, то с вашим кодом что-то не так и ваш твит не был отправлен.

Окончательный код

Окончательный код выглядит следующим образом:

Знаю, знаю. Замечаний к коду нет. Будьте уверены, я добавлю их позже.

А пока обратите внимание, насколько короток код. Если бы я включил числовые строки в Vim с помощью set number, весь основной исходный код составил бы только 15 строк кода. Тем не менее, за этими 15 строками кода стоит целый день исследований, тестирования, отладки и переходов между Vim и запуском файла Python.

Тем не менее, этот проект преподал мне множество уроков, которые окажутся очень ценными в ближайшем будущем. Прежде всего, это сложно для новичка. Не поймите меня неправильно, 15 строк кода не выглядят так уж много работы. На самом деле код написан настолько просто, что старшеклассник, вероятно, справится с ним до обеденного перерыва.

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

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

У меня также был личный опыт открытия новых границ в моем ремесле. После добрых 5 лет безделья с бессмысленным званием «начинающий программист» я наконец решил посвятить себя освоению искусства компьютерного программирования. И конечно же, как и в любом другом виде искусства, поначалу это будет тяжело. Люди на форумах используют множество ключевых слов, и вы почувствуете себя бесполезным, поскольку не можете создавать какие-либо материалы или контент. Но, как и в любом другом искусстве, как только вы преодолеете начальный этап и достигнете ясности ума от основных основ, вы почувствуете, как внутри течет творческая кровь. Ваша тяжелая работа привела к хорошему опыту, который позволяет вам видеть шаблон в документации и внутреннюю логику кода. Изучив основы, вы можете сосредоточиться на действительно важной части — творческом аспекте.

Тем не менее, для достижения этих целей вы должны стремиться только к одному: быть последовательным.