如何确保for循环中DataFrame的所有元素通过to_sql写入PostgreSQL?
解决循环写入PostgreSQL仅保留最后一条数据的问题
问题根源分析
你代码里的几个关键错误导致了最终只有最后一条数据写入数据库:
if_exists='replace'的误用:每次循环调用to_sql都用replace参数,会直接删除原表并重建,覆盖之前所有写入的数据,自然只剩最后一次的结果。- 引擎创建位置错误:你在循环内部重复创建
engine,不仅完全冗余,还可能导致连接异常,引擎只需要在循环外初始化一次即可。 - 变量笔误:代码里的
df2明显是当前循环的df,这个错误会导致你取错数据。 - 列不匹配问题:合并DataFrame失败,大概率是不同页面提取的表结构不一致,直接合并时列无法对齐。
修复后的代码示例
方案1:循环追加写入(适合表结构一致的场景)
先在循环外初始化数据库引擎,循环内用append模式追加数据,同时修正变量错误:
import pandas as pd from sqlalchemy import create_engine import camelot # 仅初始化一次数据库引擎 engine = create_engine(f'postgres://你的用户名:密码@主机地址:端口号/数据库名') pdf_path = "你的PDF文件路径" tables = camelot.read_pdf(pdf_path, pages='all', flavor='stream') # 先创建表结构(可选,确保表存在) first_table = tables[0].df init_df = pd.DataFrame() init_df['nr'] = first_table[0].values.astype(str).flatten().tolist() init_df.to_sql('new_pilsCorner4', engine, if_exists='replace', index=False) # 循环追加剩余数据 for table in tables[1:-1]: df = table.df df3 = pd.DataFrame() df3['nr'] = df[0].values.astype(str).flatten().tolist() df3.to_sql('new_pilsCorner4', engine, if_exists='append', index=False)
方案2:统一整理数据后写入(解决列不匹配问题)
如果不同页面的表结构有差异,先把所有数据整理成统一结构再一次性写入:
import pandas as pd from sqlalchemy import create_engine import camelot engine = create_engine(f'postgres://你的用户名:密码@主机地址:端口号/数据库名') pdf_path = "你的PDF文件路径" tables = camelot.read_pdf(pdf_path, pages='all', flavor='stream') # 收集所有需要的数据 all_nr_data = [] for table in tables[:-1]: df = table.df # 只提取需要的第一列,转成字符串并扁平化 col_data = df[0].values.astype(str).flatten().tolist() all_nr_data.extend(col_data) # 生成统一结构的DataFrame后写入 final_df = pd.DataFrame({'nr': all_nr_data}) final_df.to_sql('new_pilsCorner4', engine, if_exists='replace', index=False)
关键注意事项
- 确保循环内的数据提取逻辑一致,比如都取第0列,避免因列数不同导致写入或合并失败。
- 添加
index=False可以避免把DataFrame的索引写入数据库,减少不必要的冗余列。 - 如果表结构差异较大,优先用方案2,先统一清洗数据再写入,避免列对齐问题。
内容的提问来源于stack exchange,提问作者user42141
相关产品推荐
相关产品推荐

