Pandas如何通过apply调用自定义函数基于单列生成多列
Pandas 映射类方法区别
先明确三个易混淆方法的适用场景:
map:仅支持Series对象,对单列逐元素做单值映射,输入单个元素返回单个结果,无法直接生成多列apply:兼容Series和DataFrame对象:作用在Series上时逐元素运算,支持返回元组/列表形式的多结果;作用在DataFrame上时可指定按行/按列做聚合计算,是当前单列派生多列场景的适配方法applymap:仅支持DataFrame对象,对整个表的所有单元格逐元素做统一处理,适合全表元素的格式转换类操作,不适合单列派生多列的需求
多返回值派生多列实现
你的原有爬取函数不需要做任何修改,直接通过如下写法即可在原表基础上新增3列对应爬取结果:
# 直接对url列执行apply,将返回的元组序列转为列表后赋值给新列 df[['row4', 'row5', 'row6']] = df['url'].apply(get_stuff_from_url).tolist()
逻辑说明:
- 不需要额外套lambda表达式,
apply会自动将url列的每个字符串传入爬取函数,此时得到的是每行存储一个三元组的Series对象 - 调用
.tolist()后会将三元组序列拆为[(data1,data2,data3), ...]格式的嵌套列表,pandas会自动按顺序将三个值匹配到你指定的三个新列中,原有列会完整保留,不需要额外做表合并操作
爬取场景优化建议
网页爬取存在网络波动、页面结构变动的可能,建议给爬取函数增加异常捕获和空值判断,避免单个url请求失败导致整个计算流程中断:
import time import requests from bs4 import BeautifulSoup def get_stuff_from_url(url: str, retry=2): for i in range(retry+1): try: # 增加超时设置和请求头,避免请求卡死或被反爬拦截 resp = requests.get(url, timeout=10, headers={"User-Agent": "Mozilla/5.0"}) resp.raise_for_status() soup = BeautifulSoup(resp.text, "html.parser") # 增加标签存在性判断,避免页面结构变动时取text报错 data1 = soup.find('div', {'class': 'stuff1'}) data2 = soup.find('span', {'class': 'stuff2'}).text if soup.find('span', {'class': 'stuff2'}) else None data3 = soup.find('p', {'class': 'stuff3'}).text if soup.find('p', {'class': 'stuff3'}) else None return data1, data2, data3 except Exception as e: if i == retry: print(f"url {url} 爬取失败,错误信息:{str(e)}") return None, None, None time.sleep(1) # 失败后重试间隔1秒
内容的提问来源于stack exchange,提问作者thream
相关产品推荐
相关产品推荐

