[Python, API] Сохраняем комментарии youtube в csv

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
02-Июл-2021 14:32

ПредысторияКак-то на одном youtube канале, устроили розыгрыш среди комментаторов.Условием было угадать цену закрытия акций хотя бы одной из трёх конкретных компаний.Мне стало интересно собрать статистику за какие цены пользователи голосовали больше других и сработал ли "коллективный разум", когда пусть каждый отдельно взятый голос был далёк от истины, но среднее значение голосов внезапно попадало в точку.Плюс задача сбора статистики усложнялась тем, что комментарии написаны в единое текстовое поле, а не в форму, где все значения на своих местах и нужного типа. Заспойлерю: распарсить комментарии оказалось самой трудоёмкой задачей, потому что не было даже единой схемы ответа (ок была превалирующая, но далеко не единственная):
  • одни писали сначала название компании потом цену, другие -- наоборот цену, потом компанию (кое-кто писал название, много вводных слов обосновывающих его мысли и потом цену)
  • Где-то было название, где-то тикер
  • Местами текст был на английском, местами на русском
  • Дробные части отделяли и точками и запятыми
  • Встречались и опечатки в несколько букв (в одном слове)
  • Многие сокращали название до двух букв, сочетание которых очевидно встречалась и в обычных комментариях, не связанных с конкурсом
  • Были и жаргонные названия компаний (Бумер вместо BMW), и наоборот полные (Bayerische Motoren Werke)
  • Но призёрами в борьбе с парсингом стали люди, писавшие в словах Tesla и BMW - русскую букву "т" и русскую "в" в начале слова, а дописывавшие его английскими буквами ​ (последнее заметил по чистой случайности просматривая json содержимое, когда латиница идёт буквами, а весь юникод заменяется слешами с кодами)
Для чего эта статья? Ведь есть платные парсеры / документация на Yotube APIВо-первых, не все хотят платить за то, что можно получить бесплатно.Во-вторых, не у всех хорошо с английским, да и зачем курить длинные мануалы, если задача узкая и односложная.В-третьих, никакой похожей инструкции на первой странице поиска не нашлось, думаю это нужно исправить.Чтобы инструкция была более гибкой и её можно было использовать, даже если что-то изменится в одном из шагов, в каждом оставлю источники.
  • Получите ключ для работы с youtube API (источник)
  • Получите библиотеку google-api-python-client (источник)
    • Возьмите python
    • Выполните командуpip.exe install google-api-python-client
  • Запустите скрипт
    • Возьмите редактор, например visual studio code
    • Создайте новый python файл и вставьте в него следующий кодВсё что вам нужно в нём поправить для старта это
      • DEVELOPER_KEY -- получен в первом пункте
      • VIDEO_ID -- берётся из ссылки на видео, например в ссылке https://youtu.be/9b4-G-czN5Y нас интересует 9b4-G-czN5Y
# -*- coding: utf-8 -*-
import os
import googleapiclient.discovery
import csv
DEVELOPER_KEY = "AIzaSyB7oKxIsd4690TibZHZPaTLzw7nqzgrr4k"
VIDEO_ID = "9b4-G-czN5Y"
# Функция для скачивания корневых комментариев
def youtube(nextPageToken=None):
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
    api_service_name = "youtube"
    api_version = "v3"
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, developerKey = DEVELOPER_KEY)
    request = youtube.commentThreads().list(
        part="id,snippet",
        maxResults=100,
        pageToken=nextPageToken,
        videoId=VIDEO_ID
    )
    response = request.execute()
    return response
# Функция для скачивания реплаев на комментарии
def youtubechild(NextParentId, nextPageToken=None):
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
    api_service_name = "youtube"
    api_version = "v3"
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, developerKey = DEVELOPER_KEY)
    request = youtube.comments().list(
        part="id,snippet",
        maxResults=100,
  pageToken=nextPageToken,
        parentId=NextParentId
    )
    response = request.execute()
    return response
# Главная функция
def main():
    # Скачиваем комментарии
    print('download comments')
    response = youtube()
    items = response.get("items")
    nextPageToken = response.get("nextPageToken") # скачивается порциями, на каждую следующую выдаётся указатель
    i=1
    while nextPageToken is not None:
        print(str(i*100)) # показываем какая сотня комментариев сейчас скачивается
        response = youtube(nextPageToken)
        nextPageToken = response.get("nextPageToken")
        items = items + response.get("items")
        i+=1
    print(len(items)) # Отображаем количество скачаных комментариев
    # Скачиваем реплаи на комментарии
    print('download replies')
    replies = []
    for line in items: # Проходим по корневым комментам
        if line.get("snippet").get("totalReplyCount") > 0: # если есть реплаи
            print(line.get("snippet").get("totalReplyCount")) # показываем сколько реплаев будет подгружено
            response = youtubechild(line.get("snippet").get("topLevelComment").get("id"))
            replies = replies + response.get("items")
            nextPageToken = response.get("nextPageToken")
            i=1
            while nextPageToken is not None: # догружаем реплаи, если есть ещё порции
                response = youtubechild(line.get("snippet").get("topLevelComment").get("id"), nextPageToken)
                nextPageToken = response.get("nextPageToken")
                replies = replies + response.get("items")
                i+=1
    print(len(replies)) # Отображаем количество скачаных реплаев
    # Сохраняем комментарии и реплаи на них в файл csv
    print("Open csv file")
    with open('youtuberesults.csv', 'w', encoding="utf-8") as csv_file:  #конструкция with, чтобы файл закрылся автоматом после всех команд
        writer = csv.writer(csv_file, quoting=csv.QUOTE_ALL, lineterminator='\r') # использовал двойные кавычки и разделитель запятую, такой формат отлично открывается через LibreOffice Calc
        # Заголовки столбцов
        row = [
              'etag'
            , 'parentid'
            , 'id'
            , 'textDisplay'
            , 'textOriginal'
            , 'authorDisplayName'
            , 'authorProfileImageUrl'
            , 'authorChannelUrl'
            , 'authorChannelId'
            , 'likeCount'
            , 'publishedAt'
            , 'updatedAt'
        ]
        print("Start write in csv")
        writer.writerow(row) # Записываем заголовки в файл
        # Сохраняем комментарии
        print("Write comments in csv")
        for line in items:
            topLevelComment = line.get("snippet").get("topLevelComment")
            # бывает, что у пользователя нет канала, поэтому для него отдельная конструкция
            if topLevelComment.get('snippet').get('authorChannelId') is not None:
                authorChannelId = topLevelComment.get('snippet').get('authorChannelId').get('value')
            else:
                authorChannelId = ''
            row = [
                  topLevelComment.get('etag')
                , topLevelComment.get('id')
                , topLevelComment.get('id')
                , topLevelComment.get('snippet').get('textDisplay')
                , topLevelComment.get('snippet').get('textOriginal')
                , topLevelComment.get('snippet').get('authorDisplayName')
                , topLevelComment.get('snippet').get('authorProfileImageUrl')
                , topLevelComment.get('snippet').get('authorChannelUrl')
                , authorChannelId
                , topLevelComment.get('snippet').get('likeCount')
                , topLevelComment.get('snippet').get('publishedAt')
                , topLevelComment.get('snippet').get('updatedAt')
            ]
            writer.writerow(row)
        # Сохраняем реплаи
        print("Write replies in csv")
        for line in replies:
            # бывает, что у пользователя нет канала, поэтому для него отдельная конструкция
            if line.get('snippet').get('authorChannelId') is not None:
                authorChannelId = line.get('snippet').get('authorChannelId').get('value')
            else:
                authorChannelId = ''
            row = [
                  line.get('etag')
                , line.get('snippet').get('parentId')
                , line.get('id')
                , line.get('snippet').get('textDisplay')
                , line.get('snippet').get('textOriginal')
                , line.get('snippet').get('authorDisplayName')
                , line.get('snippet').get('authorProfileImageUrl')
                , line.get('snippet').get('authorChannelUrl')
                , authorChannelId
                , line.get('snippet').get('likeCount')
                , line.get('snippet').get('publishedAt')
                , line.get('snippet').get('updatedAt')
            ]
            writer.writerow(row)
    print("done")
if __name__ == "__main__":
    main()
В полученной таблице все поля, которые доступны по YouTube API. Созданный csv отлично открывается в LibreOffice Calc. Используются двойные кавычки и разделитель запятая, кодировка UTF-8. Если нужно отличить корневой комментарий от реплая можно добавить столбец с сравнением parentid с id - в случае их равенства комментарий корневой, иначе реплай.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_python, #_api, #_youtube, #_kommentarii (комментарии), #_python, #_api
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 21-Май 07:42
Часовой пояс: UTC + 5