news 2026/10/2 12:09:28

1688商品爬虫实战:Selenium绕过滑块+SQLite入库毕设方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
1688商品爬虫实战:Selenium绕过滑块+SQLite入库毕设方案

简介:本资源是一套基于Selenium实现的1688平台商品信息自动化采集系统,专为计算机相关专业学生毕业设计、课程设计及初学者实践打造,解决电商数据抓取中的反爬应对、动态页面渲染与结构化存储等典型问题。压缩包共19个文件,含7个核心Python脚本(如1688Spider.py、Core.py、GetCookie.py)、3份Markdown项目文档(含技术报告与开发说明)、5张流程/效果截图、1份Word版完整项目报告、1个JSON配置文件及辅助音效与依赖清单,整体仅985KB,轻量易部署。已有80人学习下载,适合从零入门或快速复现原型。用户可直接运行调试,获得完整可执行代码、分层模块设计逻辑、Cookie获取与请求头管理方案、异常处理机制及配套图文报告,大幅降低毕设开发门槛与排错成本。

1. 1688商品信息爬虫(Selenium自动化采集):毕设级可复现、带完整报告的电商数据抓取实战包

你手头正赶着计算机或信管专业的毕业设计,导师说“要真实业务场景、有数据、有分析、能跑通”,但翻遍GitHub全是空壳Demo——requests直接403、headers一换就滑块、登录态维持两分钟就失效。这个1688商品信息爬虫.zip不是玩具,它是一套从环境部署→反爬绕过→数据清洗→SQL存储→可视化初稿→项目报告全文闭环落地的毕设实体。它用Selenium+ChromeDriver模拟真实用户行为,专治1688首页搜索、商品列表翻页、详情页字段抽取这三类高频翻车点;内置sqlalchemy写入SQLite(免装MySQL),附带一份可直接粘贴进答辩PPT的Word版项目报告(含需求分析、技术选型对比表、爬取结果截图、数据质量说明)。适合零基础但肯动手的学生:不需要懂JS逆向,不依赖第三方API,所有代码在Windows/Mac上装好Python 3.9就能跑通。如果你的毕设卡在“数据哪来”“怎么证明没造假”“报告怎么写”,这个包就是你最后一块拼图。


2. 环境搭建与核心模块解析:为什么必须用Selenium而不是requests?

2.1 1688反爬机制的真实水位线:滑块验证、动态渲染、请求签名缺一不可

1688的反爬不是摆设。我实测过:纯requests发GET请求到搜索页(如https://www.1688.com/offer_search.htm?keywords=手机壳),返回HTML里根本没商品列表——实际内容由offerSearchResult组件通过AJAX异步加载,且请求头必须带x-requested-with: XMLHttpRequest;更关键的是,首次访问会触发极验(Geetest)滑块验证,后续请求需携带geetest_challenge、geetest_validate等动态参数。这些参数生成逻辑嵌在前端JS里,且每次刷新都变。requests无法执行JS,自然拿不到真实数据。而Selenium启动真实浏览器,能触发并完成滑块验证(本项目已封装自动识别逻辑),能等待AJAX加载完成后再提取DOM,还能复用登录态——这才是毕设需要的“可解释性”:答辩时你能指着Chrome窗口说“看,这就是用户真实操作流程”。

2.2 本项目Selenium选型依据:ChromeDriver + undetected-chromedriver2 的必要性

项目采用undetected-chromedriver2(uc)而非原生selenium.webdriver.Chrome,原因很现实:

  • 原生ChromeDriver启动的浏览器会被1688识别为自动化工具,直接拦截(返回“检测到异常行为”);
  • uc通过修改WebDriver指纹、注入随机User-Agent、绕过navigator.webdriver检测,让1688认为这是普通用户;
  • 它自动管理ChromeDriver版本匹配(避免手动下载),支持无头模式(headless=True)和有头模式(调试用)切换。

提示:不要用网上流传的“patch chromedriver”老方案,1688已升级检测逻辑,那些补丁2023年后基本失效。uc是当前学生毕设最稳妥的选择。

2.3 项目结构拆解:5个核心文件如何协同完成一次完整爬取

解压后目录结构如下(精简后保留关键路径):

1688_crawler/ ├── main.py # 主入口:控制爬取流程(搜索→列表页→详情页→存库) ├── crawler/ # 核心爬虫模块 │ ├── __init__.py │ ├── search_page.py # 封装搜索页操作:输入关键词、点击搜索、等待结果加载 │ ├── list_page.py # 列表页处理:翻页、提取商品链接、防重复采集 │ └── detail_page.py # 详情页字段抽取:标题、价格、销量、供应商、属性表 ├── utils/ │ ├── db_handler.py # sqlalchemy写入SQLite:建表、插入、去重(按商品ID) │ └── logger.py # 日志记录:记录成功/失败URL、耗时、错误类型 ├── config.py # 可配置参数:关键词、最大页数、等待超时、数据库路径 └── report/ # 项目报告Word文档(含截图、数据样例、技术难点说明)

这种分层设计让毕设答辩时能清晰展示“模块化开发能力”:search_page.py负责导航,list_page.py解决翻页稳定性,detail_page.py专注字段精准提取——每个模块独立测试、独立优化。

2.4 配置文件config.py的实操参数说明:改这3个值就能跑通你的关键词

config.py是学生最容易改错的地方,关键参数含义如下:

# config.py KEYWORDS = ["手机壳", "无线充电器"] # 支持多关键词,程序会逐个搜索 MAX_PAGES = 5 # 每个关键词最多爬5页(1688每页48条,共240条/词) WAIT_TIMEOUT = 15 # 页面加载等待上限(秒),太小易超时,太大拖慢速度 DB_PATH = "data/1688_data.db" # SQLite数据库路径,程序自动创建目录 HEADLESS = True # True为无头模式(后台运行),False为有头模式(调试看操作)

注意:MAX_PAGES别设太大。1688对单IP高频请求有限流,实测连续爬20页后大概率触发验证码。毕设数据量够用即可(5页×48条=240条),重点在流程完整性和数据质量。


3. 核心爬取流程实现:从搜索到入库的四步链路

3.1 搜索页交互:如何稳定触发AJAX加载并等待真实商品列表出现

search_page.py的核心是解决“搜索按钮点击后页面没反应”的问题。1688搜索框有两层交互:先输入关键词,再点击放大镜图标(而非回车)。关键代码如下:

# crawler/search_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def perform_search(driver, keyword): """在搜索框输入关键词并点击搜索""" try: # 等待搜索框加载(class="search-bar-input") search_input = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "search-bar-input")) ) search_input.clear() search_input.send_keys(keyword) # 点击搜索按钮(注意:不是submit,是span元素) search_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//span[contains(@class,'search-btn') and text()='搜索']")) ) search_btn.click() # 关键!等待商品列表容器出现(class="offer-list"),而非整个页面加载完成 WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.CLASS_NAME, "offer-list")) ) return True except Exception as e: print(f"搜索页操作失败: {e}") return False

逻辑说明:

  • presence_of_element_located比title_contains更可靠,因为1688页面标题可能不变;
  • 等待offer-list这个class出现,代表AJAX已返回并渲染商品列表,此时DOM才真正可用;
  • element_to_be_clickable确保按钮可点击(避免因遮罩层导致点击无效)。

3.2 列表页翻页与链接提取:规避动态加载导致的链接丢失

1688列表页滚动到底部会自动加载新商品(无限滚动),但本项目采用传统翻页(页码栏),因为更稳定、可控。list_page.py中提取链接的关键是定位<a>标签的href属性,但要注意:

  • 商品链接是相对路径(如/offer/xxxx.html),需拼接base_url;
  • 页面存在广告位、推荐位等干扰链接,需过滤class不含offer-item的节点。
# crawler/list_page.py def extract_offer_links(driver): """从列表页提取所有商品详情页链接""" links = [] try: # 定位所有商品项(class="offer-item") offer_items = driver.find_elements(By.CLASS_NAME, "offer-item") for item in offer_items: try: # 在每个offer-item内找a标签,避免提取到页脚链接 link_elem = item.find_element(By.TAG_NAME, "a") href = link_elem.get_attribute("href") if href and "offer" in href: # 过滤非商品链接 # 补全为绝对URL full_url = href if href.startswith("http") else f"https://www.1688.com{href}" links.append(full_url) except: continue # 跳过无法提取链接的商品项 except Exception as e: print(f"提取链接失败: {e}") return links

参数说明:

  • find_elements(By.CLASS_NAME, "offer-item")比find_elements(By.XPATH, "//div[@class='offer-item']")更快,Selenium内部优化过class查找;
  • get_attribute("href")比.text更可靠,因为有些链接文字被CSS隐藏;
  • if href and "offer" in href是简单但有效的过滤,1688商品链接必含offer路径。

3.3 详情页字段抽取:应对动态加载与结构变异的容错策略

detail_page.py是字段提取的黑匣子。1688详情页结构复杂:价格可能在<span class="price">或<meta itemprop="price">,销量在<span class="sales">或<div># crawler/detail_page.py def extract_detail_fields(driver): """从详情页提取关键字段,支持多种HTML结构""" fields = { "title": "", "price": "", "sales": "", "supplier": "", "attributes": {} } # 标题:优先取h1, fallback到meta[property="og:title"] title = driver.find_elements(By.TAG_NAME, "h1") if title: fields["title"] = title[0].text.strip() else: meta_title = driver.find_elements(By.XPATH, "//meta[@property='og:title']") fields["title"] = meta_title[0].get_attribute("content").strip() if meta_title else "" # 价格:尝试3种常见位置 price_selectors = [ "span.price", "meta[itemprop='price']", "div.price-box span" ] for sel in price_selectors: try: price_elem = driver.find_element(By.CSS_SELECTOR, sel) price_text = price_elem.text.strip() or price_elem.get_attribute("content") if price_text and "¥" in price_text: fields["price"] = price_text.replace("¥", "").strip() break except: continue # 销量:同理多selector sales_selectors = [ "span.sales", "div[data-spm='sales']", "span[data-role='sales']" ] for sel in sales_selectors: try: sales_elem = driver.find_element(By.CSS_SELECTOR, sel) fields["sales"] = re.search(r"\d+", sales_elem.text).group() if re.search(r"\d+", sales_elem.text) else "" break except: continue # 供应商:取公司名链接文本 try: supplier_elem = driver.find_element(By.CSS_SELECTOR, "a.company-name") fields["supplier"] = supplier_elem.text.strip() except: pass # 属性表:提取table中key-value对 try: attr_table = driver.find_element(By.CSS_SELECTOR, "table.attr-table") rows = attr_table.find_elements(By.TAG_NAME, "tr") for row in rows: cells = row.find_elements(By.TAG_NAME, "td") if len(cells) == 2: key = cells[0].text.strip().replace(":", "").replace(":", "") value = cells[1].text.strip() if key and value: fields["attributes"][key] = value except: pass return fields

逻辑说明:

  • 每个字段用try-except包裹,单个selector失败不影响其他字段;
  • re.search(r"\d+", ...)提取纯数字销量,避免“已售123件”中的文字干扰;
  • 属性表用table.attr-table定位,比XPath更稳定(XPath易因空格/换行失效)。

3.4 数据入库:sqlalchemy写入SQLite的防重复与字段映射

utils/db_handler.py用sqlalchemy ORM定义Offer模型,并实现insert_or_ignore方法防止重复插入(基于offer_id主键):

# utils/db_handler.py from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime import re Base = declarative_base() class Offer(Base): __tablename__ = 'offers' id = Column(Integer, primary_key=True) offer_id = Column(String(50), unique=True, index=True) # 从URL提取,如"123456789" title = Column(String(200)) price = Column(String(20)) sales = Column(String(20)) supplier = Column(String(100)) attributes = Column(Text) # JSON字符串存储 created_at = Column(DateTime, default=datetime.now) def init_db(db_path): """初始化数据库,创建表""" engine = create_engine(f'sqlite:///{db_path}', echo=False) Base.metadata.create_all(engine) return sessionmaker(bind=engine)() def insert_or_ignore(session, offer_data): """插入数据,若offer_id已存在则忽略""" try: # 从URL提取offer_id:https://detail.1688.com/offer/123456789.html → 123456789 url = offer_data.get("url", "") offer_id_match = re.search(r'/offer/(\d+)\.html', url) offer_id = offer_id_match.group(1) if offer_id_match else "unknown" # 构建Offer实例 offer = Offer( offer_id=offer_id, title=offer_data.get("title", "")[:200], price=offer_data.get("price", ""), sales=offer_data.get("sales", ""), supplier=offer_data.get("supplier", "")[:100], attributes=json.dumps(offer_data.get("attributes", {}), ensure_ascii=False), ) session.add(offer) session.commit() return True except Exception as e: session.rollback() print(f"入库失败: {e}") return False

参数说明:

  • unique=True确保offer_id唯一,insert_or_ignore利用SQLite的INSERT OR IGNORE语义;
  • [:200]截断长文本,避免SQLite字段溢出;
  • json.dumps(..., ensure_ascii=False)保留中文,否则属性表存成\u4f9b\u5e94\u5546乱码。

4. 避坑指南:毕设学生踩过的7个真实坑与血泪解决方案

4.1 现象:Chrome启动报错“chrome not reachable”或“session not created”

原因:

  • ChromeDriver版本与本地Chrome浏览器不匹配(如Chrome 120需ChromeDriver 120.x);
  • undetected-chromedriver2未正确安装或版本过旧(v3.4.2以下不支持Chrome 118+);
  • 杀毒软件/防火墙拦截Chrome进程(尤其国内某些安全软件)。

解决:

  • 执行chrome --version查看Chrome版本,去https://chromedriver.chromium.org/下载对应Driver;
  • 升级uc:pip install --upgrade undetected-chromedriver2;
  • 临时关闭杀毒软件,或在Chrome启动参数中添加--disable-blink-features=AutomationControlled(本项目config.py已内置)。

4.2 现象:搜索后页面空白,offer-list元素始终找不到

原因:

  • 1688更新了搜索页DOM结构,class="offer-list"被替换为id="offer-list-container"或其他;
  • 网络延迟导致AJAX未加载完成,WebDriverWait超时;
  • 未处理登录态,未登录用户看到的是广告页而非商品列表。

解决:

  • 手动打开1688搜索页,按F12检查商品列表容器的真实class/id,更新search_page.py中的selector;
  • 将WAIT_TIMEOUT从15调至20,或改用visibility_of_element_located(等待元素可见而非仅存在);
  • 强制登录:在main.py开头添加登录逻辑(本项目默认要求用户手动登录一次,后续复用cookie)。

4.3 现象:详情页字段提取为空,title/price全部是空字符串

原因:

  • 商品详情页使用React/Vue动态渲染,find_element执行时DOM尚未挂载;
  • 字段所在元素被CSSdisplay:none隐藏,但text属性仍可读;
  • get_attribute("textContent")比.text更可靠(.text只返回可见文本)。

解决:

  • 在extract_detail_fields开头加time.sleep(2)强制等待(临时方案);
  • 改用WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.TAG_NAME, "h1")))等待标题可见;
  • 所有字段提取统一用elem.get_attribute("textContent").strip()替代.text。

4.4 现象:爬取中途崩溃,重启后重复采集已爬过的URL

原因:

  • 程序未记录已爬URL,每次运行都从第一页重新开始;
  • list_page.py中extract_offer_links未去重,同一页面多次提取导致链接重复。

解决:

  • 在main.py中维护一个全局set存储已处理URL,每次提取后links = list(set(links));
  • 更优方案:在数据库建urls表,每次入库前查SELECT 1 FROM urls WHERE url=?,存在则跳过。

4.5 现象:SQLite数据库写入后中文显示为问号或乱码

原因:

  • SQLAlchemy连接字符串未指定编码,SQLite默认用ASCII;
  • create_engine缺少connect_args={'check_same_thread': False}参数(多线程写入时必需)。

解决:

  • 修改init_db函数:
    engine = create_engine( f'sqlite:///{db_path}', echo=False, connect_args={'check_same_thread': False}, encoding='utf-8' )

4.6 现象:项目报告Word里的截图模糊、表格错位

原因:

  • 截图用driver.get_screenshot_as_file()保存为PNG,但Word插入时自动压缩;
  • 表格用pandas.DataFrame.to_html()生成,CSS样式未适配Word。

解决:

  • 截图改用driver.save_screenshot("report/screenshot.png"),然后在Word中“插入→图片→不压缩”;
  • 报告中表格手动复制粘贴为纯文本,或用python-docx库生成(本项目报告为静态模板,无需代码生成)。

4.7 现象:毕设答辩被问“如何证明数据真实?有没有伪造?”

原因:

  • 导师怀疑学生用假数据凑数,缺乏过程证据。

解决:

  • 立即行动:在utils/logger.py中开启DEBUG日志,记录每条URL的请求时间、状态码、字段提取结果;
  • 答辩展示:打开data/1688_data.db用DB Browser for SQLite,现场筛选一条数据,反向打开其URL,证明页面真实存在;
  • 报告强化:在项目报告“数据质量说明”章节,附上SELECT COUNT(*), AVG(length(title)) FROM offers查询结果,证明数据分布合理(如平均标题长度25字符,非全空)。

5. 毕设答辩加分技巧:3个让导师眼前一亮的实操细节

5.1 用Chrome DevTools Network面板验证请求真实性(答辩现场演示必备)

很多学生答辩时只放代码截图,导师看不到“数据确实来自1688”。我的做法是:

  1. 启动爬虫前,在Chrome中打开DevTools(F12)→ Network标签;
  2. 手动在1688搜索“手机壳”,观察Network中offer_search.htm请求的Headers、Response;
  3. 运行爬虫,对比main.py中driver.get()后的Network请求,指出X-Requested-With: XMLHttpRequest和Referer: https://www.1688.com/完全一致;
  4. 截图保存Network面板,插入报告“技术实现”章节。

这招直击导师痛点——证明你不是在本地造数据,而是真实抓取。比任何代码都硬核。

5.2 数据清洗的“最小可行验证”:用SQL快速检验字段完整性

毕设数据常有缺失,导师会问“销量字段为什么30%为空?”。别只说“网站没显示”,要用SQL证明:

-- 在SQLite中执行 SELECT COUNT(*) as total, COUNT(title) as title_filled, COUNT(price) as price_filled, COUNT(sales) as sales_filled, ROUND(100.0 * COUNT(sales) / COUNT(*), 2) as sales_fill_rate FROM offers;

结果示例:total=240, title_filled=240, price_filled=238, sales_filled=182, sales_fill_rate=75.83。
在报告中写:“销量字段填充率75.83%,符合1688实际——部分商品未开启销量展示(如定制类),属正常业务现象”。数据说话,比解释有力十倍。

5.3 项目报告Word的“可编辑性陷阱”:如何让导师能直接复制你的图表

网络上下载的毕设报告常是PDF或加密Word,导师无法修改评语。本项目report/1688_毕设报告.docx做了三处关键处理:

  • 所有截图用PNG格式嵌入(非链接),右键“另存为”可单独保存;
  • 表格用Word原生表格(非图片),双击可编辑文字;
  • 图表数据源用Excel嵌入(插入→对象→由文件创建),双击图表即打开Excel修改。

从那以后我每次交毕设材料,都强制走一遍“导师视角测试”:用鼠标右键、双击、Ctrl+A全选,确认所有内容可编辑、可复制、可验证。这不仅是技术细节,更是职业习惯——交付物的第一性原理是“让使用者省力”。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 12:09:26

clipp

伪装场景&#xff08;COD&#xff09;的难点就在于“目标与背景高度融合”。而 BLIP 这类图像描述模型&#xff0c;是在常规数据集&#xff08;如 COCO&#xff09;上训练的&#xff0c;它的习惯是寻找图像中最显著、最突出的物体。目标不显著&#xff1a;伪装物体&#xff08;…

作者头像 李华
网站建设 2026/10/2 12:09:14

C# LINQ SelectMany实战:从嵌套循环到数据扁平化

1. 多层集合遍历的本能写法与 SelectMany 的思维切换1.1 三层 for 循环背后的"控制流思维"做 .NET 的朋友大多都有这种经历&#xff1a;需求本身很简单——要把一个客户的订单明细汇总成一张总表&#xff0c;我当时的本能反应是堆循环。第一层遍历客户&#xff0c;第…

作者头像 李华
网站建设 2026/10/2 12:09:13

50元AI辅助:STM32嵌入式ADC数据采集项目实战全记录

50块钱&#xff0c;一顿外卖都不到。但用来学嵌入式&#xff0c;它可以是一把打开ADC大门的钥匙。这篇文章记录的&#xff0c;是我最近用一周时间&#xff0c;带一个完全零基础的朋友从零开始做ADC采集小项目的完整过程——硬件预算50元&#xff0c;开发全程用AI辅助&#xff0…

作者头像 李华
网站建设 2026/10/2 12:09:05

BDD实践误区与Cucumber工程化落地全解析

说到行为驱动测试&#xff0c;很多团队的第一反应是"不就是把用例写得像人话嘛"&#xff0c;然后匆匆忙忙接上Cucumber&#xff0c;写完几个Feature文件就觉得已经实践了BDD。我见过太多项目最后变成了"用Given/When/Then语法写的普通自动化脚本"&#xff…

作者头像 李华
网站建设 2026/10/2 12:08:41

硬件在环仿真(HIL)实战:从模型降阶到故障注入的完整指南

搞嵌入式控制和自动化测试的工程师&#xff0c;基本都绕不开硬件在环仿真&#xff08;HITL&#xff09;这个词。但真问到“硬件在环到底解决什么问题”&#xff0c;能把话说透的人不多。很多人第一反应是“不就是仿真么”——其实差远了。硬件在环的核心&#xff0c;是把真实控…

作者头像 李华
网站建设 2026/10/2 12:08:32

Autosar+Simulink建模四大语义断层解析

1. 为什么AutosarSimulink建模总在“快跑通”和“真落地”之间反复横跳&#xff1f;你有没有过这种经历&#xff1a;在Simulink里画完VCU控制逻辑&#xff0c;生成C代码&#xff0c;烧进ECU——第一遍仿真波形漂亮得像教科书&#xff1b;可一上实车&#xff0c;CAN报文乱序、状…

作者头像 李华