使用 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 完成数据清洗,才能挖掘出有价值的市场信息,同时需遵守平台规则与法律规定。
相关推荐
ZHHHHHJ6617 小时前
BLE SM LTK-三星手机获取ble ltk
智能手机
de之梦-御风17 小时前
【电视投屏】针对“局域网投屏开源项目(Android 手机 ↔ Android TV)
android·智能手机·开源
He_Donglin17 小时前
Python图书爬虫
开发语言·爬虫·python
卓码软件测评18 小时前
第三方CMA.CNAS软件评测机构【深入理解Apifox的数据模型:定义和管理API数据结构】
测试工具·ci/cd·测试用例
优选资源分享18 小时前
Escrcpy 便携版 v2.0.0:安卓手机电脑同屏软件
android·智能手机·电脑
程序员三藏18 小时前
单元测试详解
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
APIshop1 天前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
AC赳赳老秦1 天前
Python 爬虫进阶:DeepSeek 优化反爬策略与动态数据解析逻辑
开发语言·hadoop·spring boot·爬虫·python·postgresql·deepseek