美团外卖平台的商家列表、菜品详情、订单数据等核心内容均采用 JavaScript 动态渲染加载,传统 requests 库仅能获取未渲染的空壳 HTML 文档,无法直接提取有效数据。针对该类动态页面,主流解决方案分为AJAX 接口逆向与浏览器模拟渲染两类:接口逆向开发成本高、易受平台接口更新影响导致失效,因此本文采用 Selenium 自动化测试框架,通过模拟真实浏览器执行环境,等待 JavaScript 渲染完成后精准提取目标数据。
核心技术难点与应对策略
美团外卖平台具备完善的反爬机制,核心限制包括地理位置校验、人机验证拦截、登录态强制校验。针对性解决策略:
- 精准配置地理位置信息,匹配平台商家推荐规则;
- 接入代理 IP 池分散请求流量,规避单 IP 限流封禁;
- 模拟人类用户操作行为(页面滚动、随机请求延迟),降低反爬识别风险。
一、开发环境配置
通过 pip 安装项目核心依赖库,实现浏览器自动化、驱动管理与数据持久化:
● selenium:主流浏览器自动化操作框架,支持模拟用户交互与页面渲染;
● webdriver-manager:自动适配 Chrome 浏览器版本,管理 ChromeDriver 驱动,无需手动配置;
● pymysql:Python 连接 MySQL 数据库的驱动库,实现爬取数据的结构化存储。
二、Selenium WebDriver 初始化
配置 Chrome 浏览器启动参数,构建稳定的自动化运行环境,核心采用显式等待保证页面元素加载完成:
python
运行
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 webdriver_manager.chrome import ChromeDriverManager
import time
import random
配置Chrome启动选项
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless') # 无头模式,生产环境推荐启用
chrome_options.add_argument('--disable-gpu') # 禁用GPU加速,适配服务器环境
chrome_options.add_argument('--no-sandbox') # 关闭沙盒模式,Linux环境必备
chrome_options.add_argument('--disable-dev-shm-usage') # 解决共享内存不足问题
配置真实User-Agent,伪装浏览器请求
chrome_options.add_argument('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')
初始化WebDriver,自动安装匹配的ChromeDriver
driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
显式等待:最长等待10秒,等待元素加载完成后执行操作
wait = WebDriverWait(driver, 10)
关键配置说明
- 无头模式:无界面运行浏览器,降低资源消耗,适合服务器部署;
- User-Agent 伪装:模拟真实浏览器请求头,减少被反爬识别的概率;
- 显式等待:替代固定休眠,高效等待页面元素加载,提升爬虫稳定性与效率。
三、地理位置限制解决方案
美团外卖基于用户地理位置实现商家精准推荐,未配置合法位置将无法获取有效数据,提供两种标准化实现方案:
方案一:Cookie 持久化复用(推荐生产环境使用)
首次手动登录美团外卖并设置位置,保存 Cookie 至本地文件,后续直接复用登录态与位置信息:
python
运行
import json
1. 首次手动登录后,获取并保存Cookie到本地文件
cookies = driver.get_cookies()
with open('meituan_cookies.json', 'w', encoding='utf-8') as f:
json.dump(cookies, f, ensure_ascii=False)
2. 后续爬虫启动时,加载本地Cookie恢复登录状态与位置信息
driver.get('https://www.meituan.com/')
with open('meituan_cookies.json', 'r', encoding='utf-8') as f:
cookies = json.load(f)
for cookie in cookies:
driver.add_cookie(cookie)
方案二:前端 JS 注入经纬度信息
通过执行 JavaScript 代码,直接修改浏览器本地存储,强制设置目标地理位置:
python
运行
访问美团外卖首页
driver.get('https://www.meituan.com/')
注入经纬度与位置信息(以北京市朝阳区为例)
driver.execute_script('''
localStorage.setItem('geohash', 'wx4g0e0e0');
localStorage.setItem('location', '{"latitude":39.9042,"longitude":116.4074,"address":"北京市朝阳区"}');
''')
刷新页面生效配置
driver.refresh()
四、商家列表数据爬取
针对美团外卖无限滚动加载的商家列表,模拟用户滚动行为加载全量数据,结合随机延迟规避反爬:
python
运行
def scrape_merchants(keyword: str = '美食', limit: int = 20) -> list:
"""
爬取美团外卖商家列表
:param keyword: 搜索关键词
:param limit: 最大爬取商家数量
:return: 商家信息列表
"""
# 构造搜索URL
search_url = f'https://www.meituan.com/s/{keyword}/'
driver.get(search_url)
time.sleep(3) # 基础页面加载延迟
merchant_list = []
last_page_height = driver.execute_script('return document.body.scrollHeight')
while len(merchant_list) < limit:
# 模拟滚动至页面底部,加载更多商家数据
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
# 随机延迟1.5-3秒,模拟真实用户操作
time.sleep(random.uniform(1.5, 3.0))
# 定位商家元素节点
merchant_elements = driver.find_elements(By.CSS_SELECTOR, '.poi-item')
# 增量提取新增商家数据
for elem in merchant_elements[len(merchant_list):]:
try:
merchant_info = {
'name': elem.find_element(By.CSS_SELECTOR, '.poi-name').text,
'rating': elem.find_element(By.CSS_SELECTOR, '.star-rating').get_attribute('title'),
'address': elem.find_element(By.CSS_SELECTOR, '.address').text,
'link': elem.find_element(By.CSS_SELECTOR, 'a').get_attribute('href')
}
merchant_list.append(merchant_info)
print(f"成功爬取商家:{merchant_info['name']} | 评分:{merchant_info['rating']}")
except Exception as e:
print(f"商家信息提取异常:{str(e)}")
continue
# 判断是否到达页面底部,无新数据则终止循环
new_page_height = driver.execute_script('return document.body.scrollHeight')
if new_page_height == last_page_height:
print('已滚动至页面底部,无更多商家数据')
break
last_page_height = new_page_height
return merchant_list[:limit]
核心实现要点
- 无限滚动处理:通过 JS 获取页面高度,循环滚动加载动态数据;
- 增量提取:仅处理新增渲染的商家元素,避免重复爬取;
异常捕获:单个商家提取异常不中断整体爬虫流程,提升鲁棒性。
五、商家详情与菜品数据爬取
进入商家详情页,加载全量菜品信息,提取商家基础信息与菜品数据:
python
运行
def scrape_merchant_detail(merchant_url: str) -> dict | None:
"""
爬取商家详情及菜品列表
:param merchant_url: 商家详情页链接
:return: 商家详情+菜品列表字典,提取失败返回None
"""
driver.get(merchant_url)
time.sleep(2)提取商家核心基础信息
try:
shop_name = driver.find_element(By.CSS_SELECTOR, '.shop-name').text business_hours = driver.find_element(By.CSS_SELECTOR, '.business-hours').text shop_phone = driver.find_element(By.CSS_SELECTOR, '.phone').textexcept Exception:
print('商家基础信息提取失败') return None滚动加载全部菜品数据
last_height = driver.execute_script('return document.body.scrollHeight')
while True:driver.execute_script('window.scrollTo(0, document.body.scrollHeight);') time.sleep(1) new_height = driver.execute_script('return document.body.scrollHeight') if new_height == last_height: break last_height = new_height提取菜品列表信息
dish_list = []
dish_elements = driver.find_elements(By.CSS_SELECTOR, '.dish-item')
for elem in dish_elements:try: dish_info = { 'name': elem.find_element(By.CSS_SELECTOR, '.dish-name').text, 'price': elem.find_element(By.CSS_SELECTOR, '.dish-price').text, 'sales': elem.find_element(By.CSS_SELECTOR, '.dish-sales').text } dish_list.append(dish_info) except Exception: continuereturn {
'name': shop_name, 'hours': business_hours, 'phone': shop_phone, 'dishes': dish_list}
六、代理 IP 配置(反封禁核心)
美团外卖对单 IP 请求频率严格限制,高频爬取会触发验证码或 IP 封禁,亿牛云爬虫代理接入方案如下:
标准代理配置(Selenium 原生)
python
运行
from selenium.webdriver.common.proxy import Proxy, ProxyType
亿牛云代理基础配置
PROXY_HOST = "t.16yun.cn"
PROXY_PORT = "31111"
PROXY_USER = "your_username"
PROXY_PASS = "your_password"
初始化代理对象
proxy = Proxy({
'proxyType': ProxyType.MANUAL,
'httpProxy': f'{PROXY_HOST}:{PROXY_PORT}',
'sslProxy': f'{PROXY_HOST}:{PROXY_PORT}',
'noProxy': 'localhost,127.0.0.1'
})
启动带代理的浏览器
driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options, proxy=proxy)
七、MySQL 数据持久化
将爬取的结构化数据存储至 MySQL 数据库,实现数据永久保存与后续分析:
python
运行
import pymysql
from pymysql.err import OperationalError
def save_to_mysql(merchant_data: list):
"""
将商家数据存储至MySQL数据库
:param merchant_data: 商家详情数据列表
"""
try:
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='your_password',
database='meituan_data',
charset='utf8mb4'
)
cursor = conn.cursor()
# 创建商家信息表(不存在则创建)
cursor.execute('''
CREATE TABLE IF NOT EXISTS merchants (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
name VARCHAR(255) NOT NULL COMMENT '商家名称',
rating VARCHAR(50) COMMENT '商家评分',
address TEXT COMMENT '商家地址',
link TEXT COMMENT '商家链接',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '爬取时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '美团外卖商家信息表';
''')
# 批量插入数据
for merchant in merchant_data:
cursor.execute('''
INSERT INTO merchants (name, rating, address, link)
VALUES (%s, %s, %s, %s)
''', (merchant['name'], merchant['rating'], merchant['address'], merchant['link']))
conn.commit()
print(f"成功存储 {len(merchant_data)} 条商家数据至MySQL")
except OperationalError as e:
print(f"数据库连接/操作失败:{str(e)}")
finally:
# 确保资源释放
if 'conn' in locals() and conn.open:
cursor.close()
conn.close()
八、爬虫完整执行流程
整合所有模块,实现初始化→登录态恢复→数据爬取→数据存储的全流程自动化:
python
运行
def main():
# 1. 初始化WebDriver
driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
wait = WebDriverWait(driver, 10)
# 2. 恢复登录Cookie
driver.get('https://www.meituan.com/')
try:
with open('meituan_cookies.json', 'r', encoding='utf-8') as f:
cookies = json.load(f)
for cookie in cookies:
driver.add_cookie(cookie)
print("Cookie加载成功,登录态已恢复")
except FileNotFoundError:
print("Cookie文件不存在,请首次手动登录获取")
# 3. 爬取商家列表
merchants = scrape_merchants(keyword='美食', limit=10)
# 4. 爬取商家详情数据
detailed_merchant_data = []
for merchant in merchants:
detail = scrape_merchant_detail(merchant['link'])
if detail:
detailed_merchant_data.append(detail)
# 商家间随机延迟,降低请求频率
time.sleep(random.uniform(2, 5))
# 5. 数据存储至MySQL
save_to_mysql(detailed_merchant_data)
# 6. 关闭浏览器,释放资源
driver.quit()
print("爬虫任务执行完成,浏览器已关闭")
if name == 'main':
main()
九、常见问题排查与性能优化
表格
异常问题 根本原因 标准化解决方案
触发人机验证码 请求频率过高 / 单 IP 异常访问 接入代理 IP 池,增大随机延迟,优化用户行为模拟
Cookie 登录态失效 平台会话超时 / 异地登录检测 定时刷新 Cookie,新增自动重登录逻辑
页面元素定位失败 前端页面结构更新 实时维护 CSS 选择器 / XPath,兼容页面迭代
长时间运行内存泄漏 WebDriver 未正常释放 / 页面缓存堆积 定时重启浏览器实例,强制启用无头模式
爬取效率过低 重复启动浏览器 / 固定休眠过长 复用 WebDriver 实例,用显式等待替代固定休眠
十、技术方案边界与选型建议
Selenium 方案优缺点
● 优势:可直接获取渲染后的完整页面,调试成本低,无需逆向加密接口,适合短期、小规模爬虫项目;
● 劣势:浏览器资源占用高、爬取速度较慢,不适用超大规模分布式采集场景。
大规模采集替代方案
- AJAX 接口逆向:直接调用平台后端 API,爬取效率最高,但需攻克加密参数、签名算法,技术门槛高;
- requests + 代理池:轻量高效,仅适用于无强反爬、非动态渲染的简单页面;
- seleniumwire + 代理池:平衡开发成本与爬取稳定性,适合中规模数据采集。