网页爬取时如何动态合并不同列的DataFrame?
问题描述
我正在开发一款网页爬取工具,需要从主表每行对应的链接进入子页面,提取子页面表格数据并合并为完整的DataFrame。问题在于每个子表格的列集合可能不同,且无法预知所有列类型。
我刚学Python一周,尝试多种方案后,目前通过循环生成临时DataFrame并与主DataFrame合并,但卡在合并步骤。以下是我的代码:
# 创建主DataFrame link1 = links[0] url_linked = url_l + link1 page_linked = requests.get(url_linked) soup_linked = BeautifulSoup(page_linked.text, 'lxml') table_linked = soup_linked.find('table', class_="XXXXX") headers_link = [] headers_unique = [] for i in table_linked.find_all('th'): title_link = i.text title_link = map(str, title_link) # 此处存在问题:单个字符串被转成迭代器 headers_link.append(title_link) headers_unique = headers_link mydata_link = pd.DataFrame(columns=headers_link) count = 1 for link in links: url_linked = url_l + link page_linked = requests.get(url_linked) soup_linked = BeautifulSoup(page_linked.text, 'lxml') table_linked = soup_linked.find('table', class_="table table-directory-responsive") row2 = [] n_columns = len(table_linked.find_all('th')) # 填充主DataFrame if count == 1: for j in table_linked.find_all('tr'): row_data = j.find_all('td') row = [i.text for i in row_data] row2.append(row) lenght_link = len(mydata_link) row2.remove(['']) # 移除空行 mydata_link.loc[lenght_link] = row2 # 此处错误:row2是列表的列表,不能直接赋值给一行 print(mydata_link) print('Completato link ' + str(count)) count = count + 1 # 创建临时DataFrame else: headers_test = [] for i in table_linked.find_all('th'): title_test = i.text title_test = map(str, title_test) headers_test.append(title_test) mydata_temp = pd.DataFrame(columns=headers_test) for j in table_linked.find_all('tr'): row_data = j.find_all('td') row = [i.text for i in row_data] row2.append(row) lenght_link = len(mydata_link) row2.remove(['']) mydata_temp.loc[lenght_link] = row2 # 同样的赋值错误 print(mydata_temp) # 尝试合并两个DataFrame headers_unique = set(headers_unique).intersection(headers_test) mydata_link = mydata_link.merge(mydata_temp, on=[headers_unique], how='outer') # merge用法错误,此处不需要关联合并 print(mydata_link) print('Completed link ' + str(count)) count = count + 1
我需要实现的功能是将如下两个DataFrame:
输入DataFrame 1
| A | B | C |
|---|---|---|
| 1 | 2 | 3 |
输入DataFrame 2
| C | A | D | E |
|---|---|---|---|
| 4 | 5 | 6 | 7 |
合并为如下结果:
期望输出DataFrame
| A | B | C | D | E |
|---|---|---|---|---|
| 1 | 2 | 3 | NaN | NaN |
| 5 | NaN | 4 | 6 | 7 |
解决方案
你的核心问题是误用了merge方法——merge用于按关联键合并表(类似SQL JOIN),而你需要的是把不同子页的行纵向拼接,自动补全缺失列的NaN。另外代码里还有表头处理、行数据赋值的细节错误,下面是修正后的方案:
步骤1:封装子页表格提取函数
先写一个通用函数,负责从单个链接提取表格并返回DataFrame,避免重复代码:
import requests from bs4 import BeautifulSoup import pandas as pd def extract_subpage_table(url): page = requests.get(url) soup = BeautifulSoup(page.text, 'lxml') table = soup.find('table', class_="table table-directory-responsive") # 提取干净的表头文本 headers = [th.text.strip() for th in table.find_all('th')] # 提取有效行数据 rows = [] for tr in table.find_all('tr'): tds = tr.find_all('td') # 只保留列数和表头一致的有效行 if len(tds) == len(headers): row = [td.text.strip() for td in tds] rows.append(row) return pd.DataFrame(rows, columns=headers)
步骤2:遍历链接收集并拼接DataFrame
把每个子页的DataFrame收集到列表,最后一次性纵向拼接:
# 假设url_l(主链接前缀)和links(子页链接列表)已定义 all_dfs = [] for idx, link in enumerate(links, 1): full_url = url_l + link df = extract_subpage_table(full_url) all_dfs.append(df) print(f'完成链接 {idx}') # 拼接所有DataFrame,自动合并所有列,缺失值填充NaN final_df = pd.concat(all_dfs, ignore_index=True) print(final_df)
关键说明
- 为什么用
concat而非merge:pd.concat([df1, df2], ignore_index=True)会按顺序拼接所有行,自动识别所有出现过的列,无数据的列自动填充NaN,完全匹配你的需求。 - 表头修正:之前用
map(str, title_link)会把单个字符串转成字符迭代器,导致表头失效,直接用th.text.strip()就能得到干净的表头文本。 - 行数据过滤:通过
len(tds) == len(headers)过滤空行或无效行,比手动删除特定空列表更可靠。 - 代码简化:封装函数后循环逻辑更清晰,新手更容易理解和维护。
内容的提问来源于stack exchange,提问作者Shattered
相关产品推荐
相关产品推荐

