实战项目:开发一个电商商品信息抓取机器人(Day 34-35)
前言
经过前面33天的学习,我们已经掌握了Selenium的基础操作、元素定位、等待机制,以及进阶的JavaScript执行、Cookie管理和浏览器配置。今天,我们将迎来第三阶段的终极实战项目——开发一个电商商品信息抓取机器人。
本项目将综合运用之前所学的所有技能,实现一个完整的自动化流程:
- 模拟用户搜索商品
- 处理动态加载内容
- 自动翻页抓取多页数据
- 提取商品名称、价格、评论数
- 将数据保存为结构化CSV文件
让我们开始这段代码之旅吧!🚀
一、项目概述与技术选型
1.1 项目目标
编写一个Python脚本,实现以下功能:
- 打开电商网站(以京东为例,页面结构相对稳定)
- 自动搜索指定关键词(如“Python书籍”)
- 滚动页面加载所有商品
- 提取当前页所有商品的名称、价格、评论数
- 自动点击“下一页”按钮,重复步骤3-5
- 将所有数据保存到CSV文件中
1.2 为什么选择Selenium?
电商网站普遍采用动态加载技术,传统的requests+BeautifulSoup方式无法获取JavaScript渲染后的内容。Selenium通过操作真实浏览器,能够完美应对这类场景。
| 对比项 | Selenium | Requests+BS4 |
|---|---|---|
| 动态内容 | ✅ 完全支持 | ❌ 无法获取 |
| 交互操作 | ✅ 点击、滚动、输入 | ❌ 仅限静态请求 |
| 反爬绕过 | 模拟真人操作,效果较好 | 容易被识别封IP |
| 执行速度 | 较慢 | 非常快 |
对于本项目,速度和复杂度的平衡点在于:我们牺牲部分速度,换取稳定性和数据完整性。
1.3 技术栈与环境搭建
# 所需库
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import time
import pandas as pd
import csv
import os
安装命令:
pip install selenium pandas
同时确保下载与本地Chrome版本匹配的ChromeDriver,并将其路径加入系统PATH。
二、代码实现:分步详解
2.1 初始化浏览器配置
为了提升效率并减少资源消耗,我们将配置无头模式和禁用图片加载。
class EcommerceSpider:
def __init__(self, headless=True):
"""
初始化爬虫,配置浏览器选项
:param headless: 是否使用无头模式
"""
self.options = Options()
if headless:
self.options.add_argument('--headless=new') # 新版无头模式
self.options.add_argument('--disable-gpu')
# 禁用图片加载,提升速度
prefs = {"profile.managed_default_content_settings.images": 2}
self.options.add_experimental_option("prefs", prefs)
# 隐藏自动化提示
self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
self.options.add_argument('--disable-blink-features=AutomationControlled')
# 设置窗口大小(对响应式布局很重要)
self.options.add_argument('--window-size=1920,1080')
self.driver = webdriver.Chrome(options=self.options)
self.wait = WebDriverWait(self.driver, 10)
self.data = [] # 存储所有商品数据
代码解读:
--headless=new:新版Chrome无头模式,更稳定- 禁用图片加载:可提升30%以上的加载速度
- 隐藏自动化特征:降低被反爬虫识别的概率
2.2 实现商品搜索
打开京东首页,定位搜索框,输入关键词并提交。
def search_product(self, keyword):
"""
打开京东并搜索商品
:param keyword: 搜索关键词
"""
print(f"正在搜索关键词: {keyword}")
self.driver.get('https://www.jd.com/')
try:
# 等待搜索框出现
search_input = self.wait.until(
EC.presence_of_element_located((By.ID, 'key'))
)
search_input.clear()
search_input.send_keys(keyword)
# 点击搜索按钮
search_button = self.driver.find_element(By.CSS_SELECTOR, 'button.button')
search_button.click()
# 等待搜索结果加载
time.sleep(3)
print("搜索完成,页面标题:", self.driver.title)
except Exception as e:
print(f"搜索失败: {e}")
raise
定位技巧:
- 京东搜索框ID为
key,搜索按钮class包含button - 使用显式等待确保元素加载完成
2.3 滚动页面加载全部商品
京东的商品列表采用懒加载模式,只有滚动到页面底部才会加载更多商品。我们需要通过JavaScript模拟滚动操作。
def scroll_to_load_all(self):
"""
滚动页面至底部,触发懒加载
"""
print("正在滚动页面加载商品...")
last_height = self.driver.execute_script("return document.body.scrollHeight")
while True:
# 滚动到底部
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # 等待新内容加载
# 再次获取滚动高度,判断是否到底
new_height = self.driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break # 没有新内容加载,退出循环
last_height = new_height
# 最后再滚动一次,确保所有图片占位符都加载
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
print("页面滚动完成")
原理说明:
- 通过
document.body.scrollHeight获取页面总高度 - 循环滚动到底部,直到高度不再变化,表示所有内容加载完毕
2.4 提取当前页商品信息
京东商品列表页的结构如下:
- 每个商品被包含在
//*[@id="J_goodsList"]/ul/li中 - 商品名称:
.//div[@class="p-name"]/a/em - 商品价格:
.//div[@class="p-price"] - 评论数量:
.//div[@class="p-commit"]/strong
def extract_page_data(self):
"""
提取当前页所有商品信息
:return: 当前页数据列表
"""
# 定位所有商品项
li_list = self.driver.find_elements(By.XPATH, '//*[@id="J_goodsList"]/ul/li')
print(f"本页找到 {len(li_list)} 个商品")
page_data = []
for li in li_list:
try:
# 商品名称
name_elem = li.find_element(By.XPATH, './/div[@class="p-name"]/a/em')
name = name_elem.text.strip()
# 商品价格
price_elem = li.find_element(By.XPATH, './/div[@class="p-price"]')
price = price_elem.text.strip()
# 评论数量
commit_elem = li.find_element(By.XPATH, './/div[@class="p-commit"]/strong')
commit = commit_elem.text.strip()
# 组装数据
item = {
'name': name,
'price': price,
'commit': commit,
'crawl_time': time.strftime('%Y-%m-%d %H:%M:%S')
}
page_data.append(item)
except Exception as e:
# 单个商品提取失败,跳过继续
print(f"提取商品信息时出错: {e}")
continue
return page_data
注意事项:
- 使用相对XPath(以
.开头)在当前商品范围内查找,避免跨商品混淆 - 异常处理确保某个商品解析失败不影响整体流程
2.5 自动翻页控制
翻页的关键在于判断是否到达最后一页。京东的“下一页”按钮在最后一页时,class会变为pn-next disabled。
def has_next_page(self):
"""
判断是否存在下一页
:return: True/False
"""
page_source = self.driver.page_source
# 查找禁用状态的下一页按钮
if 'pn-next disabled' in page_source:
return False
try:
next_btn = self.driver.find_element(By.CLASS_NAME, 'pn-next')
return next_btn.is_enabled() and next_btn.is_displayed()
except:
return False
def go_to_next_page(self):
"""
点击下一页按钮
"""
try:
next_btn = self.driver.find_element(By.CLASS_NAME, 'pn-next')
# 滚动到按钮位置,确保可点击
self.driver.execute_script("arguments[0].scrollIntoView();", next_btn)
time.sleep(1)
next_btn.click()
time.sleep(3) # 等待新页面加载
return True
except Exception as e:
print(f"翻页失败: {e}")
return False
翻页逻辑:
- 检查页面源码是否包含
pn-next disabled,包含则说明是最后一页 - 否则找到下一页按钮,滚动到可视区域后点击
- 等待新页面加载
2.6 数据存储为CSV
使用pandas将数据导出为CSV文件,确保中文不乱码。
def save_to_csv(self, filename='products.csv'):
"""
将抓取的数据保存为CSV文件
:param filename: 文件名
"""
if not self.data:
print("没有数据可保存")
return
df = pd.DataFrame(self.data)
# 指定编码为utf-8-sig,防止Excel打开乱码
df.to_csv(filename, index=False, encoding='utf-8-sig')
print(f"数据已保存至 {filename},共 {len(self.data)} 条记录")
def run(self, keyword, max_pages=5):
"""
运行爬虫主流程
:param keyword: 搜索关键词
:param max_pages: 最大抓取页数
"""
try:
# 1. 搜索商品
self.search_product(keyword)
page_num = 1
while page_num <= max_pages:
print(f"\n--- 正在抓取第 {page_num} 页 ---")
# 2. 滚动加载当前页所有商品
self.scroll_to_load_all()
# 3. 提取当前页数据
page_data = self.extract_page_data()
self.data.extend(page_data)
print(f"第 {page_num} 页提取到 {len(page_data)} 条数据")
# 4. 判断是否继续翻页
if page_num < max_pages and self.has_next_page():
if not self.go_to_next_page():
print("翻页失败,停止抓取")
break
page_num += 1
else:
print("已到达最后一页或达到最大页数限制")
break
# 5. 保存数据
self.save_to_csv(f'{keyword}_products.csv')
finally:
self.driver.quit()
print("浏览器已关闭")
三、完整代码与运行效果
3.1 完整脚本
将以上所有类方法整合,形成完整脚本:
# jd_spider.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import time
import pandas as pd
class JdSpider:
# ... 将上述所有方法粘贴在这里 ...
if __name__ == '__main__':
spider = JdSpider(headless=False) # 调试时可关闭无头模式
spider.run('Python书籍', max_pages=3)
3.2 运行结果示例
正在搜索关键词: Python书籍
搜索完成,页面标题: Python书籍 - 商品搜索 - 京东
--- 正在抓取第 1 页 ---
正在滚动页面加载商品...
页面滚动完成
本页找到 60 个商品
第 1 页提取到 60 条数据
--- 正在抓取第 2 页 ---
正在滚动页面加载商品...
页面滚动完成
本页找到 60 个商品
第 2 页提取到 60 条数据
--- 正在抓取第 3 页 ---
正在滚动页面加载商品...
页面滚动完成
本页找到 60 个商品
第 3 页提取到 60 条数据
已到达最后一页或达到最大页数限制
数据已保存至 Python书籍_products.csv,共 180 条记录
浏览器已关闭
生成的CSV文件内容示例:
| name | price | commit | crawl_time |
|---|---|---|---|
| Python编程 从入门到实践 第2版 | ¥52.50 | 200+条评价 | 2025-02-28 15:30:22 |
| Python编程三剑客套装 | ¥206.90 | 5万+条评价 | 2025-02-28 15:30:23 |
| 零基础学Python(全彩版) | ¥37.50 | 10万+条评价 | 2025-02-28 15:30:24 |
四、常见问题与优化建议
4.1 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 找不到元素 | 页面未完全加载/选择器过期 | 增加等待时间,更新XPath |
| 翻页点击无效 | 按钮被遮挡/未滚动到可视区域 | 先用scrollIntoView()滚动再点击 |
| 数据重复 | 翻页逻辑错误 | 检查翻页条件,添加去重机制 |
| 速度太慢 | 无头模式未启用/图片未禁用 | 开启无头模式,禁用图片 |
4.2 反爬虫应对策略
电商网站的反爬虫机制日益强大,建议采取以下措施:
- 随机延迟:在操作之间加入
time.sleep(random.uniform(1,3)) - 更换User-Agent:使用
fake_useragent库随机生成 - 使用代理IP:配置代理服务器
- 模拟鼠标轨迹:使用
ActionChains模拟更真实的操作
# 随机延迟示例
import random
time.sleep(random.uniform(1.5, 3.5))
4.3 扩展功能建议
- 多线程抓取:使用concurrent.futures同时抓取多个关键词
- 数据库存储:将数据存入MySQL或MongoDB
- 异常恢复:断点续传,记录已抓取页数
- 价格监控:定时运行脚本,监控价格变化
五、项目总结
通过这个实战项目,我们完整地实践了以下核心技能:
- 浏览器配置:无头模式、禁用图片、隐藏自动化特征
- 元素定位:XPath与CSS选择器的综合运用
- 等待策略:显式等待与强制等待的配合
- JavaScript执行:滚动加载的实现
- 翻页控制:基于页面特征的边界判断
- 数据持久化:CSV文件的生成与编码处理
这个机器人虽然以京东为例,但其核心逻辑可以轻松移植到淘宝、拼多多、亚马逊等其他电商平台——只需要调整元素定位表达式即可。
思考与挑战:
- 如何应对网站改版导致的选择器失效?
- 如何在抓取效率和反爬之间找到平衡?
- 如果网站使用动态分页(点击加载更多而非传统翻页),代码该如何调整?
欢迎在评论区分享你的实践经验和遇到的问题!
注:本文代码基于Selenium 4.x编写,请在遵守网站robots.txt和相关法律法规的前提下使用爬虫技术。
更多推荐




所有评论(0)