使用 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 完成数据清洗,才能挖掘出有价值的市场信息,同时需遵守平台规则与法律规定。
相关推荐
其美杰布-富贵-李5 小时前
爬虫中 XPath 使用完全指南
爬虫·xpath
喵手5 小时前
Python爬虫实战:城市停车收费标准自动化采集系统 - 让停车费透明化的技术实践(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·城市停车收费标准·采集城市停车收费数据·采集停车数据csv文件导出
我的xiaodoujiao6 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 46--撰写 README项目说明文档文件
python·学习·测试工具·pytest
喵手7 小时前
Python爬虫实战:采集菜谱网站的“分类/列表页”(例如“家常菜”或“烘焙”频道)数据,构建高可用的美食菜谱数据采集流水线(附CSV导出)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集菜谱网站数据·家常菜或烘焙频道·构建高可用食谱数据采集系统
喵手7 小时前
Python爬虫实战:硬核解析 Google Chrome 官方更新日志(正则+文本清洗篇)(附 CSV 导出)!
爬虫·python·爬虫实战·零基础python爬虫教学·csv导出·监控谷歌版本发布历史·获取稳定版更新日志
JFSJHFZJ7 小时前
清理手机顽固缓存,轻松释放几GB空间
缓存·智能手机
我的xiaodoujiao7 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 47--设置Selenium以无头模式运行代码
python·学习·selenium·测试工具·pytest
深蓝电商API20 小时前
处理字体反爬:woff字体文件解析实战
爬虫·python
七夜zippoe21 小时前
脉向AI|当豆包手机遭遇“全网封杀“:GUI Agent是通向AGI的必经之路吗?
人工智能·ai·智能手机·agent·gui
crmscs21 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑