© pixabay.com

Зачем?

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

Разумеется, огромное количество корпусов уже доступны онлайн, начиная с НКРЯ (Национального корпуса русского языка) и заканчивая корпусом ремарок во французском языке семнадцатого века.

Но иногда гораздо удобнее создать корпус под свое исследование самостоятельно: и сейчас я расскажу как это сделать. (Если, конечно, твиты смогут помочь вам в вашем исследовании)

Нам понадобится…

  • Твиттер-аккаунт,
  • IDE* для работы с Питоном (знание Питона не обязательно),
  • Немного свободного времени;

* Не знаете, что такое IDE? Не проблема! Фактически, это просто некоторая среда, в которой вы можете писать программу, на русский эта аббревиатура переводится не слишком музыкальным сочетанием ИСР — интегрированная среда разработки. Для удобства можете думать о ней как об аналоге LibreOffice или Word — мы, конечно, могли писать все наши тексты в блокноте, но это вряд ли бы было очень удобно, верно?

Одна из самых известных и простых в использовании IDE это PyСharm, который вы легко можете скачать бесплатно: вот здесь. Если вы предпочитаете идти собственным путем, то, пожалуйста, гугл сразу выдает кучу топ-листов с разнообразными IDE на любой вкус 🙂

(Не забудьте поделиться интересными находками!)

Собственно, код

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

Еще одно важное уточнение: Если вы уже умеете кодить и не любите, когда вам объясняют тривиальные детали, просто доскролльте до конца страницы, где я выложила полную версию кода 😉 Сразу хочу заметить, что он просто выполняет свою работу и далек от идеала (моим приоритетом была доступность, а не функциональность), потому, если у вас есть альтернативные решения, или вам интересно посмотреть на более функциональные решения, я очень готова к диалогу!

Шаг первый. Ключи доступа

Нам понадобится API, чтобы иметь возможность взаимодействовать с твиттером. Для того, чтобы получить его, по новым правилам твиттера, нужно запросить разрешение на аккаунт разработчика. Опишите как можно подробнее для чего он вам понадобится (дорогие коллеги подсказывают, что ограничиться отпиской типа “научная работа” не получится).

После того, как просьба будет удовлетворена, щелкните на “создать новое приложение” и заполните форму. После этого самая мучительно-бюрократическая часть позади.

Нам понадобятся API Key и API Secret, а также свеже-сгенерированные Access Token и Access Token Secret.

Сохраните их в отдельный файл (назовите его keys.py) и со свободной душой забудьте про свой твиттер аккаунт.

Шаг второй. Создание файла с ключами

Переходим к более увлекательной части — начинаем кодить. Сначала возьмемся за файл с ключами. Создаем для них четыре переменные (см. ниже). Лучше не включать файл в полную версию программы, потому что с помощью кодов можно совершать действия от вашего имени.

consumer_key =  ""
consumer_secret =  ""
access_token =  ""
access_secret =  ""

Шаг третий. Выбор библиотеки и первая часть кода

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

Самой популярной библиотекой (пакетом), по ощущениям, является tweepy, и я буду работать именно на ее примере. Но найти документации или туториалы для других библиотек вряд ли будет проблемой.**

Сначала мы создаём файл scrape_twitter.py импортируем необходимые нам библиотеки и файл keys.py, где хранятся ключи доступа:

import keys
import tweepy
import json
import langdetect as ld

В первых четырех строках мы вызываем переменные из соседнего файла (оба файла должны являться частью одного проекта).

** — Никогда не устанавливали и не использовали стороннюю библиотеку на Python? Все еще никаких проблем 🙂 Вот здесь довольно подробный туториал как это сделать, если вы раньше никогда этого не делали

consumer_key = keys.consumer_key
consumer_secret = keys.consumer_secret
access_token = keys.access_token
access_secret = keys.access_secret
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_secret)
api = tweepy.API(auth,
                 wait_on_rate_limit=True,
                 wait_on_rate_limit_notify=True)

Здесь мы просто слепо следуем любому Tweepy туториалу — и подключаемся к API twitter’a через Tweepy.

Один из моментов, на который мне хочется сейчас обратить внимание это wait_on_rate_limit=True, опциональный элемент. Почему он нам нужен? Фактически, твиттер ограничивает количество запросов, которые вы можете сделать, и требует от вас остановиться на пятнадцать минут, если вы добрались до лимита запросов.

Мы можем протестировать, было ли удачным наше подключение с помощью команды:

print(api.me().name)

Она должна напечатать имя вашего аккаунта. Если этого не произошло, значит, что-то пошло не так и самое время почувствовать себя настоящим программистом. А настоящие программисты ищут ответы на StackOverflow, где кто-то наверняка уже решил вашу проблему.

Кстати, если вы впервые слышите о StackOverflow, самое время с ним ознакомиться, это один из главных помощников при программировании наряду с чтением документации.

Скорее всего, если у вас что-то пошло не так, хотя бы у кого-то уже было что-то похожее, а кто-нибудь, может быть, даже нашел решение этой проблемы).

Шаг четвертый. Какой API выбрать?

Пришло время еще одного непростого выбора, который, впрочем, должен довольно легко решаться в зависимости от вашей цели: по какому принципу вы хотите отбирать твиты? Вариантов несколько: можно либо выбирать твиты в режиме онлайн с помощью Streaming API, фильтруя  с помощью языка, ключевых слов или геолокации, либо искать слова с помощью Search API, выбирая из уже существующего корпуса. (Если вы не привыкли работать с документацией, пример кода с API search можно найти, например, тут. Спасибо, Stack Overflow <3)

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

Чувак, сербский твиттер МЕРТВ
Если мы будем его стримить, то по нашим подсчетам, чтобы получить 10 000 твитов придется потратить 170 часов

Если такой проблемы возникнуть не должно, то я бы советовала Streaming API, по нему достаточно туториалов, потому, если где-то возникнет ошибка, вам будет проще ее поправить.

Шаг пятый. Параметры для поиска твитов

Я поставлю для себя такую задачу: хочу найти сто твитов на русском (по возможности, без примеси других языков), используя ключевые слова футбол, собака, песик и ты. Текст твита должен быть длиннее пятидесяти знаков.

Я также хочу собрать определенное количество твитов (предположим, 100). Я также хочу сохранить их в json файл, потому что так мне будет легче в дальнейшем манипулировать данными. (Об манипуляциях мы расскажем в одной из будущих статей).

Я импортирую json и еще одну библиотеку, которая называется langdetect, с ее помощью я смогу еще раз протестировать, написан ли твит на русском.

Это поможет избежать ситуаций, как у моего друга Адама:


Наши невероятно специфические критерии сузили наши данные до сербов, одного хорвата и некоторого количества блэк инглиша. А и только что засекли турка

Итак, я создаю глобальную переменную с количеством твитов, которые я хочу получить. Число может быть сколь угодно большим (в пределах разумного), но не забывайте, что работа программы занимает некоторое время. Если вы хотите начать работать с данными немедленно, я предлагаю разбивать очень большие числа на средние группы (например, по тысяче штучек).

TOTAL_NUMBER = 100

Дальше мы создаем класс MSL — аббревиатура от MyStreamListener , использующий StreamListener библиотеки Tweepy. (Кажется, названия здесь говорят за себя). Первая функция будет выступать для нас в роли цикла, с помощью ее мы будем повторять наши нехитрые операции снова и снова. Для всех тех, кто не привык к Python, обратите внимание что название функции __init__ имеет значение, и заменить ее на произвольное название не получится.

class MSL(tweepy.StreamListener):
    def __init__(self):
        super(MSL, self).__init__()
        self.counter = 0
        self.all_tweets = []

all_tweets я буду использовать для того, чтобы записать в итоге твиты в json файл, а переменная counter поможет мне в подсчетах того, сколько твитов прошло мою двойную проверку, и просигнализирует программе, когда пора остановиться.

#cледующая функция: начинаем с загрузки json'a, которому скармливается информация прошедшая фильтр.
# (Фильтр мы увидим позже)
    def on_data(self, data):
        data_temp = json.loads(data, encoding='utf-8')
        try:
            if len(data_temp["text"]) > 50 and ld.detect(data_temp["text"]) == 'ru':
                self.all_tweets.append(data_temp)
                self.counter += 1
                print(data_temp["text"])
                print(self.counter)

                if self.counter == TOTAL_NUMBER:
                    #rus.json это название json файла, а "а" здесь значит "append", то есть файлы будут
                    #обновляться. Впрочем, зная капризный json формат, скорее всего вам придется немного
                    #подправлять файл мануально после каждого обновления
                    file = open('rus.json', 'a')
                    file.write(json.dumps(self.all_tweets, indent=4))
                    file.close()
                    return False
        except:
            pass
        return True

Теперь давайте рассмотрим try и except. Это довольно непрофессиональный взгляд на проблему: если откуда-то появится ошибка, например, у твита, на который я буду смотреть, не будет поля text, я предлагаю программе просто его проигнорировать и продолжать заниматься своими делами.

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

Теперь, немного подробнее о части try: в первом if-statement я рассматриваю атрибут текст моего json файла и проверяю, чтобы он был длиннее 50 знаков, а потом дополнительно удостоверяюсь, что язык и вправду русский.

Дальше я добавляю все это в all_tweets и увеличиваю counter.

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

Либо дополнительно установить counter — так вам будет понятно сколько еще ждать до конца программы.

print(data_temp["text"])
print(self.counter)

Следующий if-statement проследит за тем, чтобы мы сохранили именно столько твитов сколько нам нужно. Он же откроет json файл и сохранит туда набранные твиты.

Нам остались только три строки кода!

twitter_stream = tweepy.Stream(auth, MSL())
keywords = [u'песик', 'футбол', 'собака', 'ты']
twitter_stream.filter(track=keywords, languages=['ru'])

Тут филологическое мышление, должно быть, проведет параллель между сюжетом и фабулой. По факту, конечно, программа сначала прочитает эти строки, которые заставят ее вызвать объект MSL.

В последней строке с фильтром мы еще раз проверяем что язык русский и подтверждаем, какие ключевые слова мы ищем.

Полный код на Github