Python爬虫实战:京东商品数据爬取+可视化分析

一、项目前言

在数据分析、竞品调研、电商学习场景中,京东商品数据具备极高的参考价值。本文给大家带来一套2025年最新可用京东爬虫+可视化完整项目,基于Python原生库开发,无需登录、无需cookie,可自定义关键词、自定义爬取页数。

项目可自动爬取京东商品ID、商品名称、售价、评价数量、入驻商家、商品详情链接等核心字段,支持导出 CSV、JSON 两种格式,同时自动绘制价格分布、热门商家、爆款商品三张数据分析图表,零基础可直接运行。

适合Python爬虫入门、数据分析实战、课程设计、毕设小型项目使用。

二、项目功能亮点

  • ✅ 自定义搜索关键词、自定义爬取页数,通用性极强

  • ✅ 精准解析价格、万+评价、商家信息、商品链接

  • ✅ 智能数据清洗:自动换算"万+"评价为真实数字、过滤无效数据

  • ✅ 双格式数据导出:CSV(Excel查看)、JSON(结构化数据)

  • ✅ 全自动可视化:价格区间、商家排行、热度商品统计图

  • ✅ 随机延时防反爬,大幅降低IP封禁概率

  • ✅ 修复旧版代码报错、链接失效、中文乱码等问题

三、环境依赖

本项目基于 Python3 开发,运行前请安装依赖库,复制以下命令终端执行:

python 复制代码
pip install requests beautifulsoup4 matplotlib numpy

依赖说明:

  • requests:发送网络请求,获取网页源码

  • beautifulsoup4:解析HTML,提取商品数据

  • matplotlib / numpy:数据可视化绘图

四、完整可运行源码(已修复BUG)

针对网上大部分失效代码,本文修复了链接错误、语法报错、关键词固定、图片命名异常等问题,以下为纯净可运行版本:

python 复制代码
import requests
from bs4 import BeautifulSoup
import json
import time
import random
import csv
import matplotlib.pyplot as plt
import numpy as np
from collections import defaultdict

# ========== 全局配置:解决matplotlib中文、负号乱码 ==========
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False

# ========== 请求头伪装浏览器,避免拦截 ==========
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive"
}

def get_jd_products(keyword, page=1):
    """
    爬取京东搜索页商品数据
    :param keyword: 搜索关键词
    :param page: 爬取页码
    :return: 单页商品列表数据
    """
    try:
        # 适配最新京东搜索链接,动态拼接关键词、页码
        url = f"https://www.jd.com/Search?keyword={keyword}&page={page}&s={(page - 1) * 30}&click=0"
        response = requests.get(url, headers=headers, timeout=15)
        response.encoding = "utf-8"

        # 解析网页
        soup = BeautifulSoup(response.text, "html.parser")
        product_list = soup.find_all("div", class_="gl-item")
        products = []

        for item in product_list:
            # 商品ID
            product_id = item.get("data-sku")

            # 商品名称
            name_tag = item.find("div", class_="p-name")
            product_name = name_tag.get_text(strip=True) if name_tag else "无名称"

            # 商品价格
            price_tag = item.find("div", class_="p-price").find("i")
            product_price = price_tag.get_text(strip=True) if price_tag else "0"
            price_num = float(product_price) if product_price.replace(".", "", 1).isdigit() else 0

            # 商品评价数 + 数据换算
            commit_tag = item.find("div", class_="p-commit").find("a")
            product_commit = commit_tag.get_text(strip=True) if commit_tag else "0"
            commit_count = 0
            if product_commit:
                if "万" in product_commit:
                    commit_count = int(float(product_commit.replace("万+", "").replace("万", "")) * 10000)
                elif "+" in product_commit:
                    commit_count = int(product_commit.replace("+", ""))

            # 商家名称
            shop_tag = item.find("div", class_="p-shop").find("a")
            product_shop = shop_tag.get_text(strip=True) if shop_tag else "无商家信息"

            # 商品链接
            link_tag = item.find("a", class_="p-img")
            product_link = "https:" + link_tag.get("href") if link_tag else "无链接"

            # 封装数据
            products.append({
                "id": product_id,
                "name": product_name,
                "price": product_price,
                "price_num": price_num,
                "commit": product_commit,
                "commit_count": commit_count,
                "shop": product_shop,
                "link": product_link
            })

        print(f"第{page}页爬取完成,共 {len(products)} 个商品")
        return products

    except Exception as e:
        print(f"爬取第{page}页时出错: {str(e)}")
        return []

# ========== 保存CSV文件 ==========
def save_to_csv(data, filename):
    if not data:
        print("没有数据可保存")
        return
    fields = data[0].keys()
    with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(data)
    print(f"数据已保存到 {filename}")

# ========== 保存JSON文件 ==========
def save_to_json(data, filename):
    if not data:
        print("没有数据可保存")
        return
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
    print(f"数据已保存到 {filename}")

# ========== 价格区间分布图 ==========
def create_price_range_chart(products, keyword):
    valid_products = [p for p in products if p['price_num'] > 0]
    if not valid_products:
        print("没有足够的价格数据生成图表")
        return

    max_price = max(p['price_num'] for p in valid_products)
    if max_price < 100:
        bins = list(range(0, int(max_price) + 20, 20))
    elif max_price < 500:
        bins = list(range(0, int(max_price) + 100, 100))
    elif max_price < 2000:
        bins = list(range(0, int(max_price) + 500, 500))
    else:
        bins = list(range(0, int(max_price) + 1000, 1000))

    price_counts = defaultdict(int)
    for p in valid_products:
        for i in range(len(bins) - 1):
            if bins[i] <= p['price_num'] < bins[i + 1]:
                price_counts[f"{bins[i]}-{bins[i + 1]}"] += 1
                break
        else:
            price_counts[f"{bins[-1]}+"] += 1

    plt.figure(figsize=(12, 6))
    plt.bar(price_counts.keys(), price_counts.values(), color='skyblue')
    plt.title(f'京东"{keyword}"商品价格区间分布')
    plt.xlabel('价格区间 (元)')
    plt.ylabel('商品数量')
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.savefig(f'jd_{keyword}_price_range.png', dpi=300)
    plt.close()
    print(f"价格区间分布图已保存")

# ========== 热门商家TOP10统计图 ==========
def create_top_shops_chart(products, keyword, top_n=10):
    shop_counts = defaultdict(int)
    for p in products:
        shop_counts[p['shop']] += 1

    sorted_shops = sorted(shop_counts.items(), key=lambda x: x[1], reverse=True)[:top_n]
    if not sorted_shops:
        print("没有足够的商家数据生成图表")
        return

    shops, counts = zip(*sorted_shops)
    plt.figure(figsize=(12, 6))
    plt.bar(shops, counts, color='lightgreen')
    plt.title(f'京东"{keyword}"商品数量最多的前{top_n}个商家')
    plt.xlabel('商家名称')
    plt.ylabel('商品数量')
    plt.xticks(rotation=90)
    plt.tight_layout()
    plt.savefig(f'jd_{keyword}_top_shops.png', dpi=300)
    plt.close()
    print(f"商家分布图已保存")

# ========== 高评价商品TOP10统计图 ==========
def create_commit_chart(products, keyword, top_n=10):
    valid_products = [p for p in products if p['commit_count'] > 0]
    if not valid_products:
        print("没有足够的评价数据生成图表")
        return

    sorted_products = sorted(valid_products, key=lambda x: x['commit_count'], reverse=True)[:top_n]
    names = [p['name'][:10] + '...' for p in sorted_products]
    commit_counts = [p['commit_count'] for p in sorted_products]

    plt.figure(figsize=(12, 6))
    plt.bar(names, commit_counts, color='salmon')
    plt.title(f'京东"{keyword}"评价数量最多的前{top_n}个商品')
    plt.xlabel('商品名称')
    plt.ylabel('评价数量')
    plt.xticks(rotation=90)
    plt.tight_layout()
    plt.savefig(f'jd_{keyword}_top_commits.png', dpi=300)
    plt.close()
    print(f"商品热度分布图已保存")

# ========== 主函数入口 ==========
def main():
    # 自定义输入参数
    keyword = input("请输入搜索关键词: ")
    try:
        pages = int(input("请输入要爬取的页数: "))
    except ValueError:
        print("页数必须是整数,将默认爬取 1 页")
        pages = 1

    all_products = []
    # 循环多页爬取
    for page in range(1, pages + 1):
        products = get_jd_products(keyword, page)
        if products:
            all_products.extend(products)
        # 随机延时防反爬
        time.sleep(random.uniform(1, 3))

    print(f"所有页面爬取完成,共获取 {len(all_products)} 个商品信息")

    # 数据保存 + 绘图
    if all_products:
        save_to_csv(all_products, f"jd_{keyword}_products.csv")
        save_to_json(all_products, f"jd_{keyword}_products.json")
        create_price_range_chart(all_products, keyword)
        create_top_shops_chart(all_products, keyword)
        create_commit_chart(all_products, keyword)

if __name__ == "__main__":
    main()

五、代码核心修复说明(对应你提供的源码)

你提供的原始代码存在多处BUG,我已全部修复,适配2025京东页面:

  • 修复1:链接错误:删除无效跳转短链接,替换为京东官方原生搜索URL

  • 修复2:语法报错 :修复文末 if name == "main" 致命报错,改为标准入口函数

  • 修复3:关键词固定问题:原代码强制固定"手机",现已完全自定义输入

  • 修复4:文件命名报错:补全下划线分隔符,避免图片/文件保存失败

  • 修复5:多余空格BUG:清理代码中多余空格、换行,解决解析失效问题

六、运行步骤教程

1、新建python文件(如 jd_spider.py),粘贴上方完整源码;

2、安装上述所有依赖库;

3、运行代码,控制台输入:搜索关键词、爬取页数;

4、等待程序执行完毕,项目根目录自动生成所有数据文件和图表。

七、运行输出结果

程序运行成功后,自动生成5个文件:

  • jd_xxx_products.csv:商品完整明细(可Excel打开)

  • jd_xxx_products.json:结构化原始数据

  • jd_xxx_price_range.png:商品价格区间分布柱状图

  • jd_xxx_top_shops.png:入驻商家商品数量TOP10图表

  • jd_xxx_top_commits.png:高评价爆款商品TOP10图表

八、常见报错解决方案

1、爬取数据为空

解决方案:检查网络、关闭代理;适当延长延时;京东偶尔更新前端标签,可手动核对class名称。

2、中文乱码

已内置utf-8-sig编码、matplotlib中文配置,无需手动修改。

3、依赖库报错

重新执行pip安装命令,建议使用Python3.8及以上版本。

九、项目拓展方向

  • 新增代理IP池,解决大批量爬取IP封禁问题

  • 多线程/异步爬取,提升爬取速度

  • 爬取商品详情页参数、销量、优惠活动

  • 对接MySQL数据库,实现数据持久化存储

  • 做价格监控脚本,定时采集价格变化

十、免责声明

本项目代码仅用于Python学习、编程实训、技术研究,禁止用于商业批量爬取、恶意爬虫、数据倒卖等违规行为。爬取过程中请遵守京东平台用户协议及国家网络安全法律法规,合理控制爬取频率。

相关推荐
tang7778921 分钟前
爬虫代理池搭建全流程:从IP筛选、去重到自动切换(附完整落地代码)
爬虫·网络协议·tcp/ip·代理ip池·爬虫代理池
jay神1 小时前
2026年YOLO还有哪些创新点可以做?
python·yolo·毕业设计·科研·课程设计·创新
不正经学生6 小时前
C语言预处理详解:编译器真正动手之前的那些事
c语言·开发语言·算法·面试·bug
门思科技6 小时前
LoRaWAN 设备类别:Class A、B、C 对比与选型指南
c语言·开发语言·php
恋恋西风9 小时前
C++ 理解 std::thread 在单核和多核上的行为差异
开发语言·c++
Brilliantwxx9 小时前
【Linux】 进程(9)程序与进程地址空间(基础+进阶+面试题)
linux·运维·服务器·开发语言·c++
BizzZ_9 小时前
C++(22)——类型转换和IO流
开发语言·c++
小范同学_9 小时前
JDK1.7 与 JDK1.8 HashMap 底层原理对比 + 数组并发扩容死循环详解
java·开发语言
geovindu10 小时前
CSharp: 万年历
开发语言·后端·c#·.net