使用 Selenium 爬取京东手机销量与评分数据 (1)

在电商数据分析场景中,京东作为头部电商平台,其手机品类的销量、评分数据是洞察市场趋势、分析用户偏好的核心依据。相较于静态网页爬取,京东采用动态渲染技术加载商品数据,传统的 Requests+BeautifulSoup 组合难以获取完整信息,而 Selenium 凭借模拟浏览器行为的特性,能完美解决动态数据爬取问题。本文将详细讲解如何基于 Selenium 实现京东手机销量与评分数据的爬取,并完成数据清洗与初步分析。

一、技术选型与环境准备

1. 核心技术栈

  • Selenium:模拟 Chrome 浏览器操作,加载动态页面并定位元素;
  • ChromeDriver:Chrome 浏览器的驱动程序,实现 Selenium 与浏览器的通信;
  • Pandas:数据清洗、存储与初步分析;
  • BeautifulSoup:辅助解析网页 HTML 结构,提取目标数据。

2. 环境配置

(1)ChromeDriver 配置
  • 查看本地 Chrome 浏览器版本(设置→关于 Chrome);
  • 前往ChromeDriver 官方下载页下载对应版本的驱动;
  • 将 ChromeDriver.exe 放入 Python 安装目录(或配置系统环境变量),确保命令行可直接调用。

二、爬取逻辑设计

1. 爬取目标

以京东 "手机" 关键词搜索结果为数据源,提取以下信息:

  • 商品名称;
  • 商品价格;
  • 商品销量(付款人数);
  • 商品评分;
  • 评论数。

三、完整实现代码

python

运行

plain 复制代码
import time
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup

# 代理配置信息(单独定义,便于维护)
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

class JDPhoneSpider:
    def __init__(self):
        # 配置Chrome选项,避免弹窗与自动化提示
        chrome_options = Options()
        # 无头模式(可选,注释后可见浏览器操作)
        # chrome_options.add_argument('--headless')
        chrome_options.add_argument('--disable-blink-features=AutomationControlled')
        chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
        chrome_options.add_experimental_option('useAutomationExtension', False)
        
        # ========== 核心修改:添加代理配置 ==========
        # 1. 配置HTTP代理(包含主机和端口)
        chrome_options.add_argument(f'--proxy-server=http://{proxyHost}:{proxyPort}')
        # 2. 处理代理账号密码认证(需通过Chrome扩展或DesiredCapabilities,这里用更通用的方式)
        # 注:Chrome 90+版本需通过selenium的ChromeDevTools协议注入认证信息
        self.proxy_auth = (proxyUser, proxyPass)
        
        # 初始化浏览器
        self.driver = webdriver.Chrome(options=chrome_options)
        # 注入代理认证(关键:避免代理需要账号密码时访问失败)
        self._set_proxy_auth()
        
        self.driver.execute_script('Object.defineProperty(navigator, "webdriver", {get: () => undefined})')
        self.driver.implicitly_wait(10)  # 隐式等待10秒
        self.wait = WebDriverWait(self.driver, 15)  # 显式等待对象
        self.data_list = []  # 存储爬取的商品数据

    def _set_proxy_auth(self):
        """注入代理账号密码认证,解决带密码的代理访问问题"""
        try:
            # 通过Chrome DevTools协议设置代理认证
            self.driver.execute_cdp_cmd(
                'Network.enable', {}
            )
            self.driver.execute_cdp_cmd(
                'Network.setExtraHTTPHeaders',
                {'headers': {'Proxy-Authorization': f'Basic {self._get_base64_auth()}'}}
            )
        except Exception as e:
            print(f"代理认证配置警告:{e},部分代理可能无需此步骤")

    def _get_base64_auth(self):
        """将代理账号密码转为Base64编码(HTTP Basic认证要求)"""
        import base64
        auth_str = f"{self.proxy_auth[0]}:{self.proxy_auth[1]}"
        return base64.b64encode(auth_str.encode('utf-8')).decode('utf-8')

    def get_page_data(self, page_url):
        """爬取单页商品数据"""
        self.driver.get(page_url)
        # 模拟滚动加载(京东动态加载商品,需滚动到底部)
        for _ in range(3):
            self.driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
            time.sleep(2)
        
        # 等待商品列表加载完成
        self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'gl-item')))
        
        # 解析页面源码
        soup = BeautifulSoup(self.driver.page_source, 'lxml')
        items = soup.find_all('li', class_='gl-item')
        
        for item in items:
            # 提取商品名称
            name_tag = item.find('div', class_='p-name').find('em')
            name = name_tag.get_text().strip() if name_tag else '未知'
            
            # 提取商品价格
            price_tag = item.find('div', class_='p-price').find('i')
            price = price_tag.get_text().strip() if price_tag else '0'
            
            # 提取销量(付款人数)
            sales_tag = item.find('div', class_='p-commit').find('a', class_='J_comment')
            sales = sales_tag.get_text().strip().replace('万+', '0000').replace('+', '') if sales_tag else '0'
            
            # 提取评分(部分商品无评分,需做容错)
            score_tag = item.find('div', class_='p-commit').find('span', class_='score')
            score = score_tag.get_text().strip() if score_tag else '无评分'
            
            # 提取评论数
            comment_tag = item.find('div', class_='p-commit').find('strong').find('a')
            comment_num = comment_tag.get_text().strip() if comment_tag else '0'
            
            # 整理数据
            self.data_list.append({
                '商品名称': name,
                '价格(元)': price,
                '销量': sales,
                '评分': score,
                '评论数': comment_num
            })
        print(f'当前页爬取完成,累计获取{len(self.data_list)}条数据')

    def crawl(self, keyword='手机', page_num=3):
        """核心爬取函数"""
        base_url = f'https://search.jd.com/Search?keyword={keyword}&enc=utf8&page='
        for page in range(1, page_num * 2, 2):
            # 京东分页URL规则:第1页page=1,第2页page=3,第3页page=5...
            page_url = base_url + str(page)
            print(f'正在爬取第{(page+1)//2}页,URL:{page_url}')
            try:
                self.get_page_data(page_url)
                # 翻页间隔,避免请求过快被风控
                time.sleep(3)
            except Exception as e:
                print(f'第{(page+1)//2}页爬取失败:{str(e)}')
                continue

    def save_data(self, filename='jd_phone_data.csv'):
        """将数据保存为CSV文件"""
        df = pd.DataFrame(self.data_list)
        # 数据清洗:价格转为数值型,销量做简单处理
        df['价格(元)'] = pd.to_numeric(df['价格(元)'], errors='coerce').fillna(0)
        df['评论数'] = df['评论数'].str.replace('万', '0000').replace('+', '')
        df['评论数'] = pd.to_numeric(df['评论数'], errors='coerce').fillna(0)
        
        df.to_csv(filename, index=False, encoding='utf-8-sig')
        print(f'数据已保存至{filename},共{len(df)}条有效数据')

    def close(self):
        """关闭浏览器"""
        self.driver.quit()

# 主程序执行
if __name__ == '__main__':
    spider = JDPhoneSpider()
    try:
        # 爬取3页手机数据
        spider.crawl(keyword='手机', page_num=3)
        # 保存数据
        spider.save_data()
    finally:
        # 确保浏览器关闭
        spider.close()

四、代码核心解析

1. 浏览器配置

  • 关闭<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">webdriver</font>特征检测:通过<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">execute_script</font>修改<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">navigator.webdriver</font>属性,避免京东识别出自动化程序;
  • 隐式等待 + 显式等待结合:隐式等待处理全局元素加载,显式等待确保商品列表加载完成后再解析,避免数据缺失。

2. 动态数据加载处理

京东商品列表采用滚动加载机制,通过<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">window.scrollTo</font>模拟鼠标滚动,配合<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">time.sleep</font>等待数据加载,确保能获取完整的商品信息。

3. 数据提取与容错

  • 针对 "无评分""销量为空" 等异常情况,通过<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">if-else</font>做容错处理,避免程序崩溃;
  • 使用 BeautifulSoup 解析页面,通过 class 定位元素,适配京东网页结构。

4. 数据清洗与存储

  • 将价格、评论数转为数值型,方便后续分析;
  • 保存为 UTF-8 编码的 CSV 文件,避免中文乱码问题。

五、爬取注意事项

  1. 反爬机制规避
    • 控制请求频率(设置<font style="color:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);">time.sleep</font>),避免短时间内大量请求;
    • 不使用高并发爬取,单线程爬取少量数据(如 10 页内)更安全;
    • 可添加随机 User-Agent,进一步降低被风控的概率。
  2. 网页结构变更:京东网页 class 名称可能随版本更新变化,若爬取失败,需通过浏览器开发者工具(F12)重新定位元素的 class 或 XPath。
  3. 合法合规性:本代码仅用于学习研究,爬取数据不得用于商业用途,需遵守京东平台的 robots 协议及相关法律法规。

六、数据应用示例

爬取完成后,可通过 Pandas 做简单分析:

python

运行

plain 复制代码
import pandas as pd

# 读取数据
df = pd.read_csv('jd_phone_data.csv')
# 1. 筛选评分≥4.8的商品
high_score_phones = df[df['评分'] != '无评分'][df[df['评分'] != '无评分']['评分'].astype(float) ≥ 4.8]
# 2. 统计价格区间分布
price_range = pd.cut(df['价格(元)'], bins=[0, 1000, 2000, 3000, 5000, 10000], labels=['千元内', '1-2千', '2-3千', '3-5千', '5千以上'])
price_dist = price_range.value_counts()
print(price_dist)

总结

  1. Selenium 是解决动态网页爬取的核心工具,通过模拟浏览器行为可获取京东动态加载的商品数据;
  2. 爬取过程中需重点处理动态加载、反爬机制、数据容错三大问题,确保数据完整性与程序稳定性;
  3. 爬取后的数据分析需基于 Pandas 完成数据清洗,才能挖掘出有价值的市场信息,同时需遵守平台规则与法律规定。
相关推荐
2501_948120153 小时前
大语言模型与爬虫技术融合的智能数据采集系统
人工智能·爬虫·语言模型
TheNextByte14 小时前
如何打印Android手机联系人?
android·智能手机
喵手5 小时前
Python爬虫实战:采集巨潮资讯网等上市公司公告数据,通过智能关键词匹配技术识别分红、回购、停牌等重要信息(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集巨潮资讯数据·智能匹配识别分红、回购等信息·csv导出+sqlite
泡泡以安5 小时前
Android 逆向实战:从零突破某电商 App 登录接口全参数加密
android·爬虫·安卓逆向
axinawang5 小时前
第9章 存储爬虫数据
爬虫
解压专家6665 小时前
手机内存告急?Kred轻量阅读器:省空间+全功能
智能手机
数学建模导师6 小时前
2026美赛A题智能手机电池续航时间预测的连续时间数学模型
智能手机
Data_Journal7 小时前
Scrapy vs. Crawlee —— 哪个更好?!
运维·人工智能·爬虫·媒体·社媒营销
深蓝电商API8 小时前
async/await与多进程结合的混合爬虫架构
爬虫·架构
Fleshy数模8 小时前
我的第一只Python爬虫:从Requests库到爬取整站新书
开发语言·爬虫·python