news 2026/7/3 21:53:58

爬虫数据清洗与存储——从爬下来到用得上的最后一公里

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
爬虫数据清洗与存储——从爬下来到用得上的最后一公里

爬虫最难的不是把数据爬下来,而是把乱七八糟的原始数据洗干净存好。这一篇讲爬虫后处理的完整流程——清洗、去重、存储。

一、脏数据的常见问题

爬下来的原始数据基本是这样的:

data=[{"title":" iPhone 15 ","price":" ¥6,999 ","stock":"100件"},{"title":"华为 Mate 60","price":"null","stock":"50"},{"title":"","price":"5.99","stock":"-1"},{"title":"小米 14","price":"3999","stock":"abc"},None,]

问题清单:

  • 空格、换行符、特殊符号混在字符串里
  • 价格带货币符号和逗号(“¥6,999”)
  • 空值、None、null 混杂
  • 数据类型不对(库存是数字但爬下来是字符串)
  • 脏数据(负数库存、空标题)
  • 重复数据

二、pandas 数据清洗三板斧

1. 去除空格和特殊字符

importpandasaspd df=pd.DataFrame(data)# 去除字符串两端的空格和换行df["title"]=df["title"].str.strip()# 去除所有空格(有些用全角空格)df["title"]=df["title"].str.replace(r"\s+","",regex=True)# 清洗价格字段:去符号、去逗号df["price"]=df["price"].str.replace(r"[¥¥$,]","",regex=True)df["price"]=pd.to_numeric(df["price"],errors="coerce")# errors="coerce":遇到不能转的变成 NaN# 清洗库存df["stock"]=pd.to_numeric(df["stock"],errors="coerce")print(df.dtypes)# 确认类型已经转成数字

2. 处理空值

# 查看哪些列有空值print(df.isnull().sum())# 删除空值过多的行df=df.dropna(subset=["title"])# 标题为空就删掉# 填充数值列的空值df["price"]=df["price"].fillna(0)df["stock"]=df["stock"].fillna(0)# 或者用均值填充df["price"]=df["price"].fillna(df["price"].mean())# 填充字符串列df["brand"]=df["brand"].fillna("未知")

3. 去重

# 查看重复行数print(df.duplicated().sum())# 删除完全重复的行df=df.drop_duplicates()# 指定列去重(保留第一次出现的)df=df.drop_duplicates(subset=["title"])# 查看某列重复值print(df["title"].value_counts())# 过滤异常数据df=df[df["price"]>0]# 价格必须大于0df=df[df["stock"]>=0]# 库存不能是负数

4. 常用数据转换

# 类型转换df["price"]=df["price"].astype(float)df["stock"]=df["stock"].astype(int)# 处理时间df["crawl_time"]=pd.to_datetime(df["crawl_time"])# 提取字段中的信息df["city"]=df["address"].str.extract(r"(北京|上海|广州|深圳|郑州)")# 统一文本格式(如品牌名统一小写)df["brand"]=df["brand"].str.lower().str.strip()

三、存储到 MySQL

1. pandas 直接写入(最简单)

fromsqlalchemyimportcreate_engineimportpandasaspd# 连接 MySQLengine=create_engine("mysql+pymysql://root:123456@localhost:3306/spider_db?charset=utf8mb4")# DataFrame 直接写入 MySQL 表# 如果表已存在,replace 替换,append 追加df.to_sql(name="products",# 表名con=engine,if_exists="append",# 追加模式index=False,# 不保存 DataFrame 的索引chunksize=1000,# 分批写入,每批1000条)print("数据已写入 MySQL")

2. 逐条写入(适合自定义处理)

importpymysql conn=pymysql.connect(host="localhost",port=3306,user="root",password="123456",database="spider_db",charset="utf8mb4",)cursor=conn.cursor()# 批量插入sql="""INSERT INTO products (title, price, brand, stock, crawl_time) VALUES (%s, %s, %s, %s, %s)"""data_list=[("iPhone 15",6999,"Apple",100,"2026-06-26"),("华为 Mate 60",5999,"华为",50,"2026-06-26"),]cursor.executemany(sql,data_list)conn.commit()print(f"插入了{cursor.rowcount}条数据")cursor.close()conn.close()

3. 更新 vs 插入(去重逻辑)

重复爬取时,同一件商品可能出现两次。处理方式:

# 方案一:INSERT IGNORE(主键或唯一索引冲突时跳过)sql="INSERT IGNORE INTO products (id, title, price) VALUES (%s, %s, %s)"# 方案二:REPLACE INTO(冲突时替换)sql="REPLACE INTO products (id, title, price) VALUES (%s, %s, %s)"# 方案三:ON DUPLICATE KEY UPDATE(冲突时更新指定字段)sql="""INSERT INTO products (id, title, price, crawl_time) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE price = VALUES(price), crawl_time = VALUES(crawl_time)"""

推荐方案三,既保留旧数据,又更新最新价格和爬取时间。

四、存储到 MongoDB

MongoDB 适合结构不固定的爬虫数据——不同商品字段可能不一样(手机有"像素",冰箱有"容积"),用 MongoDB 不用提前建表。

1. 写入

frompymongoimportMongoClient# 连接 MongoDBclient=MongoClient("localhost",27017)db=client["spider_db"]collection=db["products"]# 插入一条collection.insert_one({"title":"iPhone 15","price":6999,"brand":"Apple","crawl_time":"2026-06-26",})# 批量插入(直接传 DataFrame)collection.insert_many(df.to_dict("records"))print(f"插入了{len(df)}条")

2. 去重更新

# 以 title 为唯一标识,有则更新,无则插入for_,rowindf.iterrows():collection.update_one({"title":row["title"]},# 查询条件{"$set":row.to_dict()},# 更新内容upsert=True,# 不存在就插入)

3. 查询

# 价格大于5000的商品forproductincollection.find({"price":{"$gt":5000}}):print(product["title"],product["price"])# 按品牌统计数量pipeline=[{"$group":{"_id":"$brand","count":{"$sum":1}}},{"$sort":{"count":-1}},]forresultincollection.aggregate(pipeline):print(result["_id"],result["count"])

五、MySQL vs MongoDB 怎么选

MySQLMongoDB
数据格式需要提前建表,字段固定JSON 格式,字段灵活
爬虫场景同一种商品,字段稳定不同来源,字段不一致
查询适合关联查询、统计适合存和查,不适合复杂关联
上手需要建表写 SQL直接 insert,零门槛

实际建议:

  • 爬电商商品(字段固定)→MySQL
  • 爬多种网站,数据格式不统一 →MongoDB
  • 小数据量(几千条)→JSON 文件就够了

六、完整流水线示例

importpandasaspdfromsqlalchemyimportcreate_engineimportrequestsfrombs4importBeautifulSoupimporttimedefcrawl_products():"""爬虫 → 清洗 → 存储 一条龙"""# 1. 爬取all_data=[]forpageinrange(1,6):resp=requests.get(f"https://example.com/products?page={page}")soup=BeautifulSoup(resp.text,"html.parser")foriteminsoup.select(".product"):all_data.append({"title":item.select_one(".title").text,"price":item.select_one(".price").text,"brand":item.select_one(".brand").text,"stock":item.select_one(".stock").text,})time.sleep(1)print(f"爬取完成,共{len(all_data)}条原始数据")# 2. 清洗df=pd.DataFrame(all_data)df["title"]=df["title"].str.strip()df["price"]=df["price"].str.replace(r"[¥¥$,]","",regex=True)df["price"]=pd.to_numeric(df["price"],errors="coerce")df["stock"]=pd.to_numeric(df["stock"],errors="coerce")df=df.dropna(subset=["title"])df=df.drop_duplicates(subset=["title"])df=df[df["price"]>0]df["crawl_time"]=pd.Timestamp.now()print(f"清洗后剩余{len(df)}条有效数据")# 3. 存储engine=create_engine("mysql+pymysql://root:123456@localhost:3306/spider_db?charset=utf8mb4")df.to_sql("products",con=engine,if_exists="append",index=False)print("数据已保存到 MySQL")returndfif__name__=="__main__":crawl_products()

总结

爬虫的核心公式:

爬下来 → 清洗(去空格/去重/类型转换/去异常) → 存储(MySQL/MongoDB/文件) ↑ ↑ 最耗时的环节 按场景选

很多人把精力都放在爬取上,结果数据存进去一堆脏数据,后面分析根本没法用。洗数据花的功夫,往往比写爬虫还多,但这步做好了,后面的分析才会顺利。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 0:28:15

OpenSpec 最佳实践:从“凭感觉”到“照单执行”

OpenSpec 在 Trae 与 Cursor 上的最佳实践:从“凭感觉”到“照单执行” 引言:AI 编程的“凭感觉”困境 想象一下这个场景:周四下午,产品经理找到你:“帮忙加个用户管理功能吧,就是基本的增删改查&#xff0…

作者头像 李华
网站建设 2026/6/29 1:20:31

会展展具租赁选型参考与线下营销落地总包服务适用人群

会展展具租赁中的常见误区与成本控制企业在筹备展会时,往往容易将注意力集中在视觉设计或宣传物料上,而忽视了基础硬件的选型。会展展具租赁作为线下活动的基础设施,其品质直接影响参会体验。在实际操作中,许多策划方存在两个主要…

作者头像 李华
网站建设 2026/6/29 0:28:16

m3u8 视频在线提取,打开浏览器就能用

文章目录m3u8 视频在线提取,打开浏览器就能用m3u8 视频在线提取,打开浏览器就能用 GitHub 上有一个 m3u8 视频下载工具,Star 数超过 7000。 m3u8 是一种常见的视频格式,原理是把完整视频拆成多个 .ts 碎片文件,再用一…

作者头像 李华
网站建设 2026/6/29 1:00:53

客户拜访后攒了大量参考短视频,2026怎么总结短视频内容降本指南

先回答用户真正关心的问题 针对销售客服攒的大量客户拜访参考短视频,2026总结内容降本的核心方法是:不用手动逐帧听译整理,匹配自身使用频率和协作需求选对应AI转写总结工具即可。大部分日常场景用免费额度就能覆盖,可将单条拜访…

作者头像 李华
网站建设 2026/6/29 0:28:18

RAG的技术发展

RAG 技术已从早期的‌朴素检索(Naive RAG)‌演进为具备‌结构化推理‌与‌智能体自主决策‌的复杂系统。当前主流技术范式及演进逻辑如下: Naive RAG(原生/基础 RAG)‌ ‌定义‌:最基础形态,流程…

作者头像 李华
网站建设 2026/6/29 0:28:21

告别低效搬砖!实测2026社区版AI智能体:个人与工作室的自动化“真香”还是“深坑”?

随着2026年全球智能体(Agent)技术的爆发式普及,我们正处于从“人人都有大模型”向“人人都有数字员工”跨越的关键节点。对于预算有限的个人开发者和初创小工作室而言,市面上琳琅满目的“免费社区版”智能体似乎成了降本增效的救命…

作者头像 李华