
本文旨在解决使用python抓取数据并追加到pandas dataframe时遇到的“列不匹配”错误。当抓取到的数据行长度不一致,导致与dataframe期望的列数不符时,此错误会中断数据处理。文章将详细介绍两种有效的解决方案:跳过不完整数据行,或用`np.nan`填充缺失列,并强调采用高效的数据收集与dataframe构建方法,以优化性能。
在进行数据抓取(例如从HTML表格中解析数据)并尝试将其追加到Pandas DataFrame时,一个常见的问题是源数据的不一致性。具体来说,当某些抓取到的数据行包含的列数少于DataFrame预期的列数时,Pandas会抛出cannot set a row with mismatched columns错误,从而中断整个数据处理流程。这通常发生在数据源本身不规范,存在部分数据缺失或结构不完整的情况下。
考虑以下场景,我们尝试从HTML中提取年份数据(2020-2023)并构建一个DataFrame:

import pandas as pd
import numpy as np # 稍后会用到
# 假设 GDP_2020 是一个BeautifulSoup解析后的HTML表格行列表
# 模拟数据,实际应通过BeautifulSoup的find_all('td')获取
# 示例数据:
# Afghanistan 20,136 14,941 19,083 23,032 (完整)
# Albania 15,192 17,984 (不完整)
# Algeria 145,656 163,138 195,060 224,107 (完整)
# 原始尝试可能导致错误的代码结构
years = ['2020','2021','2022','2023']
GDP = pd.DataFrame(columns=years) # 声明了4列
# 模拟的行数据,其中包含不完整的行
mock_rows_data = [
['Afghanistan', '20,136', '14,941', '19,083', '23,032'],
['Albania', '15,192', '17,984'], # 缺少2022, 2023年的数据
['Algeria', '145,656', '163,138', '195,060', '224,107']
]
# 原始代码片段,会导致错误:
# for row_data_list in mock_rows_data[1:]: # 假设第一个是标题,这里从第二个开始
# # 模拟从BeautifulSoup提取的inpidual_row_data
# inpidual_row_data = row_data_list[1:] # 假设第一个是国家名,这里只取年份数据
# length = len(GDP)
# GDP.loc[length] = inpidual_row_data # 当inpidual_row_data长度不为4时会报错登录后复制
上述代码尝试将长度不一致的inpidual_row_data直接赋值给DataFrame的行,当数据行长度与DataFrame列数不匹配时,就会引发错误。
为了有效地处理这种数据不一致性,我们介绍两种主要的策略,并结合数据抓取最佳实践进行优化。
1. 策略一:跳过不完整的数据行
如果业务需求明确要求只处理拥有完整数据集的行,那么最直接的方法是检查每行数据的列数,只追加符合预期长度的行。
实现步骤:
- 定义预期的列名和列数。
- 在循环中抓取每行数据。
- 检查当前行数据的列数是否与预期的列数一致。
- 如果一致,则将数据添加到临时列表中。
- 循环结束后,使用收集到的完整数据一次性创建DataFrame。
示例代码:
years = ['2020','2021','2022','2023']
expected_col_count = len(years)
all_row_data = [] # 用于存储所有符合条件的行数据
# 模拟 GDP_2020 是BeautifulSoup解析后的HTML表格行列表
# 假设每行数据的第一个元素是国家名,后续是年份数据
mock_html_rows = [
['Country (or dependent territory)', '2020', '2021', '2022', '2023'], # 标题行
['Afghanistan', '20,136', '14,941', '19,083', '23,032'],
['Albania', '15,192', '17,984'], # 不完整行
['Algeria', '145,656', '163,138', '195,060', '224,107']
]
# 假设我们只关心年份数据,所以每行实际数据比期望的年份列数多一列(国家名)
# 因此,如果包含国家名,期望的长度是 expected_col_count + 1
# 如果只取年份数据,则期望长度就是 expected_col_count
for row_elements in mock_html_rows[1:]: # 从第二行开始处理数据
# 假设 row_elements 是通过 row.find_all('td') 得到的列表
# 并且我们只提取年份数据,忽略国家名
# 模拟从HTML元素中提取文本并去除空格
inpidual_row_data = row_elements[1:] # 假设第一个元素是国家名,我们只取年份数据
if len(inpidual_row_data) == expected_col_count:
all_row_data.append(inpidual_row_data)
GDP_skipped = pd.DataFrame(all_row_data, columns=years)
print("--- 策略一:跳过不完整行 ---")
print(GDP_skipped)登录后复制
优点:
- 代码逻辑简单明了。
- 生成的DataFrame只包含结构完整的数据,无需额外的清理。
缺点:
- 会丢失部分数据,如果这些不完整的数据在业务上仍有价值,则不适用。
2. 策略二:用np.nan填充缺失数据
如果需要保留所有抓取到的行,即使它们不完整,那么可以将缺失的列用np.nan(Not a Number)填充,使其与DataFrame的列数匹配。
还木有评论哦,快来抢沙发吧~