如何修复Vanguard共同基金web scraping代码运行数据不一致问题
问题定位
你遇到的偶发数据丢失问题核心有三个原因:
- 等待逻辑不合理:使用绝对路径XPATH等待固定行号加载,不同基金表格行数不一致,且绝对路径极易受页面结构微调影响,经常出现表格内容未完全渲染就触发了抓取逻辑,导致键名文本缺失,只剩下值。
- 元素选择容错性差:直接用索引取第三个
role="presentation"的表格,页面加载顺序或者结构稍有变动,就会取错表格,自然拿不到对应字段。 - 无校验重试机制:抓取完成后没有校验核心字段是否存在,哪怕数据不全也直接往下执行。
修复方案
1. 替换等待逻辑
不要用绝对路径的结构等待,改用业务字段等待,确保你需要的核心字段已经渲染完成再执行抓取:
# 替换原有WebDriverWait行,等待两个核心字段都出现在页面中 WebDriverWait(browser, 20).until(EC.text_to_be_present_in_element((By.TAG_NAME, 'body'), 'Average effective maturity')) WebDriverWait(browser, 20).until(EC.text_to_be_present_in_element((By.TAG_NAME, 'body'), 'Average duration'))
2. 优化表格定位逻辑
不要通过索引取表格,改为通过表格内的特征字段匹配,确保取到的是目标概览表:
# 原有htmlData = mySoup.findAll(...) 及之后行替换为如下逻辑 htmlData = mySoup.findAll('table',{'role':'presentation'}) overview_table = None for table in htmlData: table_content = table.get_text() if 'Fund total net assets' in table_content and 'Average duration' in table_content: overview_table = table break if not overview_table: print(f"{symbol} 未找到目标概览表,跳过") continue overviewDataList = extractOverviewTable(overview_table)
3. 增加重试与校验机制
给每个基金增加最多3次重试逻辑,抓取完成后校验核心字段是否存在,不存在就重试:
for symbol in symbols: max_retry = 3 retry_count = 0 overviewDataList = None while retry_count < max_retry: browser.get(url_vanguard.format(symbol)) print(symbol) try: # 等待核心字段加载 WebDriverWait(browser, 20).until(EC.text_to_be_present_in_element((By.TAG_NAME, 'body'), 'Average duration')) html = browser.page_source mySoup = BeautifulSoup(html, 'html.parser') # 定位概览表 htmlData = mySoup.findAll('table',{'role':'presentation'}) overview_table = None for table in htmlData: table_content = table.get_text() if 'Fund total net assets' in table_content and 'Average duration' in table_content: overview_table = table break if not overview_table: retry_count +=1 continue # 提取数据 overviewDataList = extractOverviewTable(overview_table) # 校验核心字段是否存在 if 'Average effective maturity' in overviewDataList and 'Average duration' in overviewDataList: break except TimeoutException: print(f"{symbol} 加载超时,第{retry_count+1}次重试") retry_count +=1 if not overviewDataList or 'Average duration' not in overviewDataList: print(f"{symbol} 多次重试后仍无法获取完整数据") continue # 后续业务逻辑
4. 优化提取函数容错性
避免空行、无span元素的情况导致提取失败:
def extractOverviewTable(htmlTable): table = htmlTable.find('tbody') rows = table.findAll('tr') returnDict = {} for row in rows: cols = row.findAll('td') if len(cols) < 2: continue # 兼容没有span的场景 key_span = cols[0].find('span') if key_span: key = key_span.text.replace('\n', '').strip() else: key = cols[0].text.replace('\n', '').strip() value = cols[1].text.replace('\n', '').strip() if 'Layer' in key: key = key[:key.index('Layer')].strip() if not key: continue returnDict[key] = value return returnDict
内容的提问来源于stack exchange,提问作者Ben Cole
相关产品推荐
相关产品推荐

