Это Python API, которое позволяет получать транскрипты/субтитры для заданного видео на YouTube. Оно также работает с автоматически сгенерированными субтитрами, поддерживает перевод субтитров и не требует использования headless-браузера, в отличие от решений на основе Selenium!
Поддержка этого проекта стала возможной благодаря всем участникам и спонсорам. Если вы хотите спонсировать этот проект и разместить свой аватар или логотип компании ниже, нажмите здесь. 💖
Рекомендуется установить этот модуль с помощью pip:
pip install youtube-transcript-api
Вы можете интегрировать этот модуль в существующее приложение или просто использовать его через CLI.
Самый простой способ получить транскрипт для заданного видео — выполнить:
from youtube_transcript_api import YouTubeTranscriptApi
ytt_api = YouTubeTranscriptApi()
ytt_api.fetch(video_id)
Примечание: По умолчанию будет предпринята попытка получить английский транскрипт видео. Если у вашего видео другой язык или вы хотите получить транскрипт на другом языке, прочитайте раздел ниже.
Примечание: Передавайте ID видео, а не URL. Для видео с URL
https://www.youtube.com/watch?v=12345
ID — это12345
.
Это вернет объект FetchedTranscript
, который выглядит примерно так:
FetchedTranscript(
snippets=[
FetchedTranscriptSnippet(
text="Hey there",
start=0.0,
duration=1.54,
),
FetchedTranscriptSnippet(
text="how are you",
start=1.54,
duration=4.16,
),
# ...
],
video_id="12345",
language="English",
language_code="en",
is_generated=False,
)
Этот объект реализует большинство интерфейсов List
:
ytt_api = YouTubeTranscriptApi()
fetched_transcript = ytt_api.fetch(video_id)
# is iterable
for snippet in fetched_transcript:
print(snippet.text)
# indexable
last_snippet = fetched_transcript[-1]
# provides a length
snippet_count = len(fetched_transcript)
Если вы предпочитаете работать с сырыми данными транскрипта, вы можете вызвать fetched_transcript.to_raw_data()
, который вернет список словарей:
[
{
'text': 'Hey there',
'start': 0.0,
'duration': 1.54
},
{
'text': 'how are you',
'start': 1.54
'duration': 4.16
},
# ...
]
Вы можете добавить параметр languages
, если хотите убедиться, что транскрипты будут получены на нужном вам языке (по умолчанию — английский).
YouTubeTranscriptApi().fetch(video_id, languages=['de', 'en'])
Это список кодов языков в порядке убывания приоритета. В этом примере сначала будет предпринята попытка получить немецкий транскрипт ('de'
), а затем английский ('en'
), если первая попытка не удастся. Если вы хотите узнать, какие языки доступны, ознакомьтесь с list()
.
Если вам нужен только один язык, параметр languages
все равно должен быть оформлен как список:
YouTubeTranscriptApi().fetch(video_id, languages=['de'])
Вы также можете добавить preserve_formatting=True
, если хотите сохранить элементы HTML-форматирования, такие как <i>
(курсив) и <b>
(жирный шрифт).
YouTubeTranscriptApi().fetch(video_ids, languages=['de', 'en'], preserve_formatting=True)
Если вы хотите получить список всех доступных транскриптов для заданного видео, вы можете вызвать:
ytt_api = YouTubeTranscriptApi()
transcript_list = ytt_api.list(video_id)
Это вернет объект TranscriptList
, который является итерируемым и предоставляет методы для фильтрации списка транскриптов по определенным языкам и типам, например:
transcript = transcript_list.find_transcript(['de', 'en'])
По умолчанию этот модуль всегда выбирает ручные транскрипты вместо автоматически созданных, если транскрипт на запрашиваемом языке доступен в обоих вариантах. TranscriptList
позволяет обойти это поведение по умолчанию, выполнив поиск по определенным типам транскриптов:
# filter for manually created transcripts
transcript = transcript_list.find_manually_created_transcript(['de', 'en'])
# or automatically generated ones
transcript = transcript_list.find_generated_transcript(['de', 'en'])
Методы find_generated_transcript
, find_manually_created_transcript
, find_transcript
возвращают объекты Transcript
. Они содержат метаданные о транскрипте:
print(
transcript.video_id,
transcript.language,
transcript.language_code,
# whether it has been manually created or generated by YouTube
transcript.is_generated,
# whether this transcript can be translated or not
transcript.is_translatable,
# a list of languages the transcript can be translated to
transcript.translation_languages,
)
и предоставляют метод, который позволяет получить фактические данные транскрипта:
transcript.fetch()
Это возвращает объект FetchedTranscript
, так же как и YouTubeTranscriptApi().fetch()
.
YouTube имеет функцию автоматического перевода субтитров. Этот модуль также позволяет получить доступ к этой функции. Для этого объекты Transcript
предоставляют метод translate()
, который возвращает новый переведенный объект Transcript
:
transcript = transcript_list.find_transcript(['en'])
translated_transcript = transcript.translate('de')
print(translated_transcript.fetch())
from youtube_transcript_api import YouTubeTranscriptApi
ytt_api = YouTubeTranscriptApi()
# retrieve the available transcripts
transcript_list = ytt_api.list('video_id')
# iterate over all available transcripts
for transcript in transcript_list:
# the Transcript object provides metadata properties
print(
transcript.video_id,
transcript.language,
transcript.language_code,
# whether it has been manually created or generated by YouTube
transcript.is_generated,
# whether this transcript can be translated or not
transcript.is_translatable,
# a list of languages the transcript can be translated to
transcript.translation_languages,
)
# fetch the actual transcript data
print(transcript.fetch())
# translating the transcript will return another transcript object
print(transcript.translate('en').fetch())
# you can also directly filter for the language you are looking for, using the transcript list
transcript = transcript_list.find_transcript(['de', 'en'])
# or just filter for manually created transcripts
transcript = transcript_list.find_manually_created_transcript(['de', 'en'])
# or automatically generated ones
transcript = transcript_list.find_generated_transcript(['de', 'en'])
RequestBlocked
или IpBlocked
исключение)К сожалению, YouTube начал блокировать большинство IP-адресов, которые принадлежат облачным провайдерам (таким как AWS, Google Cloud Platform, Azure и т. д.), что означает, что вы, скорее всего, столкнетесь с исключениями ReuquestBlocked
или IpBlocked
при развертывании кода в облачных решениях. То же самое может произойти с IP-адресом вашего самодельного решения, если вы делаете слишком много запросов. Вы можете обойти эти блокировки с помощью прокси. Однако, поскольку YouTube блокирует статические прокси после длительного использования, наиболее надежным вариантом являются ротируемые резидентные прокси.
Существуют разные провайдеры, предлагающие ротируемые резидентные прокси, но после тестирования различных вариантов я обнаружил, что Webshare является наиболее надежным и интегрировал его в этот модуль, чтобы упростить настройку.
После создания аккаунта Webshare и покупки пакета "Residential" прокси, подходящего для вашей нагрузки (убедитесь, что НЕ покупаете "Proxy Server" или "Static Residential"!), откройте настройки прокси Webshare, чтобы получить "Proxy Username" и "Proxy Password". Используя эту информацию, вы можете инициализировать YouTubeTranscriptApi
следующим образом:
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.proxies import WebshareProxyConfig
ytt_api = YouTubeTranscriptApi(
proxy_config=WebshareProxyConfig(
proxy_username="<proxy-username>",
proxy_password="<proxy-password>",
)
)
# all requests done by ytt_api will now be proxied through Webshare
ytt_api.fetch(video_id)
Использование WebshareProxyConfig
по умолчанию предполагает использование ротируемых резидентных прокси и не требует дополнительной настройки.
Обратите внимание, что здесь используются реферальные ссылки, и любые покупки, сделанные по этим ссылкам, поддержат этот Open Source проект, что очень ценится! 💖😊🙏💖
Однако вы, конечно, можете интегрировать собственное решение с прокси, используя класс GenericProxyConfig
, если предпочитаете другого провайдера или хотите реализовать собственное решение, как описано в следующем разделе.
В качестве альтернативы Webshare вы можете настроить любой HTTP/HTTPS/SOCKS прокси с помощью класса GenericProxyConfig
:
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.proxies import GenericProxyConfig
ytt_api = YouTubeTranscriptApi(
proxy_config=GenericProxyConfig(
http_url="http://user:[email protected]:port",
https_url="https://user:[email protected]:port",
)
)
# all requests done by ytt_api will now be proxied using the defined proxy URLs
ytt_api.fetch(video_id)
Учтите, что использование прокси не гарантирует, что вас не заблокируют, так как YouTube может заблокировать IP-адрес вашего прокси! Поэтому, если вы хотите максимизировать надежность, следует выбирать решение, которое ротирует пул адресов прокси.
При инициализации объекта YouTubeTranscriptApi
создается requests.Session
, который будет использоваться для всех HTTP(S) запросов. Это позволяет кэшировать куки при получении нескольких запросов. Однако вы можете передать объект requests.Session
в конструктор, если хотите вручную разделить куки между разными экземплярами YouTubeTranscriptApi
, переопределить настройки по умолчанию, установить пользовательские заголовки, указать SSL-сертификаты и т. д.
from requests import Session
http_client = Session()
# set custom header
http_client.headers.update({"Accept-Encoding": "gzip, deflate"})
# set path to CA_BUNDLE file
http_client.verify = "/path/to/certfile"
ytt_api = YouTubeTranscriptApi(http_client=http_client)
ytt_api.fetch(video_id)
# share same Session between two instances of YouTubeTranscriptApi
ytt_api_2 = YouTubeTranscriptApi(http_client=http_client)
# now shares cookies with ytt_api
ytt_api_2.fetch(video_id)
Некоторые видео имеют возрастные ограничения, поэтому этот модуль не сможет получить доступ к ним без какой-либо аутентификации. К сожалению, недавние изменения в API YouTube нарушили текущую реализацию аутентификации на основе куки, поэтому эта функция сейчас недоступна.
Форматтеры предназначены для дополнительной обработки транскрипта, который вы им передаете. Цель — преобразовать объект FetchedTranscript
в строку заданного "формата". Например, простой текст (.txt
) или форматы с определенной спецификацией, такие как JSON (.json
), WebVTT (.vtt
), SRT (.srt
), CSV (.csv
) и т. д.
Подмодуль formatters
предоставляет несколько базовых форматтеров, которые можно использовать как есть или расширить под свои нужды:
Вот как можно импортировать из модуля formatters
:
# the base class to inherit from when creating your own formatter.
from youtube_transcript_api.formatters import Formatter
# some provided subclasses, each outputs a different string format.
from youtube_transcript_api.formatters import JSONFormatter
from youtube_transcript_api.formatters import TextFormatter
from youtube_transcript_api.formatters import WebVTTFormatter
from youtube_transcript_api.formatters import SRTFormatter
Допустим, мы хотим получить транскрипт и сохранить его в JSON-файл. Это будет выглядеть примерно так:
# your_custom_script.py
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.formatters import JSONFormatter
ytt_api = YouTubeTranscriptApi()
transcript = ytt_api.fetch(video_id)
formatter = JSONFormatter()
# .format_transcript(transcript) turns the transcript into a JSON string.
json_formatted = formatter.format_transcript(transcript)
# Now we can write it out to a file.
with open('your_filename.json', 'w', encoding='utf-8') as json_file:
json_file.write(json_formatted)
# Now should have a new JSON file that you can easily read back into Python.
Передача дополнительных аргументов
Поскольку JSONFormatter использует json.dumps()
, вы также можете передать дополнительные аргументы в .format_transcript(transcript)
, например, сделать вывод файла более читаемым, передав аргумент indent=2
.
json_formatted = JSONFormatter().format_transcript(transcript, indent=2)
Вы можете реализовать собственный класс форматтера. Просто унаследуйтесь от базового класса Formatter
и убедитесь, что реализовали методы format_transcript(self, transcript: FetchedTranscript, **kwargs) -> str
и format_transcripts(self, transcripts: List[FetchedTranscript], **kwargs) -> str
, которые в конечном итоге должны возвращать строку при вызове на экземпляре вашего форматтера.
class MyCustomFormatter(Formatter):
def format_transcript(self, transcript: FetchedTranscript, **kwargs) -> str:
# Do your custom work in here, but return a string.
return 'your processed output data as a string.'
def format_transcripts(self, transcripts: List[FetchedTranscript], **kwargs) -> str:
# Do your custom work in here to format a list of transcripts, but return a string.
return 'your processed output data as a string.'
Выполните CLI-скрипт, используя ID видео в качестве параметров, и результаты будут выведены в командную строку:
youtube_transcript_api <first_video_id> <second_video_id> ...
CLI также позволяет указать список предпочитаемых языков:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en
Вы также можете указать, хотите ли вы исключить автоматически сгенерированные или ручные субтитры:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en --exclude-generated
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en --exclude-manually-created
Если вы предпочитаете записать результаты в файл или передать их в другое приложение, вы также можете вывести результаты в формате JSON:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en --format json > transcripts.json
Перевод транскриптов через CLI также возможен:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages en --translate de
Если вы не уверены, какие языки доступны для заданного видео, вы можете вызвать команду для списка всех доступных транскриптов:
youtube_transcript_api --list-transcripts <first_video_id>
Если ID видео начинается с дефиса, вам нужно экранировать его с помощью \
, чтобы CLI не принял его за аргумент. Например, чтобы получить транскрипт для видео с ID -abc123
, выполните:
youtube_transcript_api "\-abc123"
Если вы сталкиваетесь с ошибками ReqestBlocked
или IpBlocked
из-за блокировки вашего IP YouTube, вы можете обойти это с помощью резидентных прокси, как описано в Обход блокировок IP. Чтобы использовать резидентные прокси Webshare через CLI, вам нужно создать аккаунт Webshare и купить пакет "Residential" прокси, подходящий для вашей нагрузки (убедитесь, что НЕ покупаете "Proxy Server" или "Static Residential"!). Затем вы можете использовать "Proxy Username" и "Proxy Password" из настроек прокси Webshare, чтобы выполнить следующую команду:
youtube_transcript_api <first_video_id> <second_video_id> --webshare-proxy-username "username" --webshare-proxy-password "password"
Если вы предпочитаете другое решение с прокси, вы можете настроить общий HTTP/HTTPS прокси с помощью следующей команды:
youtube_transcript_api <first_video_id> <second_video_id> --http-proxy http://user:pass@domain:port --https-proxy https://user:pass@domain:port
Для аутентификации с помощью куки через CLI, как описано в Аутентификация с помощью куки, выполните:
youtube_transcript_api <first_video_id> <second_video_id> --cookies /path/to/your/cookies.txt
Этот код использует недокументированную часть YouTube API, которая вызывается веб-клиентом YouTube. Поэтому нет гарантии, что он не перестанет работать завтра, если YouTube изменит свою работу. Однако я приложу все усилия, чтобы как можно скорее восстановить работоспособность, если это произойдет. Так что если что-то перестанет работать, дайте мне знать!
Для локальной настройки проекта выполните следующее (требуется установленный poetry):
poetry install --with test,dev
Есть задачи poe для запуска тестов, покрытия, линтера и форматтера (вам нужно пройти все эти проверки для успешной сборки):
poe test
poe coverage
poe format
poe lint
Если вы просто хотите убедиться, что ваш код проходит все необходимые проверки для успешной сборки, вы можете просто выполнить:
poe precommit
Если этот проект сэкономил ваше время разработки, вы можете сделать меня счастливым, угостив меня чашечкой кофе или став Спонсором этого проекта :)