1688商品爬虫实战:Selenium绕过滑块+SQLite入库毕设方案
发布时间:2026/10/2 12:09:31来源:尧图网络
简介本资源是一套基于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存储→可视化初稿→项目报告全文闭环落地的毕设实体。它用SeleniumChromeDriver模拟真实用户行为专治1688首页搜索、商品列表翻页、详情页字段抽取这三类高频翻车点内置sqlalchemy写入SQLite免装MySQL附带一份可直接粘贴进答辩PPT的Word版项目报告含需求分析、技术选型对比表、爬取结果截图、数据质量说明。适合零基础但肯动手的学生不需要懂JS逆向不依赖第三方API所有代码在Windows/Mac上装好Python 3.9就能跑通。如果你的毕设卡在“数据哪来”“怎么证明没造假”“报告怎么写”这个包就是你最后一块拼图。2. 环境搭建与核心模块解析为什么必须用Selenium而不是requests2.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-chromedriver2uc而非原生selenium.webdriver.Chrome原因很现实原生ChromeDriver启动的浏览器会被1688识别为自动化工具直接拦截返回“检测到异常行为”uc通过修改WebDriver指纹、注入随机User-Agent、绕过navigator.webdriver检测让1688认为这是普通用户它自动管理ChromeDriver版本匹配避免手动下载支持无头模式headlessTrue和有头模式调试用切换。提示不要用网上流传的“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: # 等待搜索框加载classsearch-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() # 关键等待商品列表容器出现classoffer-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: # 定位所有商品项classoffer-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 fhttps://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[classoffer-item])更快Selenium内部优化过class查找get_attribute(href)比.text更可靠因为有些链接文字被CSS隐藏if href and offer in href是简单但有效的过滤1688商品链接必含offer路径。3.3 详情页字段抽取应对动态加载与结构变异的容错策略detail_page.py是字段提取的黑匣子。1688详情页结构复杂价格可能在span classprice或meta itempropprice销量在span classsales或div># crawler/detail_page.py def extract_detail_fields(driver): 从详情页提取关键字段支持多种HTML结构 fields { title: , price: , sales: , supplier: , attributes: {} } # 标题优先取h1 fallback到meta[propertyog: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[propertyog:title]) fields[title] meta_title[0].get_attribute(content).strip() if meta_title else # 价格尝试3种常见位置 price_selectors [ span.price, meta[itempropprice], 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-spmsales], span[data-rolesales] ] 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_keyTrue) offer_id Column(String(50), uniqueTrue, indexTrue) # 从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, defaultdatetime.now) def init_db(db_path): 初始化数据库创建表 engine create_engine(fsqlite:///{db_path}, echoFalse) Base.metadata.create_all(engine) return sessionmaker(bindengine)() def insert_or_ignore(session, offer_data): 插入数据若offer_id已存在则忽略 try: # 从URL提取offer_idhttps://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_idoffer_id, titleoffer_data.get(title, )[:200], priceoffer_data.get(price, ), salesoffer_data.get(sales, ), supplieroffer_data.get(supplier, )[:100], attributesjson.dumps(offer_data.get(attributes, {}), ensure_asciiFalse), ) session.add(offer) session.commit() return True except Exception as e: session.rollback() print(f入库失败: {e}) return False参数说明uniqueTrue确保offer_id唯一insert_or_ignore利用SQLite的INSERT OR IGNORE语义[:200]截断长文本避免SQLite字段溢出json.dumps(..., ensure_asciiFalse)保留中文否则属性表存成\u4f9b\u5e94\u5546乱码。4. 避坑指南毕设学生踩过的7个真实坑与血泪解决方案4.1 现象Chrome启动报错“chrome not reachable”或“session not created”原因ChromeDriver版本与本地Chrome浏览器不匹配如Chrome 120需ChromeDriver 120.xundetected-chromedriver2未正确安装或版本过旧v3.4.2以下不支持Chrome 118杀毒软件/防火墙拦截Chrome进程尤其国内某些安全软件。解决执行chrome --version查看Chrome版本去https://chromedriver.chromium.org/下载对应Driver升级ucpip install --upgrade undetected-chromedriver2临时关闭杀毒软件或在Chrome启动参数中添加--disable-blink-featuresAutomationControlled本项目config.py已内置。4.2 现象搜索后页面空白offer-list元素始终找不到原因1688更新了搜索页DOM结构classoffer-list被替换为idoffer-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默认用ASCIIcreate_engine缺少connect_args{check_same_thread: False}参数多线程写入时必需。解决修改init_db函数engine create_engine( fsqlite:///{db_path}, echoFalse, connect_args{check_same_thread: False}, encodingutf-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”。我的做法是启动爬虫前在Chrome中打开DevToolsF12→ Network标签手动在1688搜索“手机壳”观察Network中offer_search.htm请求的Headers、Response运行爬虫对比main.py中driver.get()后的Network请求指出X-Requested-With: XMLHttpRequest和Referer: https://www.1688.com/完全一致截图保存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;结果示例total240, title_filled240, price_filled238, sales_filled182, sales_fill_rate75.83。在报告中写“销量字段填充率75.83%符合1688实际——部分商品未开启销量展示如定制类属正常业务现象”。数据说话比解释有力十倍。5.3 项目报告Word的“可编辑性陷阱”如何让导师能直接复制你的图表网络上下载的毕设报告常是PDF或加密Word导师无法修改评语。本项目report/1688_毕设报告.docx做了三处关键处理所有截图用PNG格式嵌入非链接右键“另存为”可单独保存表格用Word原生表格非图片双击可编辑文字图表数据源用Excel嵌入插入→对象→由文件创建双击图表即打开Excel修改。从那以后我每次交毕设材料都强制走一遍“导师视角测试”用鼠标右键、双击、CtrlA全选确认所有内容可编辑、可复制、可验证。这不仅是技术细节更是职业习惯——交付物的第一性原理是“让使用者省力”。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网