Рубрики
Без рубрики

Анализируя комментарии Reddit с помощью Python

В этом посте мы посмотрим, как построить простой сценарий Python для анализа слов. Мы будем … помечены с Python, WebDev, учебником, программированием.

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

Обзор

Между моим рабочим и боковыми проектами, я обычно проводимую большую часть моего временных веб-приложений, используя React и Node. Это означает, что написание почти исключительно JavaScript. Чтобы сохранить свою перспективу на программировании свежей и не ограничиваться одним языком, я хотел занять немного времени, чтобы выйти из мира JavaScript и изучить мир другого языка программирования. Я решил придумать небольшую идею проекта и построить его с Python. Python – это мощный, но дружественный язык программирования, который популярен с новичками и опытными программистами. Он был создан в 1991 году Гвидо Ван Россомом, но продолжает расти по популярности почти 30 лет спустя. В 2020 году Python был в верхней части списка для языков потребностей для рабочих заданий по программированию. Это также считалось Wired Magazine, чтобы быть более популярны, чем когда-либо прежде Отказ Проведя только короткий срок написания кода с Python, не усердно видеть, почему это популярный выбор. Давайте прыгнем.

Настройка

Этот пост предполагает, что у вас есть базовые знания программирования и что у вас установлен Python 3. Для получения более подробной информации о установке Python Начните здесь Отказ РЕПО для этого проекта можно найти здесь Отказ Вы заметите .py Файл, содержащий полный скрипт, а также .ipynb Файл, содержащий ноутбук Jupyter для скрипта. Jupyter ноутбук Это веб-приложение с открытым исходным кодом, которое позволяет создавать и обмениваться документами, которые содержат в прямом эфире, уравнения, визуализации и повествовательный текст, что может облегчить следовать и узнать, как работает сценарий Python.

Сценарий

Сценарий во всей полноте можно найти здесь Отказ

Первое, что нам нужно сделать, это импортировать библиотеку запросов. Это то, что мы будем использовать, чтобы сделать HTTP-запрос REDDIT, чтобы получить данные комментариев из поста Reddit. После этого мы инициализируем несколько глобальных переменных. Мы будем использовать эти глобальные переменные, чтобы отслеживать данные по мере просмотра комментариев. comment_count это целое число и отслеживает количество комментариев, которые мы разбираем, comment_array это массив и проведет реальный комментарий строки, а more_comment_ids Это еще один массив, который удержит id-строки, которые нам понадобится, чтобы получить дополнительные комментарии, которые не возвращаются в первоначальную полезную нагрузку (обычно встречаются в постах со многими комментариями).

# imports
import requests  # The requests library for HTTP requests in Python

# globals
comment_count = 0
comment_array = []
more_comment_ids = []

Далее нам нужно получить данные для поста Reddit. Для этого мы можем добавить .json. до конца любого реддита пост URL.

Пример будет: https://www.reddit.com/r/redditdev/comments/krolrb/multicomments.json Отказ

То, что мы вернемся, это JSON, у которого будет базовый формат, который выглядит так:

{
    "kind": "Listing",
    "data": {
        "children": [
            "kind": "t1",
            "data": {
                "body": "",
                "replies": ""
            }
        ]
    }
}

Почта Reddit называется « листинг ». Списки могут содержать многие виды детей. Ребенок с добрый T1 Указывает, что ребенок представляет комментарий. В комментариях данные Свойство, среди многих других свойств, текст комментариев можно найти на Тело Собственность, наряду с любыми возможными ответами, которые находятся на отвечает свойство. Ответы структурированы так же, как комментарии. Они содержат детей, а у детей есть добрый и данные характеристики. В каждом ответе на комментарий мы можем увидеть другой ответ на этот ответ комментариев. Каждый из них содержит свои идентично отформатированные дети. Таким образом, чтобы проанализировать все комментарии в теме, мы должны рекурсивно просеять через все комментарии и ответы.

Если нужно следовать каждому человеку, дерево комментариев рекурсивно к его концу не было достаточно сложно, есть еще одна проблема, о которой мы должны беспокоиться. Поскольку резьбы комментариев могут стать довольно долго, а не каждый комментарий всегда отображается на начальной нагрузке на резьбу. Когда это произойдет, Reddit показывает кнопки «Загрузить больше ответов» в потоках. Итак, как мы тоже получим? Чтобы справиться с этими случаями, API доставит ребенка с добрый Значение свойств Больше Отказ

{
    "kind": "more",
    "data": {
        "count": 2,
        "name": "t1_ghp1m6v",
        "id": "ghp1m6v",
        "parent_id": "t1_ghozojl",
        "depth": 2,
        "children": [
            "ghp1m6v"
        ]
    }
}

Массив дети в пределах Больше Объект будет содержать список идентификаторов потоков, которые можно использовать для получения дополнительных комментариев. В приведенном выше примере кода есть только один ребенок, GHP1M6V Отказ Таким образом, в дополнение к разбору всех комментариев деревьев рекурсивно, нам также придется собирать какие-либо дополнительные комментарии идентификаторы потоков, а затем сделать то же самое для тех.

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

Первая функция, которую мы напишем, это parse_children_for_comments. .

def parse_children_for_comments(children):
    global comment_count
    global comment_array
    for child in children:
        if child['kind'] == "more":
            children = child['data']['children']
            for id in children:
                more_comment_ids.append(id)
        if child['kind'] == "t1":
            comment_count += 1
            comment_array.append(child['data']['body'])
            get_replies(child['data'])

Это займет массив дети Объекты, которые отправляются обратно в данные ответа и вытащит текст комментариев, который найден в Тело свойство. Для каждого ребенка в аргументе массива дети мы проверим его добрый Отказ Если добрый это Больше Мы будем перебраться и добавить каждый идентификатор в глобальный массив, который мы создали, more_comment_ids Отказ В конечном итоге вернусь к этому массиву IDS и проанализируем через него.

Далее, если добрый это T1 Это означает, что у нас есть комментарий, и мы хотим прочитать его текст. Для этого мы просто получим текст с Ребенок [«Данные»] [«Тело»] и добавить его в наш глобальный comment_array переменная.

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

def get_replies(comment):
    global comment_count
    if comment['replies'] != "":
        children = comment['replies']['data']['children']
        parse_children_for_comments(children)

Во-первых, мы проверяем, есть ли ответы. Когда нет ответов, отвечает Свойство будет пустой строкой. Если строка не пуста, мы знаем, что у нас есть ответ. Как я уже упоминал выше, ответы получают один и тот же формат, что и оригинальный комментарий, на который он отвечает. Таким образом, чтобы разбирать текст ответа, мы можем повторно использовать то же самое parse_children_for_comments. Функция мы уже написали. С parse_children_for_comments снова позвонит get_replies и get_replies снова позвонит parse_children_for_comments Пока нет никаких комментариев, не осталось, это рекурсивно продолжит, пока мы не достигнем дочернего комментария с пустым отвечает свойство. Довольно аккуратно

С помощью этих функций помощника определены, мы готовы получить наши данные. Для этого мы будем использовать встроенный Python функцию под названием вход Что позволит пользователю ввести URL-адрес на пост Reddit.

# get url from user
print('enter the reddit post url (e.g. https://www.reddit.com/r/redditdev/comments/krolrb/multicomments/):')
thread_url = input()

Мы можем ожидать, что пользователь вставляется в URL для поста Reddit. Например, это может выглядеть что-то подобное: https://www.reddit.com/r/redditdev/comments/krolrb/multicomments/

Чтобы получить данные посту, нам нужно повернуть https://www.reddit.com/r/redditdev/comments/krolrb/multicomments/ в https://www.reddit.com/r/redditdev/comments/krolrb/multicomments.json Отказ

Для этого мы можем написать небольшую функцию помощника.

def sanitize_input(url):
    last_char = url[-1]
    if last_char == '/':
        url = url[:-1]
    url = f'{url}.json'
    return url

Мы передаем URL в качестве аргумента в функцию. Функция проверяет, если последний символ URL это / и удаляет его, если это так. Тогда функция добавляет .json до конца урл . После того, как мы пройдем введенный пользователем URL-адрес этой функции, мы готовы получить данные пост.

# pass user's url to sanitize helper
sanitized_thread_url = sanitize_input(thread_url)

# make network call
req_data = requests.get(sanitized_thread_url, headers={'User-agent': 'adamgoth.com'})

if req_data.status_code != 200:
    print('request failed')
    print(req_data.json())

if req_data.status_code == 200:
    json_data = req_data.json()
    for item in json_data:
        children = item['data']['children']
        parse_children_for_comments(children)

Мы называем requests.get () Передача наш URL в качестве первого параметра, а также значение заголовков для второго параметра. Причина, по которой нам нужно указать Пользовательский агент Свойство в заголовке – это так, что у нас есть уникальная идентичность Reddit. Это гарантирует, что мы появились полностью анонимными и бегите в ограничение скорости вопросы.

Как только у нас есть наши данные в нашем req_data Переменная, первое, что мы проверим, если мы не получили 200 ответ по любой причине. Если ответ не 200 Мы распечатаем ошибку.

Предполагая, что мы получаем 200 Затем мы можем начать разбирать данные. Мы можем использовать библиотеку запросов, встроенный JSON Decoder и называется .json () на ответ. Затем мы написали простое для Заявление, которое берет каждого ребенка в данные ответа и передает его на parse_children_for_comments. Мы ранее обсудили.

После для Петля из линии 13 завершает, у нас должно быть несколько комментариев, хранящихся в нашем глобальном comment_array Отказ Кроме того, в зависимости от количества комментариев с поста мы, возможно, нашли некоторые дополнительные идентификаторы комментариев и сохраняют их в нашем глобальном more_comment_ids множество. В качестве напоминания это идентификаторы, которые мы можем использовать для получения дополнительных комментариев, которые не отображались в начальной нагрузке. В UI REDDIT они представляют ссылки в теме комментариев, которые отображаются как «загружать больше ответов», а в нашем ответе на данные эти идентификаторы приходят от детей, которые имеют добрый Значение свойств Больше Отказ

URL для получения дополнительных данных дополнительных комментариев выглядит аналогично URL-адресу, которое мы использовали для получения исходных данных пост. Единственное отличие – это идентификатор комментариев прилагается к концу. Итак, https://www.reddit.com/r/redditdev/comments/krolrb/multicomments.json становится https://www.reddit.com/r/redditdev/comments/krolrb/multicomments/(Comment_ID ).json. . Мы можем написать простой помощник, чтобы сделать это для нас.

def create_thread_url(comment_id):
    return sanitized_thread_url.replace('.json', f'/{comment_id}.json')

Мы просто проходим comment_id как аргумент, а затем сделайте строку заменить на .json. с /(comment_id ust.json. .

Затем мы готовы сделать запросы на дополнительные комментарии.

# handle extra comment ids
for id in more_comment_ids:
    req_data = requests.get(create_thread_url(
        id), headers={'User-agent': 'adamgoth.com'})
    if req_data.status_code != 200:
        print('request failed')
        print(req_data.json())

    if req_data.status_code == 200:
        json_data = req_data.json()
        for item in json_data:
            children = item['data']['children']
            parse_children_for_comments(children)

Чтобы получить дополнительные комментарии, мы будем использовать другой для петля к цикле через каждый идентификатор в more_comment_ids множество. Для каждого, мы снова используем requests.get () Передача идентификатор комментариев к create_thread_url Функция мы только что написали вместе с тем же Пользовательский агент заголовок как наш предыдущий запрос. После того, как у нас будет наш ответ, мы снова проверяем код состояния, и если это успешно, мы разбираем данные так же, как мы сделали ранее, передавая каждого ребенка в данные для parse_children_for_comments. . Как слово осторожности, для постов с тысячами комментариев ответов, это может привести к большому количеству дополнительных идентификаторов комментариев. Можно иметь сотни идентификаторов для получения. Каждый из них потребует синхронного сетевого вызова, поэтому он может занять некоторое время, если это так.

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

def analyze_words(words):
    analysis_string = words.split(' ')
    word_dict = {}
    for word in analysis_string:
        cleaned_word = word.replace('.', '').replace("'", '').replace(
            '\n', '').replace(',', '').replace("'", '').lower()
        if cleaned_word not in word_dict:
            word_dict[cleaned_word] = 1
        else:
            word_dict[cleaned_word] += 1

    return word_dict

Функция принимает одну строку как аргумент под названием слова Отказ Затем он нарушает струну в массив слов, называемых Analue_String Разделение струны на каждом пространстве, найденном в строке. Мы создаем пустую Словарь называется Word_dict Что мы будем использовать, чтобы отслеживать внешний вид каждого слова. Затем мы петлю через каждое слово в нашем Analue_String множество. Для каждого слова мы используем строку, заменяет для разложения различных общих специальных символов (запятых, периодов и т. Д.), а затем звонить .ниже() На нем, чтобы преобразовать все прописные буквы в строчные буквы. Это гарантирует, что и не отслеживаются как два разных слова. Когда мы проходим через каждое слово в массиве, если слово не существует в нашем Word_dict Словарь пока мы добавим его и дам ему значение 1 Отказ Если это уже существует в Word_dict Тогда мы просто увеличиваем значение подсчета на 1. Когда мы закончили зацикливаться через каждое слово, мы вернем Word_dict Мы создали.

Конец сценария выглядит следующим образом:

comment_string = ' '.join(comment_array)
results = analyze_words(comment_string)

sorted = sorted(results.items(), key=lambda x: x[1], reverse=True)

print(f'{comment_count} comments analyzed')

for key in sorted:
    print(key)

После объединения всех комментариев в одну строку и передачу этой строки через анализировать_words Мы можем отсортировать все результаты по количеству внешних видов, считающихся, позвонив отсортировано (результаты. Итоги (), х: х [1],) Отказ Затем мы можем распечатать общее количество комментариев, которые мы проанализированы, а затем каждое слово и количество раз появилось.

Упаковка

Сценарий во всей полноте можно найти здесь Отказ Чтобы запустить скрипт, просто запустите Python Reddit-comment-analysiz.py из каталога, содержащего файл сценария.

Если у вас установлены ноутбуки Jupyter, можно найти более интерактивную версию этого поста здесь Отказ

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

  • Входная проверка
  • Варианты обращения с верхним и нижним корпусом
  • Варианты удаления специальных символов
  • Варианты для удаления общих слов (и, I и т. Д.)

Если вам понравился этот пост или нашел его полезным, пожалуйста, рассмотрите возможность Разделить его в Twitter Отказ

Если вы хотите остановиться в обновлении на новых сообщениях, Следуй за мной в Twitter Отказ

Если у вас есть какие-либо вопросы, комментарии или просто хотите сказать привет, Пришлите мне сообщение Отказ

Спасибо за чтение!

Оригинал: “https://dev.to/adamgoth/analyzing-reddit-comments-using-python-4772”