本教程详细讲解如何利用python和USDA食品数据中心API获取完整的食品营养数据。针对API默认50条结果的限制,我们将深入探讨API分页机制,包括pageSize和pageNumber参数的使用。通过迭代请求、优化代码结构及错误处理,本文将提供一个高效的解决方案,帮助用户克服API分页限制,全面抓取并处理所有可用的食品营养信息。
理解API分页机制
在使用外部api获取数据时,经常会遇到api对单次请求返回结果数量的限制。这是一种常见的机制,旨在防止单个请求消耗过多服务器资源,并提高api响应速度。usda食品数据中心(fdc)api也不例外,其默认情况下每次请求可能只返回50条结果。
要解决这个问题,关键在于查阅API的官方文档。通过仔细阅读API文档,我们可以发现FDC API支持分页查询,这意味着数据被分割成多个“页面”,每次请求只能获取一个页面的数据。文档中明确指出了两个关键参数:
- pageSize:定义了每个页面返回结果的最大数量。默认值为50,但根据文档,最大可以设置为200。
- pageNumber:指定要请求的页面编号。通过递增此参数,可以逐页获取所有数据。
API响应中通常会包含currentPage(当前页码)和totalPages(总页数)等信息,这些信息对于实现完整的数据遍历至关重要。
分页数据获取的Python实现
为了获取所有符合条件的食品营养数据,我们需要编写一个循环,在每次迭代中请求不同的页面,直到所有页面都被访问。以下是实现这一目标的Python代码示例,它对原始请求逻辑进行了优化和扩展。
优化后的api调用函数
import requests import JSon import pandas as pd def get_all_usda_foods(query_term, api_key): """ 通过USDA食品数据中心API获取所有匹配的食品营养数据。 该函数处理API分页,并返回一个包含所有食品项的列表。 Args: query_term (str): 查询的食品名称或关键词,例如 'raw'。 api_key (str): 您的USDA API密钥。 Returns: list: 包含所有食品项字典的列表,如果请求失败则返回None。 """ all_foods = [] # 构造基础URL,设置pageSize为最大值200以减少请求次数 base_url = f"https://api.nal.usda.gov/fdc/v1/foods/search?api_key={api_key}&query={query_term}&pageSize=200" with requests.Session() as session: # 使用requests.Session提高效率 try: # 首次请求获取总页数 print(f"Fetching initial page for query: '{query_term}'...") initial_response = session.get(base_url, timeout=10) initial_response.raise_for_status() # 检查HTTP请求是否成功 api_response_data = initial_response.json() if not api_response_data.get("foods"): print(f"No foods found for query: '{query_term}'.") return [] total_pages = api_response_data.get("totalPages", 1) print(f"Total pages to retrieve: {total_pages}") all_foods.extend(api_response_data["foods"]) # 添加第一页的数据 # 遍历剩余页面 for page_num in range(2, total_pages + 1): print(f"Fetching page {page_num}/{total_pages}...") page_url = f"{base_url}&pageNumber={page_num}" page_response = session.get(page_url, timeout=10) page_response.raise_for_status() page_data = page_response.json() all_foods.extend(page_data["foods"]) except requests.exceptions.HTTPError as http_err: print(f"HTTP error occurred: {http_err} - Status Code: {http_err.response.status_code}") return None except requests.exceptions.ConnectionError as conn_err: print(f"Connection error occurred: {conn_err}") return None except requests.exceptions.Timeout as timeout_err: print(f"Request timed out: {timeout_err}") return None except requests.exceptions.RequestException as req_err: print(f"An unexpected error occurred: {req_err}") return None except json.JSONDecodeError as json_err: print(f"Error decoding JSON response: {json_err}") print(f"Response content: {initial_response.text if 'initial_response' in locals() else 'N/A'}") return None return all_foods # 替换为您的实际API密钥 API_KEY = "YOUR_USDA_API_KEY" food_items = get_all_usda_foods("raw", API_KEY) if food_items: print(f"nSuccessfully retrieved {len(food_items)} food items.") # 后续数据处理... else: print("Failed to retrieve food items or no items found.")
代码解析与优化点
-
requests.Session的使用: requests.Session对象可以在多次请求中保持某些参数(如cookies、请求头),并且会重用底层的TCP连接,这对于进行多次API请求(如分页)来说,可以显著提高效率和性能。
-
pageSize参数优化: 我们将pageSize直接设置为API允许的最大值200。这样做可以减少需要发起的API请求总数,从而缩短数据获取时间。
-
分页逻辑:
立即学习“Python免费学习笔记(深入)”;
- 首先发起一个请求来获取第一页的数据,并从响应中提取totalPages(总页数)。
- 将第一页的数据添加到all_foods列表中。
- 然后,使用for循环从第2页开始(因为第1页已经获取),一直迭代到totalPages。
- 在每次循环中,通过添加&pageNumber={page_num}参数来构造新的URL,并发送请求获取当前页的数据。
- 将每页获取到的foods列表扩展到all_foods中。
-
健壮的错误处理:
数据处理与导出
获取到完整的食品数据列表food_items后,下一步就是将其转换为结构化的数据格式(如Pandas DataFrame),并导出到excel文件。
# 假设food_items已经通过get_all_usda_foods函数获取 if food_items: table_data = [] for food_item in food_items: row = { "Description": food_item.get("description", "N/A"), "FDC_ID": food_item.get("fdcId", "N/A") # 添加FDC ID以便追踪 } # 提取营养成分 for nutrient in food_item.get("foodNutrients", []): nutrient_name = nutrient.get("nutrientName") nutrient_value = nutrient.get("value") if nutrient_name and nutrient_value is not None: row[nutrient_name] = nutrient_value table_data.append(row) # 创建DataFrame df = pd.DataFrame(table_data) # 打印一些信息,例如数据框的形状和前几行 print("nDataFrame created:") print(f"Shape: {df.shape}") print("First 5 rows:") print(df.head()) # 导出到Excel output_filename = 'usda_nutritional_facts_all.xlsx' try: df.to_excel(output_filename, index=False) print(f"nData successfully exported to {output_filename}") except Exception as e: print(f"Error exporting to Excel: {e}") else: print("No data to process or export.")
在这个数据处理阶段,我们:
- 遍历food_items列表中的每一个食品字典。
- 为每个食品创建一个行字典,包含Description和FDC_ID等基本信息。
- 遍历foodNutrients列表,将每个营养成分的名称作为列名,其值作为单元格内容。
- 使用pd.DataFrame()将列表中的所有行字典转换为一个Pandas DataFrame。
- 最后,使用df.to_excel()方法将DataFrame导出为Excel文件,index=False表示不将DataFrame的索引写入Excel。
注意事项与最佳实践
- API文档是黄金法则:在与任何API交互之前,务必仔细阅读其官方文档。它是理解API功能、参数、限制和错误代码的唯一权威来源。
- 错误处理:始终在您的代码中包含健壮的错误处理机制。网络不稳定、API密钥失效、请求参数错误等都可能导致程序崩溃。良好的错误处理能提高程序的稳定性和用户体验。
- API密钥安全:切勿将API密钥硬编码到公共代码仓库或客户端代码中。应通过环境变量、配置文件或秘密管理服务来安全地存储和访问API密钥。
- 速率限制:某些API会有请求频率限制(rate limiting),即在一定时间内允许的请求次数。如果超出限制,API会返回错误。虽然USDA FDC API的文档中没有明确提及严格的速率限制,但在进行大量请求时仍需注意。如果遇到此类问题,可能需要引入延迟(time.sleep())来避免被封禁。
- 数据量考量:如果获取的数据量非常大,一次性加载到内存中可能会导致内存溢出。在这种情况下,可以考虑分批处理数据,或直接将数据流式写入文件,而不是先全部收集到内存中再写入。
总结
通过本教程,我们深入探讨了如何利用Python克服USDA食品数据中心API的分页限制,从而获取完整的食品营养数据。核心在于理解API文档中关于pageSize和pageNumber参数的定义,并结合requests.Session进行高效的迭代请求。同时,我们也强调了错误处理、API密钥安全以及API文档的重要性,这些都是构建健壮、可靠的API客户端程序的关键要素。掌握这些技巧,您将能够更有效地从各类API中提取所需数据。