向Excel模板追加数据失败求助:代码报错与文件生成异常
问题排查与解决方案
问题概述
需求:读取含Parcel ID的CSV文件,爬取对应房产信息后插入现有Excel模板,最终保存至指定文件夹。
当前问题:
- 无法将数据写入原有Excel模板,反而生成临时文件
- 报错:
An error occurred while appending the search results to the Excel worksheet.
核心错误分析
- 宽泛的异常捕获:所有
except块未指定具体异常类型,也未打印错误详情,无法定位问题根源。 - Driver资源管理漏洞:
perform_search中driver在try块内初始化,但finally块直接调用driver.quit(),若初始化失败(如Chrome驱动缺失),会触发NameError。 - 地址解析逻辑脆弱:硬编码按逗号/空格拆分地址,实际爬取的地址格式可能不符合预期,引发
IndexError。 - 文件占用冲突:若模板Excel文件处于打开状态,
openpyxl无法覆盖原文件,会生成类似~$EJW Template.xlsx的临时文件,被误认为是新文件。 - 行号匹配风险:
append_search_results中固定使用row_number+6等行号,若CSV行数过多或模板行结构不匹配,会导致写入位置错误。
修复后的完整代码
import os import csv import openpyxl from openpyxl import load_workbook from openpyxl.styles import Alignment from selenium import webdriver from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException, WebDriverException from bs4 import BeautifulSoup import time import pandas as pd def run_search(input_csv, output_excel, output_folder): # 加载输入CSV df = pd.read_csv(input_csv) # 检查模板文件是否存在 if not os.path.exists(output_excel): print(f"错误:模板文件 {output_excel} 不存在") return # 检查输出文件夹是否存在,不存在则创建 if not os.path.exists(output_folder): os.makedirs(output_folder) try: # 加载Excel模板,保留公式和格式 workbook = load_workbook(output_excel, data_only=False) worksheet = workbook['Justification Worksheet'] except Exception as e: print(f"加载Excel模板失败:{str(e)}") return # 遍历CSV每行 for index, row in df.iterrows(): parcel_number = row['Tax_ID'] print(f"正在处理Parcel ID: {parcel_number}") search_results = perform_search(parcel_number) if search_results: # 写入Excel模板 if append_search_results(search_results, worksheet, index): print(f"Parcel ID {parcel_number} 写入成功") # 保存单条结果到CSV save_search_results(search_results, output_folder) # 保存修改后的模板 try: workbook.save(output_excel) print(f"所有结果已保存到模板:{output_excel}") except PermissionError: print("错误:模板文件被占用,请关闭Excel后重试") except Exception as e: print(f"保存Excel失败:{str(e)}") def perform_search(parcel_number): """爬取指定Parcel ID的房产信息""" driver = None try: driver = webdriver.Chrome() driver.get("https://esearch.mobilecopropertytax.com/") time.sleep(3) # 缩短等待时间,可根据网络调整 # 输入Parcel ID并搜索 search_bar = driver.find_element(By.XPATH, '//*[@id="keywords"]') search_bar.send_keys(parcel_number) search_button = driver.find_element(By.XPATH, '/html/body/div[2]/div[1]/div/div[2]/div/div/span/button') search_button.click() time.sleep(3) # 点击搜索结果行 result_row = driver.find_element(By.XPATH, '/html/body/div[3]/div[3]/div[1]/div[2]/table/tbody/tr') result_row.click() time.sleep(3) # 解析页面内容 soup = BeautifulSoup(driver.page_source, 'lxml') # 提取字段 parcel_num = soup.find('th', text='Parcel Number:').find_next_sibling().text.strip() name = soup.find('th', text='Name:').find_next_sibling().text.strip() # 处理地址字段,增加容错 address_elem = soup.find(lambda tag: tag.name=='th' and 'Address:' in tag.text) if not address_elem: raise ValueError("未找到地址字段") address = address_elem.find_next_sibling().text.strip() # 健壮性拆分地址 city = state = zip_code = "" if ',' in address: parts = address.split(',', 1) # 只拆分第一个逗号 city_part = parts[0].strip() city = city_part.split(' ')[-1] if ' ' in city_part else city_part state_zip_part = parts[1].strip() if ' ' in state_zip_part: state, zip_code = state_zip_part.rsplit(' ', 1) # 从右拆分第一个空格 return [parcel_num, name, address, city, state, zip_code] except NoSuchElementException as e: print(f"爬取失败:页面元素未找到 - {str(e)}") return None except WebDriverException as e: print(f"浏览器驱动错误:{str(e)}") return None except Exception as e: print(f"爬取过程出错:{str(e)}") return None finally: if driver: driver.quit() def append_search_results(search_results, worksheet, row_number): """将结果写入Excel指定位置,返回是否成功""" try: # 计算目标行号(根据模板结构调整) base_row = row_number + 6 # 写入对应单元格 worksheet[f'C{base_row}'].value = search_results[0] # Parcel Number worksheet[f'C{base_row+3}'].value = search_results[1] # Name worksheet[f'C{base_row+4}'].value = search_results[2] # Address worksheet[f'C{base_row+5}'].value = search_results[3] # City worksheet[f'E{base_row+5}'].value = search_results[4] # State worksheet[f'G{base_row+5}'].value = search_results[5] # Zip code # 设置单元格对齐 align = Alignment(horizontal='left', vertical='center', shrink_to_fit=False) for cell in [f'C{base_row}', f'C{base_row+3}', f'C{base_row+4}', f'C{base_row+5}', f'E{base_row+5}', f'G{base_row+5}']: worksheet[cell].alignment = align return True except Exception as e: print(f"写入Excel出错:{str(e)}") return False def save_search_results(search_results, output_folder): """保存单条结果到独立CSV""" filename = os.path.join(output_folder, f"{search_results[0]}.csv") with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerow(['Parcel Number', 'Name', 'Address', 'City', 'State', 'Zip Code']) writer.writerow(search_results) # 配置文件路径 input_csv = r'C:\Users\user\Desktop\parcel_numbers.csv' output_excel = r'C:\Users\user\Desktop\EJW Template.xlsx' output_folder = r'C:\Users\user\Desktop\EJW Jeffereson Co Text' # 执行程序 run_search(input_csv, output_excel, output_folder)
关键修复说明
- 精准异常捕获:替换宽泛
except为具体异常类型(如PermissionError、NoSuchElementException),并打印错误详情,便于定位问题。 - Driver安全管理:初始化
driver为None,finally块先判断driver是否存在再调用quit(),避免未初始化时报错。 - 健壮的地址解析:使用
split(',',1)和rsplit(' ',1)拆分地址,避免多逗号/多空格导致的索引错误,增加字段检查。 - 文件占用处理:捕获
PermissionError,明确提示用户关闭Excel文件。 - 日志输出:增加处理过程的打印信息,便于跟踪进度。
内容的提问来源于stack exchange,提问作者Mason
相关产品推荐
相关产品推荐

