Каков самый Pythonic способ идентифицировать различные типы вложенных словарей, которые возвращает API, чтобы можно было применить правильный тип синтаксического анализа?
Я делаю вызовы API из Reddit, чтобы получить URL-адреса, и получаю вложенные словари с разными именами ключей и различной структурой вложенных словарей.
Я извлекаю нужные мне URL-адреса, но мне нужен более питонический способ. чтобы идентифицировать разные имена ключей и разные структуры вложенных словарей, потому что операторы if
, которые я пробовал в одном цикле for
, приводят к ошибкам, потому что, если словарь не содержит ключ, я получаю ошибку NoneType
только из оператора if
, спрашивающего, находится ли указанный ключ в словарь.
Еще в нескольких абзацах я опишу проблему, но вы, возможно, сможете погрузиться в примеры словарей и мой код ниже и увидеть мою проблему, заключающуюся в невозможности определить один из трех типов словарей за один проход. Вложенные словари не имеют одинаковых структур, и мой код полон try
s и, как мне кажется, избыточных for
циклов.
У меня есть функция для обработки трех типов вложенных словарей. topics_data
(используется ниже) — это кадр данных Pandas, а столбец vid
— это имя столбца в topics_data
, которое содержит вложенный словарь. Иногда объект в ячейке vid
равен None
, если сообщение, которое я читаю, не является видеопостом.
API возвращает только три основных типа вложенных словарей (если не None
). Моя самая большая проблема заключается в том, чтобы определить имя первого ключа без получения ошибки NoneType
, если я попытаюсь оператором if
поймать вложенный словарь с ключом reddit_video
, который вместо этого начинается с другого ключа, такого как oembed
. Из-за этой проблемы я перебираю список вложенных словарей три раза для каждого из трех типов вложенных словарей. Я хочу иметь возможность перебирать список вложенных словарей один раз и идентифицировать и обрабатывать каждый тип вложенного словаря за один проход.
Ниже приведены примеры трех различных типов вложенных словарей, которые я получаю, и уродливого кода, который я сейчас настроил для их обработки. Код у меня работает, но он уродлив. Пожалуйста, копайте и смотрите.
Вложенные словари...
Вложенный словарь 1
{'reddit_video': {'fallback_url': 'https://v.redd.it/te7wsphl85121/DASH_2_4_M?source=fallback',
'height': 480,
'width': 480,
'scrubber_media_url': 'https://v.redd.it/te7wsphl85121/DASH_600_K',
'dash_url': 'https://v.redd.it/te7wsphl85121/DASHPlaylist.mpd?a=1604490293%2CYmQzNDllMmQ4MDVhMGZhODMyYmIxNDc4NTZmYWNlNzE2Nzc3ZGJjMmMzZGJjMmYxMjRiMjJiNDU4NGEzYzI4Yg%3D%3D&v=1&f=sd',
'duration': 17,
'hls_url': 'https://v.redd.it/te7wsphl85121/HLSPlaylist.m3u8?a=1604490293%2COTg2YmIxZmVmZGNlYTVjMmFiYjhkMzk5NDRlNWI0ZTY4OGE1NzgxNzUyMDhkYjFiNWYzN2IxYWNkZjM3ZDU2YQ%3D%3D&v=1&f=sd',
'is_gif': False,
'transcoding_status': 'completed'}}
Вложенный словарь два
{'type': 'gfycat.com',
'oembed': {'provider_url': 'https://gfycat.com',
'description': 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).',
'title': 'Protestors in Hong Kong are cutting down facial recognition towers.',
'type': 'video',
'author_name': 'Gfycat',
'height': 600,
'width': 600,
'html': '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fedibleunrulyargentineruddyduck&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fedibleunrulyargentineruddyduck-hong-kong-protest&image=https%3A%2F%2Fthumbs.gfycat.com%2FEdibleUnrulyArgentineruddyduck-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="600" height="600" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="true"></iframe>',
'thumbnail_width': 280,
'version': '1.0',
'provider_name': 'Gfycat',
'thumbnail_url': 'https://thumbs.gfycat.com/EdibleUnrulyArgentineruddyduck-size_restricted.gif',
'thumbnail_height': 280}}
Вложенный словарь 3
{'oembed': {'provider_url': 'https://gfycat.com',
'description': 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).',
'title': 'STRAYA! Ski-roos. ???????????? ???? Stephan Grenfell for Australian Geographic',
'author_name': 'Gfycat',
'height': 338,
'width': 600,
'html': '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fhairyvibrantamericanratsnake&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fhairyvibrantamericanratsnake-snow-kangaroos&image=https%3A%2F%2Fthumbs.gfycat.com%2FHairyVibrantAmericanratsnake-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="600" height="338" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="true"></iframe>',
'thumbnail_width': 444,
'version': '1.0',
'provider_name': 'Gfycat',
'thumbnail_url': 'https://thumbs.gfycat.com/HairyVibrantAmericanratsnake-size_restricted.gif',
'type': 'video',
'thumbnail_height': 250},
'type': 'gfycat.com'}
Моя функция для обработки этих трех типов вложенных словарей. topics_data
— это кадр данных Pandas, а столбец vid
— это имя столбца в topics_data
, которое содержит вложенный словарь, или это None
.
def download_vid(topics_data, ydl_opts):
for i in topics_data['vid']:
try:
if i['reddit_video']:
B = i['reddit_video']['fallback_url']
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([B])
print(B)
except:
pass
for n, i in enumerate(topics_data['vid']):
try:
if i['type'] == 'gfycat.com':
C = topics_data.loc[n]['vid']['oembed']['thumbnail_url'].split('/')[-1:][0].split('-')[0]
C = 'https://giant.gfycat.com/'+ C +'.mp4'
sub = str(topics_data.loc[n]['subreddit']).lower()
urllib.request.urlretrieve(C,
'/media/iii/Q2/tor/Reddit/Subs/'+sub+'/'+C.split('/')[-1:][0])
print(C)
except:
pass
for i in topics_data['vid']:
try:
if i['oembed']['thumbnail_url']:
D = topics_data.loc[n]['vid']['oembed']['thumbnail_url'].split('/')[-1:][0].split('-')[0]
D = 'https://giant.gfycat.com/'+ D +'.mp4'
sub = str(topics_data.loc[n]['subreddit']).lower()
urllib.request.urlretrieve(D, '/media/iii/Q2/tor/Reddit/Subs/'+sub+'/'+D.split('/')[-1:][0])
print(D)
except:
pass
После написания этого кода я увидел, что операторы if
являются избыточными, потому что он либо будет try
, и успешно разберет topics_data.loc[n]['vid']['oembed']
, если это возможно, либо нет внутри каждого блока try
.
Не зацикливайтесь на том, как устроен вложенный словарь. проанализировано, потому что это не совсем моя проблема. Моя проблема в основном связана с тем, как определить, какой тип вложенного словаря имеет итератор. Я предполагаю, что все это можно обработать в одном цикле for
вместо трех.
И последняя проблема: иногда встречаются четвертый, пятый или шестой тип словаря, который мне неинтересен в разборе, потому что они слишком редки.
Этот последний блок кода, вероятно, не требуется, но я добавляю его только для того, чтобы ответить на вопрос. Моя функция, которая идентифицирует и анализирует словари, также принимает параметры для youtube-dl.
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')
def yt_dl_opts(topics_data):
ydl_opts = {
'format': 'bestvideo+bestaudio/37/22/18/best',
'merge': 'mp4',
'noplaylist' : True,
'progress_hooks': [my_hook],
'outtmpl' : '/media/iii/Q2/tor/Reddit/Subs/'+ str(topics_data.loc[0]['subreddit']).lower()+'/%(id)s'
}
return ydl_opts
ОБНОВЛЕНИЕ
Вот ответ на вопрос с помощью Нила. Просто добавлено, чтобы сделать вопросы и ответы более понятными для будущих поколений.
Все по-прежнему заключено в try: except: pass
, потому что все еще есть несколько случайных и всегда возвращаются новые структуры dic. Я пишу цикл для подсчета результатов видео, которые не являются None
, и подсчитываю все видео, успешно загруженные с помощью os.walk
.
def download_vid(topics_data, ydl_opts):
y_base = 'https://www.youtube.com/watch?v='
for n, i in enumerate(topics_data['vid']):
try:
if 'type' in i:
if 'youtube.com' in i[n]['type']:
print('This is a Youtube Video')
A = i['oembed']['html'].split('embed/')[1].split('?')[0]
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([A])
print(y_base+A)
if 'reddit_video' in i:
print('This is a reddit_video Video')
B = i['reddit_video']['fallback_url']
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([B])
print(B)
if 'type' in i:
if 'gfycat.com' in i[n]['type']:
print('This is a type, gfycat Video')
C = topics_data.loc[n]['vid']['oembed']['thumbnail_url'].split('/')[-1:][0].split('-')[0]
C = 'https://giant.gfycat.com/'+ C +'.mp4'
sub = str(topics_data.loc[n]['subreddit']).lower()
urllib.request.urlretrieve(C,
'/media/iii/Q2/tor/Reddit/Subs/'+sub+'/'+C.split('/')[-1:][0])
print(C)
if 'oembed' in i:
print('This is a oembed, gfycat Video')
D = topics_data.loc[n]['vid']['oembed']['thumbnail_url'].split('/')[-1:][0].split('-')[0]
D = 'https://giant.gfycat.com/'+ D +'.mp4'
sub = str(topics_data.loc[n]['subreddit']).lower()
urllib.request.urlretrieve(C, '/media/iii/Q2/tor/Reddit/Subs/'+sub+'/'+D.split('/')[-1:][0])
print(D)
except:
pass