Кой е най-Pythonic начинът за идентифициране на различните вложени типове речници, които API връща, така че да може да се приложи правилният тип анализ?
Извършвам API извиквания от Reddit, за да получа URL адреси и получавам вложени речници с различни имена на ключове и различни структури на вложени речници.
Изтеглям URL адресите, от които се нуждая, добре, но имам нужда от по-питоничен начин за идентифициране на различните имена на ключове и различни структури на вложените речници, защото if
изразите, които опитах в един for
цикъл, се сблъскват с грешки, защото ако речникът не съдържа ключа, получавам NoneType
грешка само от израза if
, питайки дали споменатият ключ е в речникът.
В още няколко параграфа описвам проблема, но може да успеете да се потопите в примерите за речници и моя код по-долу и да видите моя проблем с невъзможността да идентифицирам един от трите типа речници с едно преминаване. Вложените речници нямат еднакви структури и моят код е пълен с try
s и според мен излишни for
цикли.
Имам функция за обработка на три типа вложени речници. topics_data
(използван по-долу) е Pandas Dataframe, а колоната vid
е име на колона в topics_data
, което съдържа вложен речник. Понякога обектът в клетката vid
е None
, ако публикацията, която чета, не е видео публикация.
Има само три основни типа вложени речници, които API връща (ако не None
). Най-големият ми проблем е да идентифицирам името на първия ключ, без да получа грешка NoneType
, ако се опитам с оператор if
да уловя вложен речник с ключа reddit_video
, който вместо това започва с друг ключ, като например oembed
. Поради този проблем преглеждам списъка с вложени речници три пъти за всеки от трите типа вложени речници. Искам да мога да прегледам списъка с вложени речници веднъж и всеки тип вложен речник да бъде идентифициран и обработен с едно преминаване.
По-долу са примери за трите различни типа вложени речници, които получавам, и грозния код, който съм настроил сега, за да се справя с тях. Кодът, който имам, работи, но е грозен. Моля, разровете и вижте.
Вложените речници...
Вложен речник първи
{'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}}
Вложен речник трети
{'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 Dataframe и колоната 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
АКТУАЛИЗАЦИЯ
Ето отговора на въпроса с помощта на Нийл. Просто добавям, за да направя Q и A по-ясни за бъдещите поколения.
Всичко все още е обвито в 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