You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python爬虫脚本问题:如何从函数返回多个值优化Garmin数据爬取

问题解决与代码优化

1. 函数返回多个值的正确方式

你用return result_2 and hr_value是错误的——这是逻辑运算,只会返回最后一个为真的值,而非两个独立结果。Python中返回多个值只需用逗号分隔,返回一个元组即可:

return result_2, hr_value

调用时直接将元组解包到两个变量,这样只需要执行一次搜索,不用重复调用函数:

distance_walked, heart_rate_walk = get_distance_and_hr("Walking")

2. 心率提取的潜在问题修复

核心问题点:

  • XPath定位可能失效:Garmin页面结构若有变动,div[5]//div[4]未必对应心率模块,建议基于元素属性或文本特征定位;
  • 文本处理不健壮:rstrip(" bpm")无法适配非标准格式的文本,改用replace或split更稳妥;
  • 未处理元素缺失情况:若找不到心率元素,直接读取val.text会抛出异常,需增加异常捕获或存在性判断。

修改后的get_distance_and_hr函数:

def get_distance_and_hr(activity):
    time.sleep(2)
    search_box = driver.find_element("name", "search")
    search_box.clear()
    search_box.send_keys(activity)
    search_box.send_keys(Keys.RETURN)

    time.sleep(3)
    total_distance = 0.0
    total_hr = 0
    count_hr = 0  # 统计有效心率数据的次数,用于计算平均值

    # 定位当天的所有活动项
    activity_items = driver.find_elements(By.XPATH, f"//span[text()='{x}']//ancestor::div[@class='list-item-container']")
    
    for item in activity_items:
        # 提取距离
        try:
            distance_elem = item.find_element(By.XPATH, ".//div[5]//div[1]//span//span[1]")
            if distance_elem.text:
                distance_val = float(distance_elem.text.replace(" km", ""))
                total_distance += distance_val
        except:
            pass  # 无距离数据则跳过
        
        # 提取心率
        try:
            hr_elem = item.find_element(By.XPATH, ".//div[5]//div[4]//span//span")
            if hr_elem.text and "bpm" in hr_elem.text:
                hr_val = float(hr_elem.text.replace(" bpm", ""))
                total_hr += hr_val
                count_hr += 1
        except:
            pass  # 无心率数据则跳过
    
    # 计算平均心率,避免除以0的情况
    avg_hr = total_hr / count_hr if count_hr > 0 else 0
    return total_distance, avg_hr

3. 其他优化点

  • 补充缺失的webdriver导入:原代码中缺少from selenium import webdriver,会导致运行报错;
  • 用显式等待替代固定time.sleep:比如WebDriverWait(driver, 10).until(EC.presence_of_element_located(...)),比固定等待时间更可靠;
  • 脚本结束后关闭浏览器:添加driver.quit()释放资源。

完整修改后的代码

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import login as login
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import datetime
import time
from selenium import webdriver  # 补充缺失的导入

x = datetime.datetime.now()
x = x.strftime("%b %d")

driver = webdriver.Firefox()
driver.set_window_size(1512, 1000)
driver.get("https://connect.garmin.com/modern/activities")

# 显式等待登录iframe加载
iframe = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "gauth-widget-frame-gauth-widget"))
)
driver.switch_to.frame(iframe)

# 登录操作
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.NAME, "username"))).send_keys(login.username)
password_box = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.NAME, "password")))
password_box.send_keys(login.password)
password_box.send_keys(Keys.RETURN)

driver.switch_to.default_content()

# 等待主页搜索框加载完成
WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.NAME, "search")))

def get_distance_and_hr(activity):
    search_box = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.NAME, "search")))
    search_box.clear()
    search_box.send_keys(activity)
    search_box.send_keys(Keys.RETURN)

    # 等待搜索结果加载
    WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.XPATH, f"//span[text()='{x}']//ancestor::div[@class='list-item-container']")))
    
    total_distance = 0.0
    total_hr = 0
    count_hr = 0

    activity_items = driver.find_elements(By.XPATH, f"//span[text()='{x}']//ancestor::div[@class='list-item-container']")
    
    for item in activity_items:
        # 提取距离
        try:
            distance_elem = item.find_element(By.XPATH, ".//div[5]//div[1]//span//span[1]")
            if distance_elem.text:
                distance_val = float(distance_elem.text.replace(" km", ""))
                total_distance += distance_val
        except:
            pass
        
        # 提取心率
        try:
            hr_elem = item.find_element(By.XPATH, ".//div[5]//div[4]//span//span")
            if hr_elem.text and "bpm" in hr_elem.text:
                hr_val = float(hr_elem.text.replace(" bpm", ""))
                total_hr += hr_val
                count_hr += 1
        except:
            pass
    
    avg_hr = total_hr / count_hr if count_hr > 0 else 0
    return total_distance, avg_hr

# 一次调用获取距离和心率
distance_walked, heart_rate_walk = get_distance_and_hr("Walking")

print(f"Distance walked today is {distance_walked:.2f} km and average BPM is {heart_rate_walk:.1f}")

# 关闭浏览器释放资源
driver.quit()

内容的提问来源于stack exchange,提问作者Zeno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 17:45:37