
简介面向Python爬虫与数据分析学习者以淘宝热销零食商品为应用场景提供从网页采集、数据清洗到可视化分析的完整实践方案非常适合作为课程设计或毕业设计的参考素材。资源共包含十六个文件覆盖可运行的爬虫源码py、用于交互式数据分析的Jupyter Notebook脚本ipynb、抓取后的CSV数据集以及介绍环境配置与运行步骤的Word文档docx压缩包整体仅3.09MB轻量易用。爬虫部分抓取商品名称、价格、销量、店铺所在省份与城市、是否包邮、满减活动等公开透明信息数据分析环节则完成了数据清洗、热销商品排行、店铺地区分布统计和产品词云绘制脉络清晰。已有87人学习过本资源源码注释细致完整并附有详细使用说明文档可帮助快速复现淘宝零食数据的采集与分析流程也便于在掌握思路后自行修改拓展实现更个性化的数据分析需求。1. python 爬虫抓淘宝热销零食先想清楚分析终点再写代码做电商运营或竞品分析的人多半经历过这种场景想看看淘宝热销零食的价格带和店铺分布打开页面一页页翻把“月销7000”“券后19.9”手工抄进 Excel。零食品类 SKU 多、销量数字大、标题词密集手工整理一小时结论往往还是“大家卖得都差不多”。标题里这条链路是标准数据管道用 Python 抓商品列表用 pandas 把“月销7000”洗成可计算数字按销量和店铺聚合再用词云还原商品标题里的高频卖点。它不绑定零食品类换护肤、数码配件也能复用。适合会用 Python 但没跑过完整数据链路的工程师也适合被透视表反复折磨的运营。需要提醒的是淘宝页面访问依赖登录态和平台风控下文代码用于低频学习与内部分析生产环境优先走官方开放平台接口。2. 用 requests 搭零食商品抓取骨架字段先于代码定型2.1 抓哪几个字段决定词云和销量分析能不能落地爬虫代码最容易写字段设计最容易返工。写抓取脚本前先对着分析目标列出字段表后面清洗能少改一轮。抓零食热销商品我会固定抓下面五列刚好覆盖销量分布、店铺集中度和标题词云三类分析。字段原始形态示例分析用途title某零食大礼包分词、词云输入price券后19.9、19.9-39.9价格带聚合sales月销7000、已售10万销量分布、Top 排序shop_name三只松鼠官方旗舰店店铺维度聚合item_urlitem.taobao.com/item.htm?idxxx去重主键、后续详情采集字段越少清洗成本越低。第一次跑通只抓这五列足够出三张核心分析图。跑通后再按需扩展商品链接、发货地、评论数代码结构不用推翻。2.2 最小可跑通的抓取代码接口地址配置化淘宝搜索页有登录态和签名校验最常见且可靠的做法不是硬编码接口而是打开浏览器开发者工具手动搜索一次把带完整参数的搜索接口地址复制出来作为配置项填进代码。后续接口失效重新复制一次即可。import requests import pandas as pd from urllib.parse import quote # 从浏览器复制搜索接口完整地址存入配置文件避免把参数写死在代码里 # 以 q零食page1 结尾的地址为例 URL_TEMPLATE open(config/search_api.txt, encodingutf-8).read().strip() HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36, Referer: https://www.taobao.com/, Accept: application/json, } def parse_cookie(cookie_str: str) - dict: cookies {} for item in cookie_str.split(;): if in item: k, v item.strip().split(, 1) cookies[k] v return cookies def fetch_snack_page(keyword: str, page: int 1, cookie_str: str ): # 关键词做 URL 编码替换掉模板里的零食与页码 url URL_TEMPLATE.replace(%E9%9B%B6%E9%A3%9F, quote(keyword)) url url.replace(page1, fpage{page}) resp requests.get(url, headersHEADERS, cookiesparse_cookie(cookie_str), timeout10) resp.raise_for_status() return resp.json()这段代码的逻辑是先用 requests 发送 GET 请求返回的 JSON 交给解析阶段。TEMPLATE来自抓包关键词需要 URL 编码所以用quote(keyword)替换页码替换则直接改 URL 里的page1。parse_cookie把浏览器复制的 Cookie 字符串转成 dictrequests 会以 Cookie 形式随请求发送。注意替换关键词后如果返回签名错误说明接口参数对登录态敏感那就按新关键词重新抓一次 URL 再跑这是接口类爬虫的正常维护节奏。2.2.1 解析返回结果兼容字段名标题去掉高亮标签def parse_items(data: dict) - list: rows [] # 淘宝接口的字段名因版本变化会出现差异读取时优先用新字段名取不到再降级 for item in data.get(data, {}).get(itemsArray, []): title item.get(title, ) title title.replace(span classH, ).replace(/span, ) rows.append({ title: title, price: item.get(price, ) or item.get(priceText, ), sales: item.get(realSales, ) or item.get(sell, ), shop_name: item.get(shopName, ), item_url: https://item.taobao.com/item.htm?id str(item.get(itemId, )), }) return rows解析时用or做了字段降级例如realSales取不到就用sell。标题里的span classH是搜索高亮标签直接替换掉避免后面分词时混入 HTML 噪音。判断解析是否正确可以打印一页结果里的前两条记录确认 title 和 sales 都有内容。2.3 翻页与多关键词合并样本量不够时词云会骗人只抓一页“零食”只有几十条数据画词云时高频词会被一两个大店带偏。常见做法是准备三到五个关键词比如“零食 热销”“网红零食”“肉干 零食”各自抓三到五页合并后再去重样本量能到几百条。import time def collect_rows(keywords: list, max_pages: int 4, cookie_str: str ): rows [] for kw in keywords: for page in range(1, max_pages 1): data fetch_snack_page(kw, page, cookie_str) rows.extend(parse_items(data)) # 低频抓取每页之间至少间隔 2 秒避免触发平台风控 time.sleep(2 page * 0.5) return rows每次请求之间用 sleep 拉开节奏这是 requests 直连方案里最有效的自我保护手段。2 page * 0.5让页码越深间隔越长因为翻页越深被风控盯上的概率越高。collect_rows返回的列表直接交给 pandas下一步落盘。2.4 原始数据先落盘 CSV清洗放到第二步抓到的数据先原样存一份不要边抓边清洗。原因很简单清洗规则很可能返工比如你突然想分析“价格区间”而不是“最低价”那时候重新抓接口成本和风险都比重新读 CSV 高。df pd.DataFrame(collect_rows([零食 热销, 网红零食], max_pages4, cookie_str你的cookie)) df.to_csv(data/raw_snack.csv, indexFalse, encodingutf-8-sig) print(f共抓取 {df.shape[0]} 条去重后 {df[item_url].nunique()} 条)utf-8-sig编码能让 Excel 直接打开不乱码保存原始字段的 CSV 就是给后续数据清洗留一份可回溯的快照。打印行数与去重后的唯一数可以粗略判断翻页是否重复过多。3. Pandas 数据清洗把“月销7000”与“券后19.9”变成可计算字段3.1 先看看爬到的数据脏在哪读入 CSV 后第一步不看代码而是用df.info()和df.head(10)肉眼看原始数据。零食类目常见的脏数据有四种销量文本混着“月销”“已售”“万”价格字段带“券后”和区间标题残留 HTML 标签店铺名带着全角空格或首尾换行。import pandas as pd import re df pd.read_csv(data/raw_snack.csv, dtype{shop_name: string}) print(df.info()) print(df.head(10).to_string())dtype把店铺名强制声明为 string避免 pandas 把店铺名读成 float 后出现“三只松鼠.0”这类问题。先跑这两行确认数据形态再动手清洗。3.2 数据清洗规则表与 pandas 实现清洗不是把空行删掉那么简单每一列都要有明确的转换规则。先列规则再写代码规则表本身也是排查问题的依据。字段清洗前清洗后处理规则item_url重复出现多条唯一记录按 item_url 去重保留首次出现titlespan classH零食/span大礼包零食大礼包正则去掉所有 HTML 标签sales月销7000、已售10万7000、100000提取数字含“万”乘以 10000price券后19.9-39.919.9去掉“券后”取区间第一个数字shop_name三只松鼠官方旗舰店三只松鼠官方旗舰店strip 去首尾空白替换全角空格# 1. 去重同一个商品链接只保留一条避免多关键词重复采集干扰统计 df df.drop_duplicates(subset[item_url], keepfirst) # 2. 标题去 HTML 标签 df[title] df[title].str.replace(r[^], , regexTrue) # 3. 销量标准化 def parse_sales(s): if pd.isna(s): return pd.NA match re.search(r[\d.], str(s)) if not match: return pd.NA num float(match.group()) if 万 in str(s): num * 10000 return int(num) df[sales_num] df[sales].apply(parse_sales) # 4. 价格标准化取区间最小值作为阶段性的到手价口径 def parse_price(p): if pd.isna(p): return pd.NA nums re.findall(r[\d.], str(p).replace(券后, ).replace(¥, )) return float(nums[0]) if nums else pd.NA df[price_num] df[price].apply(parse_price) # 5. 店铺名清理 df[shop_name] df[shop_name].astype(string).str.strip() df[shop_name] df[shop_name].str.replace(\u3000, , regexFalse)parse_sales先提取字符串里的第一个数字再判断原文是否含“万”这比直接按“月销”切分更省事。parse_price用findall取区间里第一个数字“19.9-39.9”会得到 19.9代表商品的起价。店铺名里的\u3000是全角空格Excel 粘贴数据时最常见regexFalse表示只做字面替换不需要正则开销。3.2.1 公众号为什么不直接把 sales 转成 int 再乘直接astype(int)会在“月销7000”这种字符串上直接抛异常正则提取的兼容性更好。“已售10万”里的“10万”用正则拿到的数字是 10乘以 10000 得到 100000覆盖了两种常见销量文本。至于“付款9999”里没有“万”正则直接取 9999。3.2.2 价格区间的口径选择价格字段里的“19.9-39.9”通常是同一链接下不同规格最低价是引流价最高价是组合价。做价格带分布时用最低价更符合“用户点进链接看到的第一个价格”这一直觉但如果后续要做利润率模型就得改为抓取 SKU 明细不在本阶段处理。3.3 清洗后的质量核对有效值数量必须对得上清洗完不能只看代码不报错要确认每一列有效值数量合理。原始数据 400 条清洗后销量列如果只剩 200 条有效值说明有一半商品没有销量文本得回去看那些行长什么样。print(df[[sales_num, price_num]].describe()) print(清洗后总行数:, len(df)) print(销量缺失行数:, df[sales_num].isna().sum()) print(df[df[sales_num].isna()][[title, sales]].head(10)) print(df[shop_name].value_counts().head(10))describe()给出销量和价格的均值、分位数能快速发现异常值例如某个商品销量解析成了“0”很可能是正则没匹配到有效数字。value_counts()查看店铺 Top10确认没有“三只松鼠”和“三只松鼠 ”这种因空格拆分出的重复店铺。提示清洗的判据不是代码能跑而是每一列的有效值数量和你对业务的理解对得上。4. 销量、店铺与词云数据分析从清洗结果里找选品结论4.1 店铺集中度Top10 拿走样本里多少销量清洗完先做聚合。店铺维度用groupby累加销量看头部店铺的统治力这是“销量、店铺分析”里最直接的一层。shop_sales df.groupby(shop_name, as_indexFalse)[sales_num].sum() shop_sales shop_sales.sort_values(sales_num, ascendingFalse) total_sales shop_sales[sales_num].sum() top10_sales shop_sales.head(10)[sales_num].sum() print(fTop10 店铺销量合计占比{top10_sales / total_sales:.1%})如果占比超过 60%说明这个类目被头部店铺主导新品想靠自然流量突围很难如果只有 30% 左右则代表长尾店铺活跃新店还有机会。as_indexFalse是为了让结果保留 shop_name 列后续导出 CSV 更友好。4.2 价格带交叉销量最高的是哪个价位把清洗后的价格分成 0-10 元、10-20 元、20-30 元、30-50 元、50 元以上几档再按价格带汇总销量能看出零食走量靠低价还是中高价。bins [0, 10, 20, 30, 50, 10000] labels [0-10, 10-20, 20-30, 30-50, 50] df[price_band] pd.cut(df[price_num], binsbins, labelslabels, rightFalse) band_summary df.groupby(price_band, observedTrue)[sales_num].sum() print(band_summary.sort_values(ascendingFalse))pd.cut的rightFalse表示区间左闭右开10 元整会归入“10-20”而不是“0-10”避免边界数据被重复计算。observedTrue是 pandas 2.x 对 category 分组的收口参数不加会弹 FutureWarning谨慎起见直接带上。4.3 中文词云数据分析jieba 分词 stylecloud 出图词云的输入是清洗后的标题文本。标题比评论短噪音少分词速度也快。先拼接全部标题再用 jieba 切词过滤单字与停用词最后交给 stylecloud 生成词云图。import jieba from stylecloud import gen_stylecloud # 停用词表每行一个词手工维护 stopwords set(open(data/stopwords.txt, encodingutf-8).read().split()) text .join(df[title].dropna().tolist()) words [w for w in jieba.cut(text) if len(w.strip()) 1 and w not in stopwords and not w.isdigit()] gen_stylecloud( text .join(words), font_pathC:/Windows/Fonts/msyh.ttc, palettetableau.BlueRed_6, output_namedata/snack_wordcloud.png, )jieba.cut返回生成器列表推导式里先过滤空白、单字和纯数字再把拼接结果交给gen_stylecloud。生成图片的参数里font_path是最关键的一项。4.3.1 字体路径与停用词是中文词云的成败手Windows 用C:/Windows/Fonts/msyh.ttcmacOS 常见路径是/System/Library/Fonts/PingFang.ttcLinux 可以找/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc。不指定字体的词云图中文全部渲染成方块图很好看但没有任何信息量。停用词表要按类目单独维护。零食场景里“零食”“热销”“包邮”“正品”“旗舰店”“网红”这些词频率极高但没有区分度全部塞进停用词文件。stopwords | {零食, 热销, 包邮, 旗舰店, 正品, 小吃}把这类词滤掉词云里的特征词才能浮现出来——“辣条”“肉脯”“整箱”“独立包装”这类真正影响购买决策的词才有机会变大。4.4 词云读出结论而不是读出“大家都在卖零食”词云不是最终交付物结论才是。如果图里“辣条”“甜辣”字号突出说明样本中的辣味零食占主导选品时应该往辣味倾斜如果“整箱”“办公室”变大则代表囤货与分享场景是核心卖点。把词云结论和 4.2 的价格带结论交叉到一起低价位带出现“散装”“试吃”中高价位带出现“礼盒”“礼包”意味着不同价位对应完全不同的消费场景与包装策略。5. 让零食爬虫可复用增量落盘、限速与多组词云对比5.1 已抓链接跳过用集合做增量爬虫第二次跑时已经抓过的商品没必要重新解析。把 item_url 逐行写入一个文本文件下次采集前先读进内存集合新数据里不在集合中的才保留并把新链接追加回文件。import os seen_file data/seen_urls.txt seen set() if os.path.exists(seen_file): with open(seen_file, encodingutf-8) as f: seen {line.strip() for line in f if line.strip()} rows collect_rows([零食 热销], max_pages3, cookie_str你的cookie) new_rows [r for r in rows if r[item_url] not in seen] with open(seen_file, a, encodingutf-8) as f: for r in new_rows: f.write(r[item_url] \n)增量逻辑的关键是集合判断必须用完整链接不能只用 item_id因为不同商品可能有同 id 不同 trackId 的链接变体。这个技能点让脚本可以挂在定时任务里每周自动补抓新上的热销零食。5.2 限速优先于高并发别让 requests 跑成压力测试爬虫并发设计到底哪个好我的判断一向是小规模采集里限速优先于并发。零食品类一天新增 SKU 有限四次并发和单线程抓取在最终数据量上没有本质差别但把请求频率拉高后被风控处理的概率会显著上升。from concurrent.futures import ThreadPoolExecutor keyword_pages [(kw, p) for kw in [零食 热销, 网红零食] for p in range(1, 4)] with ThreadPoolExecutor(max_workers2) as executor: results list(executor.map( lambda kwp: fetch_snack_page(kwp[0], kwp[1], 你的cookie), keyword_pages ))max_workers2是小规模抓取的常用值线程数超过 4 后收益递减而风控概率却随频率上升。executor.map会按传入顺序返回结果不会乱序方便后续逐条解析。5.3 多组词云对比低价组与中高价组的词语差异把清洗好的数据按价格带分组分别生成词云并排看两张图比单张总词云更容易得出“低价靠量、中高价位靠场景”的结论。low_df df[df[price_band].isin([0-10, 10-20])] high_df df[df[price_band].isin([30-50, 50])] for name, subset in [(low, low_df), (high, high_df)]: text .join(subset[title].dropna().tolist()) words [w for w in jieba.cut(text) if len(w.strip()) 1 and w not in stopwords] gen_stylecloud( text .join(words), font_pathC:/Windows/Fonts/msyh.ttc, output_namefdata/wordcloud_{name}.png, )对比解读时留意低价组标题里高频出现的“散装”“试吃”“小包装”对应低客单价的决策关键词中高价组如果出现“礼盒”“零食大礼包”“送女友”说明搜索场景已经从自己吃转变成送礼。两个词云的差异就是下一步标题优化或选品方向最直接的证据来源。本文还有配套的精品资源点击获取