问题描述
识别API返回的不同嵌套字典类型的最Python方式是什么,以便可以应用正确的解析类型?
我正在从Reddit进行API调用以获取URL,并且正在获取具有不同键名和嵌套字典结构的嵌套字典。
我只需要提取URL,但是我需要一种更Python化的方法来标识嵌套字典的不同键名和不同结构,因为我在一个if
循环中尝试过的for
条语句出错因为“ if”字典中不包含键,如果所说的键在字典中,我只是从nonetype
语句“问”中得到了if
错误。
在更多几段中,我描述了问题,但您也许可以深入了解字典示例和下面的代码,并看到我无法一次性识别三种字典之一的问题。嵌套的字典没有相同的结构,我的代码充满了try
和我认为多余的for
循环。
我有一个函数可以处理三种类型的嵌套字典。 topics_data
(以下使用)是Pandas数据框,列vid
是topics_data
中包含嵌套字典的列名称。如果我正在阅读的帖子不是视频帖子,有时vid
单元格中的对象是None
。
API仅返回三种主要的嵌套字典类型(如果不是None
)。我最大的问题是,如果我尝试执行nonetype
语句以键if
捕获以另一个键(例如{{1 }}。由于这个问题,对于三种嵌套字典类型中的每一种,我都会对嵌套字典列表进行三次迭代。我希望能够一次迭代嵌套字典的列表,并一次识别和处理每种类型的嵌套字典。
下面是我得到的三种不同类型的嵌套字典的示例,以及我现在设置用来处理它们的丑陋代码。我拥有的代码有效,但是很难看。请深入查看。
嵌套字典...
嵌套词典一
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}}
是Pandas数据框,列{'oembed': {'provider_url': 'https://gfycat.com','title': 'STRAYA! Ski-roos. ??? ? Stephan Grenfell for Australian Geographic','height': 338,'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,'thumbnail_url': 'https://thumbs.gfycat.com/HairyVibrantAmericanratsnake-size_restricted.gif','thumbnail_height': 250},'type': 'gfycat.com'}
是topics_data
中的列名称,其中包含嵌套的字典,或者是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
到每个{{1} }块。
不要对嵌套字典的解析方式感到困惑,因为这并不是我真正的问题。我的问题主要是如何确定迭代器具有哪种嵌套字典。我认为这可以在一个try
循环而不是三个循环中处理。
最后一个问题是偶尔会出现第四,第五或第六种字典,我对它们不感兴趣,因为它们太少了。
这最后的代码块可能不是必需的,但我添加它只是为了使问题完整。我用于识别和解析字典的函数还采用了youtube-dl的参数。
topics_data.loc[n]['vid']['oembed']
更新
在尼尔的帮助下,这是问题的答案。只是为了使Q和A对于子孙后代更加清楚。
一切仍然包裹在try
中,因为仍然有一些随机的,并且总是返回新的dic结构。我编写了一个循环以计算非for
的视频结果,并计算通过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
解决方法
更新:已实现的OP文本正在处理非唯一查询。添加了一段描述该操作的方法。
如果您发现自己多次遍历词典列表来执行查找,请将该列表重新组织为字典,以便查找是关键。例如:
a = [{"id": 1,"value": "foo"},{"id": 2,"value": "bar"}]
for item in a:
if item["id"] == 1:
print(item["value"])
可以变成这个:
a = [{"id": 1,"value": "bar"}]
a = {item["id"]: item for item in a} # index by lookup field
print(a[1]["value"]) # no loop
... # Now we can continue to loopup by id eg a[2] without a loop
如果它是非唯一查询,则可以执行类似操作:
indexed = {}
a = [{"category": 1,{"category": 2,"value": "bar"},{"category": 1,"value": "baz"}]
for item in a: # This loop only has to be executed once
if indexed.get(item["category"],None) is not None:
indexed[item["category"]].append(item)
else:
indexed[item["category"]] = [item]
# Now we can do:
all_category_1_data = indexed[1]
all_category_2_data = indexed[2]
如果出现索引错误,请使用默认的字典索引来更轻松地处理
if a.get(1,None) is not None:
print(a[1]["value"])
else:
print("1 was not in the dictionary")
关于此IMO的内容没有什么“ Pythonic”,但如果API返回需要循环的列表,则可能是设计不良的API
更新:好的,我会尝试修复您的代码:
def download_vid(topics_data,ydl_opts):
indexed_data = {'reddit': [],'gfycat': [],'thumbnail': []}
for item in topics_data['vid']:
if item.get('reddit_video',None) is not None:
indexed_data['reddit'].append(item)
elif item.get('type',None) == "gfycat.com":
indexed_data['gfycat'].append(item)
elif item.get('oembed',None) is not None:
if item['oembed'].get('thumbnail_url',None) is not None:
indexed_data['thumbnail'].append(item)
for k,v in indexed_data.items():
assert k in ('reddit_video','gfycat','thumbnail')
if k == 'reddit_video':
B = v['reddit_video']['fallback_rul']
...
elif k == 'gfycat':
C = v['oembed']['thumbnail_url']
...
elif k == 'thumbnail':
D = v['oembed']['thumbnail_url']
...
以防万一,为什么更好呢?
-
OP遍历了topic_data ['vid'] 3次。我做了两次。
-
更重要的是,如果添加了更多主题,我仍然只会执行两次。 OP必须再次循环。
-
没有异常处理。
-
现在已为每组对象建立索引。因此,OP可以执行例如indexed_data ['gfycat']来获取所有这些对象(如果需要的话),这是一个哈希表查找,因此其运行速度很快