利用Python高效获取USDA食品营养数据:API分页处理详解(高效.分页.详解.获取.营养...)

wufei123 发布于 2025-08-29 阅读(6)

利用Python高效获取USDA食品营养数据:API分页处理详解

本教程详细讲解如何利用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.")
代码解析与优化点
  1. requests.Session的使用: requests.Session对象可以在多次请求中保持某些参数(如cookies、请求头),并且会重用底层的TCP连接,这对于进行多次API请求(如分页)来说,可以显著提高效率和性能。

  2. pageSize参数优化: 我们将pageSize直接设置为API允许的最大值200。这样做可以减少需要发起的API请求总数,从而缩短数据获取时间。

  3. 分页逻辑:

    • 首先发起一个请求来获取第一页的数据,并从响应中提取totalPages(总页数)。
    • 将第一页的数据添加到all_foods列表中。
    • 然后,使用for循环从第2页开始(因为第1页已经获取),一直迭代到totalPages。
    • 在每次循环中,通过添加&pageNumber={page_num}参数来构造新的URL,并发送请求获取当前页的数据。
    • 将每页获取到的foods列表扩展到all_foods中。
  4. 健壮的错误处理:

    • try...except块用于捕获各种可能发生的网络和API错误,例如requests.exceptions.HTTPError(HTTP状态码非200)、requests.exceptions.ConnectionError(连接失败)、requests.exceptions.Timeout(请求超时)以及json.JSONDecodeError(JSON解析失败)。
    • response.raise_for_status()是一个便捷的方法,如果HTTP请求返回了错误的状态码(如4xx或5xx),它会抛出一个HTTPError异常。
数据处理与导出

获取到完整的食品数据列表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.")

在这个数据处理阶段,我们:

  1. 遍历food_items列表中的每一个食品字典。
  2. 为每个食品创建一个行字典,包含Description和FDC_ID等基本信息。
  3. 遍历foodNutrients列表,将每个营养成分的名称作为列名,其值作为单元格内容。
  4. 使用pd.DataFrame()将列表中的所有行字典转换为一个Pandas DataFrame。
  5. 最后,使用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中提取所需数据。

以上就是利用Python高效获取USDA食品营养数据:API分页处理详解的详细内容,更多请关注知识资源分享宝库其它相关文章!

标签:  高效 分页 详解 

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。