一、为什么需要爬取美团外卖数据?
在餐饮外卖行业,商家评分和销量是消费者决策的核心依据。对于商家运营者,分析竞品数据可优化定价策略;对于市场调研者,掌握区域销量分布能洞察消费趋势。美团外卖作为国内最大外卖平台,其数据具有极高的商业价值。但手动收集效率低下,本文将通过Python爬虫技术,实现自动化数据采集。
二、技术选型:工具与原理
1. 核心工具包
- Requests:发送HTTP请求获取网页内容
- BeautifulSoup:解析静态HTML结构
- Selenium:模拟浏览器行为处理动态加载
- Pandas:数据清洗与存储
- 代理IP池:突破反爬机制
2. 美团外卖数据特点
美团采用AJAX动态加载技术,商家列表和详情页数据通过接口分批获取。例如:
- 商家列表接口:
https://meishi.meituan.com/api/v1/poi/list - 返回格式:JSON结构包含商家ID、名称、评分、月销量等字段
三、实战步骤:从环境搭建到数据落地
1. 环境准备
pip install requests beautifulsoup4 selenium pandas fake_useragent
安装ChromeDriver(需与浏览器版本匹配),配置环境变量。
2. 基础爬虫实现(静态页面解析)
import requests
from bs4 import BeautifulSoup
import pandas as pd
def get_static_data(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, 'html.parser')
shops = []
for item in soup.find_all('div', class_='shop-item'):
name = item.find('h3').text.strip()
rating = item.find('span', class_='rating').text
sales = item.find('span', class_='sales').text.split('月售')[1].strip()
shops.append({'name': name, 'rating': rating, 'sales': sales})
return pd.DataFrame(shops)
# 示例调用
df = get_static_data('https://www.meituan.com/meishi/')
df.to_csv('meituan_static.csv', index=False)
问题:此方法仅能获取首屏数据,后续内容需滚动加载。
3. 动态数据采集(Selenium方案)
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
import time
def get_dynamic_data(url):
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 无头模式
driver = webdriver.Chrome(options=options)
driver.get(url)
# 模拟滚动加载
for _ in range(5):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)
# 解析动态加载的元素
shops = []
items = driver.find_elements(By.CSS_SELECTOR, '.shop-item')
for item in items:
name = item.find_element(By.CSS_SELECTOR, 'h3').text
rating = item.find_element(By.CSS_SELECTOR, '.rating').text
sales = item.find_element(By.CSS_SELECTOR, '.sales').text.split('月售')[1]
shops.append({'name': name, 'rating': rating, 'sales': sales})
driver.quit()
return pd.DataFrame(shops)
# 示例调用
df = get_dynamic_data('https://www.meituan.com/meishi/')
df.to_csv('meituan_dynamic.csv', index=False)
优化点:
- 添加
time.sleep()模拟人类操作节奏 - 使用CSS选择器替代XPath提升解析效率
4. 终极方案:直接调用API接口
通过Chrome开发者工具(F12)的Network面板,捕获商家列表请求:
import requests
import json
def get_api_data(city_id, offset=0):
url = f"https://meishi.meituan.com/api/v1/poi/list?cityId={city_id}&offset={offset}&limit=20"
headers = {
'Referer': 'https://www.meituan.com/meishi/',
'User-Agent': 'Mozilla/5.0...'
}
response = requests.get(url, headers=headers)
data = json.loads(response.text)
shops = []
for poi in data.get('data', []):
shops.append({
'name': poi['title'],
'rating': poi['avgScore'],
'sales': poi['recentSalesNum'],
'address': poi['address']
})
return shops
# 示例:获取北京前40家商家数据
all_shops = []
for i in range(2): # 每页20条,获取2页
all_shops.extend(get_api_data(city_id=1, offset=i*20))
pd.DataFrame(all_shops).to_csv('meituan_api.csv', index=False)
优势:
- 直接获取结构化JSON数据
- 无需处理HTML解析
- 效率比页面渲染高10倍以上
四、反爬策略与应对方案
1. 常见反爬机制
- IP限制:单IP请求频率过高触发封禁
- User-Agent检测:识别非浏览器请求
- 验证码:图形/滑动验证码拦截
- 行为分析:检测鼠标轨迹、点击间隔等
2. 应对策略
(1)IP代理池
import random
from fake_useragent import UserAgent
proxies = [
{'http': 'http://123.123.123.123:8080'},
{'http': 'http://124.124.124.124:8081'}
]
def get_with_proxy(url):
proxy = random.choice(proxies)
headers = {'User-Agent': UserAgent().random}
try:
return requests.get(url, headers=headers, proxies=proxy, timeout=5)
except:
return get_with_proxy(url) # 失败自动重试
(2)请求头伪装
headers = {
'Accept': 'application/json',
'Referer': 'https://www.meituan.com/',
'X-Requested-With': 'XMLHttpRequest',
'Cookie': 'your_cookie_here' # 必要时携带合法Cookie
}
(3)请求频率控制
import time
import random
def request_with_delay(url):
delay = random.uniform(1, 3) # 1-3秒随机延迟
time.sleep(delay)
return get_with_proxy(url)
五、数据存储与可视化
1. 存储方案
# 存储为CSV
df.to_csv('meituan_data.csv', index=False, encoding='utf_8_sig')
# 存储到MySQL
import pymysql
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://user:password@localhost/meituan')
df.to_sql('shops', engine, if_exists='replace', index=False)
2. 数据可视化
import matplotlib.pyplot as plt
# 评分分布饼图
ratings = df['rating'].value_counts()
plt.pie(ratings, labels=ratings.index, autopct='%1.1f%%')
plt.title('商家评分分布')
plt.show()
# 销量TOP10柱状图
top10 = df.nlargest(10, 'sales')
plt.barh(top10['name'], top10['sales'])
plt.xlabel('月销量')
plt.title('销量TOP10商家')
plt.show()
六、常见问题Q&A
Q1:被网站封IP怎么办?
A:立即启用备用代理池,建议使用住宅代理(如站大爷IP代理),配合每请求更换IP策略。若使用Selenium,可结合selenium-wire库自动轮换代理。
Q2:如何获取特定区域的商家数据?
A:通过API接口的cityId参数指定城市,或解析页面URL中的区域标识(如/meishi/bj/代表北京)。
Q3:数据缺失或格式错误如何处理?
A:在解析阶段添加异常处理:
try:
rating = float(item['avgScore'])
except (KeyError, ValueError):
rating = 0.0
Q4:如何避免被法律风险?
A:严格遵守《网络安全法》,仅爬取公开数据,避免高频请求(建议延迟≥3秒),不存储敏感信息。商业用途前建议咨询法律顾问。
Q5:Selenium爬取时出现元素未加载怎么办?
A:使用显式等待替代固定延迟:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '.shop-item'))
)
七、总结与延伸
本文通过三种技术方案(静态解析、Selenium模拟、API直连)实现了美团外卖数据采集,核心要点包括:
- 优先尝试API接口获取结构化数据
- 动态页面需结合代理池与请求延迟
- 数据存储建议采用CSV+MySQL双方案
- 可视化阶段重点关注评分分布与销量排名
进阶方向:
- 使用Scrapy框架构建分布式爬虫
- 结合NLP分析用户评论情感倾向
- 搭建实时数据监控看板(如Grafana)
数据采集的本质是信息获取效率的竞赛,但始终需牢记:技术应服务于正当需求,合规性比技术实现更重要。