Esta é uma API Python que permite recuperar a transcrição/legendas de um vídeo do YouTube. Ela também funciona com legendas geradas automaticamente, suporta tradução de legendas e não requer um navegador headless, como outras soluções baseadas em selenium!
A manutenção deste projeto é possível graças a todos os contribuidores e patrocinadores. Se deseja patrocinar este projeto e ter seu avatar ou logo da empresa aparecer abaixo clique aqui. 💖
Recomenda-se instalar este módulo usando pip:
pip install youtube-transcript-api
Você pode integrar este módulo em uma aplicação existente ou apenas usá-lo via CLI.
A maneira mais fácil de obter uma transcrição para um vídeo específico é executar:
from youtube_transcript_api import YouTubeTranscriptApi
ytt_api = YouTubeTranscriptApi()
ytt_api.fetch(video_id)
Nota: Por padrão, isso tentará acessar a transcrição em inglês do vídeo. Se o seu vídeo estiver em outro idioma ou se você deseja obter a transcrição em um idioma diferente, leia a seção abaixo.
Nota: Passe o ID do vídeo, NÃO a URL do vídeo. Para um vídeo com a URL
https://www.youtube.com/watch?v=12345
, o ID é12345
.
Isso retornará um objeto FetchedTranscript
semelhante a este:
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,
)
Este objeto implementa a maioria das interfaces de uma 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)
Se preferir lidar com os dados brutos da transcrição, você pode chamar fetched_transcript.to_raw_data()
, que retornará uma lista de dicionários:
[
{
'text': 'Hey there',
'start': 0.0,
'duration': 1.54
},
{
'text': 'how are you',
'start': 1.54
'duration': 4.16
},
# ...
]
Você pode adicionar o parâmetro languages
se quiser garantir que as transcrições sejam recuperadas no idioma desejado (o padrão é inglês).
YouTubeTranscriptApi().fetch(video_id, languages=['de', 'en'])
É uma lista de códigos de idioma em prioridade decrescente. Neste exemplo, ele tentará primeiro obter a transcrição em alemão ('de'
) e, se falhar, buscará a transcrição em inglês ('en'
). Se quiser descobrir quais idiomas estão disponíveis primeiro, consulte list()
.
Se você quiser apenas um idioma, ainda precisa formatar o argumento languages
como uma lista:
YouTubeTranscriptApi().fetch(video_id, languages=['de'])
Você também pode adicionar preserve_formatting=True
se quiser manter elementos de formatação HTML como <i>
(itálico) e <b>
(negrito).
YouTubeTranscriptApi().fetch(video_ids, languages=['de', 'en'], preserve_formatting=True)
Se quiser listar todas as transcrições disponíveis para um vídeo específico, você pode chamar:
ytt_api = YouTubeTranscriptApi()
transcript_list = ytt_api.list(video_id)
Isso retornará um objeto TranscriptList
que é iterável e fornece métodos para filtrar a lista de transcrições por idiomas e tipos específicos, como:
transcript = transcript_list.find_transcript(['de', 'en'])
Por padrão, este módulo sempre escolhe transcrições criadas manualmente em vez de geradas automaticamente, se uma transcrição no idioma solicitado estiver disponível em ambos os formatos. O TranscriptList
permite que você ignore esse comportamento padrão, procurando por tipos específicos de transcrição:
# 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'])
Os métodos find_generated_transcript
, find_manually_created_transcript
, find_transcript
retornam objetos Transcript
. Eles contêm metadados sobre a transcrição:
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,
)
e fornecem o método que permite obter os dados reais da transcrição:
transcript.fetch()
Isso retorna um objeto FetchedTranscript
, assim como YouTubeTranscriptApi().fetch()
faz.
O YouTube possui um recurso que permite traduzir legendas automaticamente. Este módulo também possibilita acessar esse recurso. Para isso, os objetos Transcript
fornecem um método translate()
, que retorna um novo objeto Transcript
traduzido:
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
ou IpBlocked
)Infelizmente, o YouTube começou a bloquear a maioria dos IPs conhecidos por pertencerem a provedores de nuvem (como AWS, Google Cloud Platform, Azure, etc.), o que significa que você provavelmente encontrará exceções RequestBlocked
ou IpBlocked
ao implantar seu código em qualquer solução de nuvem. O mesmo pode acontecer com o IP da sua solução self-hosted, se você fizer muitas solicitações. Você pode contornar esses bloqueios de IP usando proxies. No entanto, como o YouTube pode banir proxies estáticos após uso prolongado, optar por proxies residenciais rotativos é a opção mais confiável.
Existem diferentes provedores que oferecem proxies residenciais rotativos, mas após testar diferentes opções, descobri que o Webshare é o mais confiável e, portanto, o integrei a este módulo para facilitar sua configuração.
Depois de criar uma conta no Webshare e comprar um pacote de proxy "Residencial" adequado à sua carga de trabalho (certifique-se de NÃO comprar "Proxy Server" ou "Static Residential"!), acesse as Configurações de Proxy do Webshare para recuperar seu "Proxy Username" e "Proxy Password". Com essas informações, você pode inicializar o YouTubeTranscriptApi
da seguinte forma:
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)
Usar o WebshareProxyConfig
padronizará o uso de proxies residenciais rotativos e não requer configuração adicional.
Observe que links de referência são usados aqui e qualquer compra feita através desses links apoiará este projeto Open Source, o que é muito apreciado! 💖😊🙏💖
No entanto, você é livre para integrar sua própria solução de proxy usando a classe GenericProxyConfig
, se preferir usar outro provedor ou implementar sua própria solução, conforme abordado na próxima seção.
Alternativamente ao uso do Webshare, você pode configurar qualquer proxy HTTP/HTTPS/SOCKS genérico usando a classe 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)
Esteja ciente de que usar um proxy não garante que você não será bloqueado, pois o YouTube pode sempre bloquear o IP do seu proxy! Portanto, você deve sempre escolher uma solução que faça rotação por um pool de endereços de proxy, se quiser maximizar a confiabilidade.
Ao inicializar um objeto YouTubeTranscriptApi
, ele criará um requests.Session
que será usado para todas as solicitações HTTP(S). Isso permite o cache de cookies ao recuperar várias solicitações. No entanto, você pode opcionalmente passar um objeto requests.Session
em seu construtor, se quiser compartilhar cookies manualmente entre diferentes instâncias de YouTubeTranscriptApi
, sobrescrever padrões, definir cabeçalhos personalizados, especificar certificados SSL, etc.
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)
Alguns vídeos têm restrição de idade, então este módulo não poderá acessá-los sem algum tipo de autenticação. Infelizmente, algumas mudanças recentes na API do YouTube quebraram a implementação atual da autenticação baseada em cookie, então esse recurso não está disponível no momento.
Formatadores são destinados a ser uma camada adicional de processamento da transcrição que você passa para eles. O objetivo é converter um objeto FetchedTranscript
em uma string consistente de um determinado "formato". Como um texto básico (.txt
) ou até formatos que têm uma especificação definida, como JSON (.json
), WebVTT (.vtt
), SRT (.srt
), formato separado por vírgulas (.csv
), etc...
O submódulo formatters
fornece alguns formatadores básicos, que podem ser usados como estão ou estendidos conforme suas necessidades:
Aqui está como importar do módulo 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
Digamos que queremos recuperar uma transcrição e armazená-la em um arquivo JSON. Isso seria algo assim:
# 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.
Passando argumentos de palavra-chave extras
Como o JSONFormatter utiliza json.dumps()
, você também pode encaminhar argumentos de palavra-chave para .format_transcript(transcript)
, como tornar a saída do arquivo mais legível, encaminhando o argumento indent=2
.
json_formatted = JSONFormatter().format_transcript(transcript, indent=2)
Você pode implementar sua própria classe de formatador. Basta herdar da classe base Formatter
e garantir que você implemente os métodos format_transcript(self, transcript: FetchedTranscript, **kwargs) -> str
e format_transcripts(self, transcripts: List[FetchedTranscript], **kwargs) -> str
, que devem retornar uma string quando chamados em sua instância de formatador.
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.'
Execute o script CLI usando os IDs dos vídeos como parâmetros e os resultados serão impressos na linha de comando:
youtube_transcript_api <first_video_id> <second_video_id> ...
A CLI também oferece a opção de fornecer uma lista de idiomas preferidos:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en
Você também pode especificar se deseja excluir legendas geradas automaticamente ou criadas manualmente:
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
Se preferir escrever em um arquivo ou canalizar para outro aplicativo, você também pode gerar os resultados como json usando a seguinte linha:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages de en --format json > transcripts.json
Traduzir transcrições usando a CLI também é possível:
youtube_transcript_api <first_video_id> <second_video_id> ... --languages en --translate de
Se não tiver certeza de quais idiomas estão disponíveis para um determinado vídeo, você pode chamar para listar todas as transcrições disponíveis:
youtube_transcript_api --list-transcripts <first_video_id>
Se o ID de um vídeo começar com um hífen, você terá que mascarar o hífen usando \
para evitar que a CLI o confunda com um nome de argumento. Por exemplo, para obter a transcrição do vídeo com o ID -abc123
, execute:
youtube_transcript_api "\-abc123"
Se você estiver enfrentando erros RequestBlocked
ou IpBlocked
porque o YouTube bloqueou seu IP, você pode contornar isso usando proxies residenciais, conforme explicado em Contornando bloqueios de IP. Para usar proxies "Residenciais" do Webshare através da CLI, você precisará criar uma conta no Webshare e comprar um pacote de proxy "Residencial" adequado à sua carga de trabalho (certifique-se de NÃO comprar "Proxy Server" ou "Static Residential"!). Em seguida, você pode usar o "Proxy Username" e "Proxy Password", que podem ser encontrados nas suas Configurações de Proxy do Webshare, para executar o seguinte comando:
youtube_transcript_api <first_video_id> <second_video_id> --webshare-proxy-username "username" --webshare-proxy-password "password"
Se preferir usar outra solução de proxy, você pode configurar um proxy HTTP/HTTPS genérico usando o seguinte comando:
youtube_transcript_api <first_video_id> <second_video_id> --http-proxy http://user:pass@domain:port --https-proxy https://user:pass@domain:port
Para autenticar usando cookies através da CLI, conforme explicado em Autenticação por Cookie, execute:
youtube_transcript_api <first_video_id> <second_video_id> --cookies /path/to/your/cookies.txt
Este código usa uma parte não documentada da API do YouTube, que é chamada pelo cliente web do YouTube. Portanto, não há garantia de que ele não deixará de funcionar amanhã, se eles mudarem como as coisas funcionam. No entanto, farei o possível para corrigir as coisas o mais rápido possível, se isso acontecer. Portanto, se parar de funcionar, me avise!
Para configurar o projeto localmente, execute o seguinte (requer poetry instalado):
poetry install --with test,dev
Há tarefas poe para executar testes, cobertura, linter e formatador (você precisará passar por todos eles para que a build seja aprovada):
poe test
poe coverage
poe format
poe lint
Se você apenas quiser garantir que seu código passe em todas as verificações necessárias para obter uma build verde, pode simplesmente executar:
poe precommit
Se este projeto te deixa feliz, reduzindo seu tempo de desenvolvimento, você pode me deixar feliz me oferecendo um café ou se tornando um Patrocinador deste projeto :)