☰
Python数据采集实战:用Playwright与AI高效抓取京东商品信息
2026/10/3 21:39:18 网站建设 项目流程

在实际的Python接单项目中,数据采集是一个高频需求,尤其是针对电商平台如京东的商品信息抓取。这类单子往往要求稳定、高效且能应对平台的反爬机制,对于新手开发者来说,直接上手可能会感到无从下手。本文将以一个模拟的“京东商品数据采集”项目为例,展示如何利用现代开发工具和AI辅助,系统性地完成从环境搭建、代码编写到数据获取与处理的完整流程。即使你是Python新手,只要按照步骤操作,也能理解其核心逻辑并复现结果。本文将重点讲解使用Playwright进行网页自动化,并结合AI工具(如SeekSeek,此处作为AI辅助编程的示例代称)来提升开发效率,避开常见陷阱。

1. 理解数据采集项目与工具选型

在开始写代码之前,必须清楚我们要做什么,以及为什么选择特定的工具。盲目开始往往会导致项目中途卡壳。

1.1 项目目标与核心挑战

我们的目标是采集京东指定商品列表的详细信息,例如商品标题、价格、促销信息、评价数量、店铺名称等。这听起来简单,但面临几个核心挑战:

  1. 动态内容加载:现代电商网站大量使用JavaScript异步加载数据,简单的HTTP请求(如requests库)只能获取初始HTML,拿不到动态渲染后的商品信息。
  2. 反爬虫机制:平台会检测异常访问行为,如高频请求、无头浏览器特征等,可能导致IP被封或要求验证码。
  3. 页面结构变化:网站的HTML结构和CSS选择器可能随时调整,代码需要一定的容错性和可维护性。
  4. 数据清洗与存储:采集到的原始数据往往包含HTML标签、多余空格或非常规格式,需要清洗并结构化地存储(如CSV、数据库)。

1.2 为什么选择 Playwright 而非传统方法

对比常见的采集方案,可以清晰看到选型依据:

工具/方案原理优点缺点适用场景
Requests + BeautifulSoup发送HTTP请求,解析静态HTML。速度快,资源消耗低。无法处理JavaScript渲染的内容。纯静态网站,或API接口已知的网站。
Selenium自动化真实浏览器。能处理动态内容,兼容性好。速度较慢,资源占用高,需要对应浏览器驱动。需要模拟复杂用户交互(如登录、滑块)的场景。
Playwright提供高级API控制Chromium、Firefox、WebKit等浏览器。速度比Selenium快,API更现代,自动等待机制好,可录制脚本。相对较新,社区资源略少于Selenium。现代动态网站采集、自动化测试、需要可靠等待机制的场景。

对于京东这类重度使用JS的网站,Playwright是更合适的选择。它能像真实用户一样打开浏览器,等待页面元素加载完成,再执行数据提取,极大提高了成功率。

1.3 AI辅助编程(SeekSeek)在项目中的作用

“SeekSeek”在此处泛指一类AI编程助手。在数据采集项目中,AI可以辅助完成以下工作,提升开发效率:

  • 生成基础代码框架:描述需求(如“用Playwright打开京东并搜索商品”),AI可以生成对应的Python代码片段。
  • 解释复杂选择器:当你不确定如何定位某个页面元素时,可以将HTML片段提供给AI,让它帮你生成准确的XPath或CSS选择器。
  • 调试错误:将运行报错信息抛给AI,它可以提供可能的排查方向和修复建议。
  • 优化代码结构:对现有代码提出重构建议,使其更健壮、更易读。

注意:AI生成的代码是参考,必须理解其逻辑并根据实际项目上下文进行调整和测试,不能盲目直接使用。

2. 环境准备与项目初始化

一个清晰、隔离的开发环境是项目成功的第一步。

2.1 Python环境安装与配置

确保你的系统已安装Python 3.7或更高版本。推荐使用Python 3.8+。

  1. 检查Python版本:
    python --version # 或 python3 --version
  2. 安装与配置(以Windows为例):
    • 从 Python官网 下载安装包。
    • 安装时务必勾选“Add Python to PATH”。
    • 安装完成后,再次在命令行验证版本。

2.2 创建虚拟环境与安装依赖

虚拟环境能隔离项目依赖,避免包版本冲突。

  1. 创建项目目录并进入:
    mkdir jd_data_collector cd jd_data_collector
  2. 创建虚拟环境:
    python -m venv venv
  3. 激活虚拟环境:
    • Windows (CMD/PowerShell):
      venv\Scripts\activate
    • macOS/Linux:
      source venv/bin/activate
    激活后,命令行提示符前通常会显示(venv)。
  4. 安装核心依赖: 创建requirements.txt文件,内容如下:
    playwright==1.40.0 pandas==2.1.4
    然后执行安装:
    pip install -r requirements.txt
  5. 安装Playwright浏览器: Playwright需要下载它自带的浏览器二进制文件。
    playwright install chromium
    这一步会下载Chromium浏览器,用于后续的自动化操作。

2.3 项目结构规划

一个清晰的结构有助于代码管理。

jd_data_collector/ ├── venv/ # 虚拟环境目录(.gitignore忽略) ├── config.py # 配置文件(如URL、请求头、超时时间) ├── crawler.py # 核心爬虫逻辑 ├── data_processor.py # 数据清洗与处理逻辑 ├── main.py # 主程序入口 ├── requirements.txt # 项目依赖 └── output/ # 输出目录(存放采集的数据) └── jd_products_20231201.csv

3. 核心爬虫逻辑实现

我们将爬虫功能模块化,提高代码的可读性和可维护性。

3.1 配置文件 (config.py)

将易变的参数集中管理。

# config.py class Config: # 京东搜索页基础URL (以“手机”为例) BASE_SEARCH_URL = "https://search.jd.com/Search?keyword={keyword}&page={page}" # 请求头,模拟真实浏览器 HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept-Language': 'zh-CN,zh;q=0.9', } # 超时设置(毫秒) TIMEOUT = 30000 # 是否以无头模式运行(不显示浏览器界面) HEADLESS = True # 每页采集后等待时间(秒),避免请求过快 WAIT_TIME = 2

3.2 爬虫主类 (crawler.py)

这是最核心的部分,负责控制浏览器、导航页面和提取数据。

# crawler.py import asyncio from playwright.async_api import async_playwright import pandas as pd from config import Config import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class JDCrawler: def __init__(self, headless=Config.HEADLESS): self.headless = headless self.data_list = [] # 存储采集到的数据 async def fetch_page(self, url): """使用Playwright打开页面并返回页面对象""" async with async_playwright() as p: # 启动浏览器,使用Chromium browser = await p.chromium.launch(headless=self.headless, slow_mo=500) # slow_mo可减慢操作,便于观察 context = await browser.new_context(user_agent=Config.HEADERS['User-Agent']) page = await context.new_page() try: logger.info(f"正在访问: {url}") # 导航到目标URL,并等待网络空闲 await page.goto(url, timeout=Config.TIMEOUT, wait_until='networkidle') # 等待关键商品列表容器加载,这里使用CSS选择器 await page.wait_for_selector('#J_goodsList', timeout=Config.TIMEOUT) return page except Exception as e: logger.error(f"访问页面失败 {url}: {e}") await browser.close() return None async def parse_product_list(self, page): """从商品列表页解析商品信息""" products = [] # 定位所有商品项元素 items = await page.query_selector_all('#J_goodsList .gl-item') logger.info(f"本页找到 {len(items)} 个商品项") for item in items: try: # 使用更稳健的选择器,并处理可能缺失的元素 title_elem = await item.query_selector('.p-name a em') title = await title_elem.text_content() if title_elem else 'N/A' title = title.strip() price_elem = await item.query_selector('.p-price strong i') price = await price_elem.text_content() if price_elem else 'N/A' # 店铺名可能在不同位置,这里是一个常见选择器 shop_elem = await item.query_selector('.p-shop a') shop = await shop_elem.text_content() if shop_elem else 'N/A' shop = shop.strip() # 评价数 comment_elem = await item.query_selector('.p-commit strong a') comment = await comment_elem.text_content() if comment_elem else '0' product_info = { 'title': title, 'price': price, 'shop': shop, 'comment': comment, # 可以继续添加其他字段,如图片链接、商品详情页URL等 } products.append(product_info) except Exception as e: logger.warning(f"解析单个商品时出错: {e}") continue # 跳过解析失败的商品,继续下一个 return products async def search_and_crawl(self, keyword, max_pages=2): """执行搜索并爬取多页数据""" for page_num in range(1, max_pages + 1): logger.info(f"开始爬取第 {page_num} 页,关键词: {keyword}") # 构造搜索URL url = Config.BASE_SEARCH_URL.format(keyword=keyword, page=page_num) current_page = await self.fetch_page(url) if not current_page: logger.error(f"第 {page_num} 页获取失败,跳过") continue # 解析当前页商品 page_products = await self.parse_product_list(current_page) self.data_list.extend(page_products) logger.info(f"第 {page_num} 页爬取完成,获得 {len(page_products)} 条商品数据") # 关闭当前页的浏览器实例 await current_page.context.browser().close() # 页面间等待,避免请求过快 if page_num < max_pages: await asyncio.sleep(Config.WAIT_TIME) logger.info(f"所有页面爬取完成,共采集到 {len(self.data_list)} 条数据") return self.data_list def save_to_csv(self, filename='jd_products.csv'): """将数据保存为CSV文件""" if not self.data_list: logger.warning("没有数据可保存") return df = pd.DataFrame(self.data_list) output_path = f'output/{filename}' df.to_csv(output_path, index=False, encoding='utf-8-sig') # utf-8-sig支持Excel直接打开 logger.info(f"数据已保存至: {output_path}")

3.3 主程序入口 (main.py)

协调整个采集流程。

# main.py import asyncio from crawler import JDCrawler async def main(): # 1. 初始化爬虫 crawler = JDCrawler(headless=False) # 首次调试可设为False看浏览器操作 # 2. 执行爬取任务 keyword = "手机" max_pages = 3 # 控制爬取页数,避免过多请求 try: await crawler.search_and_crawl(keyword, max_pages) except Exception as e: print(f"爬虫运行过程中发生错误: {e}") finally: # 3. 保存数据 crawler.save_to_csv(f'jd_{keyword}_data.csv') if __name__ == '__main__': asyncio.run(main())

4. 运行验证与结果分析

4.1 首次运行与调试

  1. 确保在项目根目录下,且虚拟环境已激活。
  2. 运行主程序:
    python main.py
  3. 观察浏览器行为:如果headless=False,你会看到浏览器自动打开京东搜索页,滚动并加载商品。这是验证脚本是否正常工作的最直观方式。
  4. 检查控制台输出:程序会打印日志信息,如“正在访问...”、“本页找到...个商品项”。
  5. 查看输出文件:运行结束后,检查output/目录下是否生成了CSV文件(如jd_手机_data.csv)。

4.2 数据验证与清洗 (data_processor.py)

原始数据可能需要清洗。创建一个数据处理模块。

# data_processor.py import pandas as pd import re def clean_jd_data(input_csv, output_csv): """清洗京东商品数据""" df = pd.read_csv(input_csv) # 1. 价格清洗:去除‘¥’等符号,转换为数值类型 df['price'] = df['price'].astype(str).str.replace('¥', '', regex=False).str.replace(',', '').str.strip() # 将无法转换的设为NaN df['price'] = pd.to_numeric(df['price'], errors='coerce') # 2. 评价数清洗:提取数字 df['comment'] = df['comment'].astype(str).str.extract(r'(\d+)').astype(float) # 3. 标题去重和去除极端短标题(可能是无效数据) df = df.drop_duplicates(subset=['title']) df = df[df['title'].str.len() > 5] # 4. 处理缺失值 df['shop'].fillna('未知店铺', inplace=True) # 5. 保存清洗后的数据 df.to_csv(output_csv, index=False, encoding='utf-8-sig') print(f"数据清洗完成,已保存至 {output_csv}") print(f"原始数据 {len(pd.read_csv(input_csv))} 条,清洗后 {len(df)} 条") return df # 可以在main.py中调用 # cleaned_df = clean_jd_data('output/jd_手机_data.csv', 'output/jd_手机_data_cleaned.csv')

4.3 预期输出与格式

运行成功的标志:

  • 控制台无红色报错,并显示完整的爬取和保存日志。
  • CSV文件被成功创建。
  • 用Excel或文本编辑器打开CSV,能看到结构化的数据,列名如title,price,shop,comment,并且数据基本正确。

5. 常见问题排查与优化策略

在实际运行中,你几乎一定会遇到下面这些问题。

5.1 爬虫被阻断或无法获取数据

问题现象可能原因检查与解决方案
页面空白或提示“网络连接错误”1. IP被暂时限制。
2. 请求头或Cookies不完整。
3. 网站检测到自动化工具。
1.增加等待时间:在config.py中调大WAIT_TIME,或在关键操作后加await page.wait_for_timeout(随机时间)。
2.完善请求头:在browser.new_context()时添加更多headers,甚至从浏览器开发者工具复制完整的headers。
3.使用代理IP:在browser.launch()或context.new_page()时配置代理服务器。
找不到元素(如#J_goodsList)1. 页面结构已更新,选择器失效。
2. 页面未完全加载。
1.更新选择器:手动打开京东页面,使用开发者工具(F12)重新检查元素,获取新的CSS选择器或XPath。这是AI辅助(如SeekSeek)最能帮上忙的地方:把HTML片段给AI,让它帮你生成健壮的选择器。
2.优化等待策略:将wait_until参数从'networkidle'改为'domcontentloaded'或'load',或使用page.wait_for_selector()等待更具体的元素出现。
弹出验证码或滑块触发了反爬机制。1.降低频率:大幅增加页面间和请求间的等待时间,模拟真人操作。
2.更换IP:使用代理IP池。
3.考虑验证码识别服务:对于商业项目,可能需要集成第三方打码平台,但这会增加复杂度和成本。

5.2 代码运行错误与调试

  • asyncio.run()错误或事件循环问题:
    • 现象:在Jupyter或某些IDE中直接运行asyncio.run(main())报错。
    • 解决:确保在主线程中运行。如果环境复杂,可以改用:
      loop = asyncio.get_event_loop() loop.run_until_complete(main())
  • Playwright 浏览器启动失败:
    • 现象:提示找不到浏览器或无法启动。
    • 解决:确认已运行playwright install chromium。如果网络问题导致安装失败,可以设置环境变量PLAYWRIGHT_DOWNLOAD_HOST使用国内镜像源。
  • 数据提取为None或空字符串:
    • 调试:在解析代码中临时加入截图功能,保存问题页面,人工查看元素结构。
      await page.screenshot(path='debug_page.png')
    • 稳健性:像示例代码中一样,对每个query_selector操作进行判空处理(if elem else 'N/A')。

5.3 性能与稳定性优化

  1. 并发控制(高级):Playwright支持多浏览器上下文甚至多浏览器实例并发爬取。但务必谨慎,过高的并发会立刻导致IP被封。可以从慢速开始,逐步测试平台的容忍度。
  2. 断点续爬:将已爬取的页码或商品ID记录到文件或数据库,程序启动时读取,避免重复爬取。
  3. 日志系统:使用Python的logging模块将不同级别的信息(INFO, WARNING, ERROR)输出到文件,便于后期排查问题。
  4. 配置外置:将关键词、页数、请求头等配置项放在config.ini或config.yaml文件中,而不是硬编码在代码里。

6. 从学习到生产:最佳实践与扩展方向

6.1 新手接单避坑指南

  1. 明确需求边界:与客户确认清楚需要采集哪些字段、多少数据、更新频率、数据格式。避免做了一半才发现需求理解有误。
  2. 评估法律与合规风险:了解目标网站的robots.txt协议和服务条款。告知客户数据采集的潜在风险,仅将技术用于合法学习与研究目的。
  3. 报价与交付物:报价时考虑开发时间、维护成本和潜在的反爬对抗成本。明确交付物是代码、可执行程序还是清洗后的数据文件。
  4. 代码交付规范:交付的代码应有清晰的注释、README使用说明和依赖列表(requirements.txt)。

6.2 项目扩展方向

  • 采集更多字段:深入商品详情页,获取规格参数、商品描述、用户评价等。
  • 定时任务:结合APScheduler或操作系统crontab,实现定时自动采集。
  • 数据入库:将数据存入SQLite、MySQL或MongoDB,便于复杂查询和分析。
  • 构建简单UI:使用PyQt或Tkinter为你的爬虫工具制作一个图形界面,方便非技术人员使用。
  • 监控与告警:编写监控脚本,检查爬虫是否正常运行,失败时发送邮件或短信告警。

6.3 生产环境检查清单

在将脚本部署到服务器长期运行前,请核对以下清单:

  • [ ]依赖是否锁定:使用pip freeze > requirements.txt精确锁定所有包版本。
  • [ ]路径是否绝对或可配置:代码中所有文件路径(如output/)不应是相对路径,或应在配置文件中指定绝对路径。
  • [ ]日志是否完备:是否记录了足够的运行信息、错误信息和警告信息到文件。
  • [ ]异常是否被捕获:关键步骤是否有try...except,避免程序因单个商品解析失败而整体崩溃。
  • [ ]资源是否清理:Playwright的浏览器、页面、上下文是否在finally块或async with中正确关闭,避免内存泄漏。
  • [ ]反爬策略是否足够:是否设置了合理的延迟、使用了代理池、定期更换User-Agent。
  • [ ]数据去重机制:是否有机制防止在多次运行中采集重复数据。
  • [ ]是否有停止开关:是否可以通过外部信号(如一个特定的文件)优雅地停止正在运行的爬虫任务。

通过这个完整的项目流程,你不仅学会了用Playwright采集京东数据,更重要的是掌握了处理动态网页、应对反爬、结构化代码和排查问题的通用方法。在实际接单中,客户的需求千变万化,但解决问题的逻辑和工具链是相通的。从这个小项目出发,不断迭代和扩展你的技能树,就能更从容地应对更复杂的数据采集需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询