一、问题背景

目标很明确:在真实用户环境下,从淘宝搜索结果页批量采集商品信息(名称、价格、店铺、链接),并支持结构化导出。

约束条件:

  • 淘宝具备较强反自动化机制(登录校验、滑块验证、行为检测)

  • 页面为动态渲染(懒加载 + 多版本 DOM)

  • 搜索结果可能在新标签页打开

  • 翻页结构不稳定

因此,本项目核心不是“爬”,而是在接近真实用户行为的前提下,稳定获取结构化数据


二、整体架构

流程拆分为五个模块:

浏览器初始化 → 登录 → 搜索 → 数据采集 → 数据保存

对应代码结构:

main()
 ├── login(driver)
 ├── search(driver)
 ├── collect(driver)
 └── save_data(data)

三、浏览器初始化与反检测

核心问题

Selenium 默认特征非常明显,会被识别为自动化工具。

解决方案

通过 Edge + CDP + 指纹伪装:

options = Options()

options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")

options.add_argument(
    'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
)

进一步隐藏:

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
    Object.defineProperty(navigator, 'webdriver', {
      get: () => undefined
    })
    """
})

四、登录逻辑(重点难点)

淘宝登录包含三类阻碍:

  1. 账号密码输入

  2. 滑块验证

  3. 手机验证码

关键实现

username_input = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "#fm-login-id"))
)
username_input.send_keys(login_username)

password_input = driver.find_element(By.CSS_SELECTOR, "#fm-login-password")
password_input.send_keys(login_password)

滑块处理(模拟人类轨迹)

track = [20, 40, 60, 80, 50, 30, 10, 5, 2]

for x in track:
    action.move_by_offset(xoffset=x, yoffset=random.randint(-2, 2)).perform()
    time.sleep(random.uniform(0.1, 0.3))

验证逻辑

driver.find_element(By.LINK_TEXT, "我的淘宝")

五、搜索模块(兼容多版本 DOM)

淘宝搜索框存在多个版本:

selectors = [
    (By.ID, "q"),
    (By.CSS_SELECTOR, "input[name='q']"),
    (By.CSS_SELECTOR, ".search-combobox-input"),
]

逐个尝试:

for by, sel in selectors:
    try:
        search_input = driver.find_element(by, sel)

搜索触发策略

优先回车:

search_input.send_keys(product_name + '\n')

失败 fallback:

driver.execute_script("arguments[0].click();", btn)

新标签页处理

handles = driver.window_handles

if len(handles) > 1:
    driver.switch_to.window(handles[-1])

六、数据采集(核心逻辑)

1. 懒加载处理

for i in range(3):
    driver.execute_script(
        f"window.scrollTo(0, document.body.scrollHeight * {(i+1)/3});"
    )

2. 商品卡片定位(多策略兜底)

products = driver.find_elements(By.CSS_SELECTOR, "a[class*='doubleCardWrapperAdapt--']")

兜底:

div[class*='search-content-col'] > a
div[class*='Card--']

3. 字段解析

title_elem = product.find_element(By.CSS_SELECTOR, "div[class*='title--']")
product_name = title_elem.get_attribute("title") or title_elem.text
price_elem = product.find_element(By.CSS_SELECTOR, "div[class*='innerPriceWrapper--']")
shop_elem = product.find_element(By.CSS_SELECTOR, "a[class*='shopName--']")

4. 翻页机制(关键稳定性设计)

不依赖固定 class,而是基于“语义匹配”:

next_btn_selectors = [
    "//button[contains(., '下一页')]",
    "//a[contains(., '下一页')]",
]

过滤无效按钮:

if el.get_attribute("disabled"):
    continue

点击:

driver.execute_script("arguments[0].click();", next_btn)

验证采集成功

输出:

print(f"第 {current_page} 页采集完毕,累计 {len(product_list)} 条")

七、数据导出模块

支持四种格式:

格式

技术

CSV

csv

Excel

pandas

JSON

json

XML

ElementTree


示例:Excel 导出

df = pd.DataFrame(data, columns=['商品名称', '价格', '店铺名称', '商品链接'])
df.to_excel(filepath, index=False)

兼容处理(无 pandas)

自动降级:

fallback_filepath = f"{filename}_excel_fallback.csv"

八、完整执行流程

driver.get("https://www.taobao.com")

login(driver)
search(driver)
collect(driver)
save_data(product_list)

九、实际问题与原因分析

1. 登录失败

可能原因:

  • DOM 结构变化

  • 触发风控(IP / 行为异常)

  • 需要短信验证

处理:

  • 增加人工介入(input暂停)

  • 降低自动化速度


2. 商品抓取为空

原因:

  • 懒加载未触发

  • selector 失效

处理:

  • 增加 scroll 次数

  • 扩展 CSS 匹配规则


3. 翻页失效

原因:

  • 按钮被遮挡

  • class 动态变化

处理:

  • 改用 XPath + 文本匹配

  • 强制 JS 点击


十、结论(实事求是)

该脚本本质不是“高并发爬虫”,而是一个模拟真实用户行为的浏览器自动化采集工具

其有效性的前提:

  • 不追求速度(否则触发风控)

  • 接受人工参与(验证码环节)

  • 持续适配页面结构变化


十一、可扩展方向

  1. 接入代理池(降低风控概率)

  2. 引入 OCR 自动识别验证码

  3. 数据入库(MySQL / MongoDB)

  4. 分布式调度(但风险显著上升)