影刀RPA实操指南:酒店OTA多平台房态价格同步与预订自动化
一、痛点切入:酒店OTA运营的"切屏地狱"
开过酒店的人才知道,OTA运营是门"苦力活"。
一家中等规模(100-150间房)的酒店,通常同时入驻了携程、美团、飞猪、去哪儿、同程、艺龙等6-8个平台。每天运营人员的工作循环是:

- 早上8:00 — 打开携程后台,查看昨晚的预订情况,确认今天剩余可售房数
- 打开美团后台 — 重复以上操作,手动修改剩余房数
- 打开飞猪后台 — 重复以上操作
- 打开去哪儿后台 — 重复以上操作
- ……循环,直到所有平台的房态和价格同步完毕
然后是价格调整:
- 淡季要降价促销 → 每个平台逐条修改促销价格
- 旺季要上调价格 → 每个平台逐房型调整
- 节假日特殊定价 → 设置加价规则
- 竞争酒店降价 → 跟进调整
还有预订确认——虽然系统可以自动确认,但超时未确认的订单会降低酒店排名,所以运营人员需要盯着"待确认"列表,手动加速处理。
整个流程下来,一个OTA运营人员每天花在"切屏同步"上的时间约4-6小时。而且手动操作极易出错——房态少改了1间,旺季可能导致超售引发客诉;价格小数点标错了,直接损失收入。
二、核心方法:四步构建酒店自动化运营系统

【房态同步】→ 【价格同步】→ 【预订确认】→ 【入住提醒】
↓ ↓ ↓ ↓
多平台库存 多平台价格 自动确认/ 短信/APP
[video(video-ZC1J9f8T-1785343212456)(type-csdn)(url-https://live.csdn.net/v/embed/525010)(image-https://v-blog.csdnimg.cn/asset/f4faa587144cb7070f19e8b36813806b/cover/Cover0.jpg)(title-店群矩阵自动化突破运营极限!)]
统一管理 统一调整 超时告警 自动推送
第一步:多平台房态统一管理
房态同步的核心思路:以PMS(酒店管理系统)为唯一真实数据源,影刀RPA从PMS读取实时房态 → 自动同步到各OTA平台。
# -*- coding: utf-8 -*-
"""
酒店行业 - OTA多平台房态价格同步与预订自动化
作者:林焱
"""
import pandas as pd
import time
import logging
from datetime import datetime, timedelta
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
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# ========== 1. 房态数据源(PMS系统) ==========
class PMSDataReader:
"""酒店PMS系统数据读取器"""
def __init__(self, pms_type='西软'):
"""
支持的PMS系统: 西软、中软、绿云、石基等
不同PMS的导出格式不同,这里以西软为例
"""
self.pms_type = pms_type
def read_room_status(self, date=None):
"""
从PMS读取指定日期的房态数据
返回 DataFrame:
房型 | 总房数 | 已售 | 维修 | 锁定 | 可用 | 门市价
"""
if date is None:
date = datetime.now().date()
try:
# 从PMS导出的房态报表中读取
# 实际场景通过影刀操作PMS的"导出"功能自动生成Excel
file_path = f'D:/酒店数据/PMS/房态报表_{date.strftime("%Y%m%d")}.xlsx'
df = pd.read_excel(file_path, sheet_name='房态')
# 标准化列名
column_mapping = {
'房型': 'room_type',
'总房数': 'total_rooms',
'已售房数': 'sold_rooms',
'维修房数': 'maintenance_rooms',
'可用房数': 'available_rooms',
'门市价': 'rack_rate',
}
df = df.rename(columns={k: v for k, v in column_mapping.items()
if k in df.columns})
# 计算可用房数(如果PMS没有直接提供)
if 'available_rooms' not in df.columns:
df['available_rooms'] = (
df['total_rooms'] - df['sold_rooms'] - df['maintenance_rooms']
)
logging.info(f"读取PMS房态: {date} — {len(df)} 个房型")
return df
except FileNotFoundError:
logging.error(f"PMS房态文件不存在: {file_path}")
return pd.DataFrame()
except Exception as e:
logging.error(f"读取PMS房态失败: {e}")
return pd.DataFrame()
# ========== 2. OTA平台房态同步 ==========
class OTARoomSync:
"""OTA平台房态同步器"""
# 各平台配置
PLATFORMS = {
'携程': {
'login_url': 'https://hotel.ctrip.com/ebk/',
'room_mgmt_url': 'https://hotel.ctrip.com/ebk/room/manage',
'field_selectors': {
'房型选择': 'select[name="room_type"]',
'可售房数': 'input[name="available_count"]',
'价格': 'input[name="sell_price"]',
'保存按钮': 'button.save-btn',
}
},
'美团': {
'login_url': 'https://e.meituan.com/hotel/',
'room_mgmt_url': 'https://e.meituan.com/hotel/room/inventory',
'field_selectors': {
'房型选择': '.room-type-select',
'可售房数': '.available-input input',
'价格': '.price-input input',
'保存按钮': '.save-button',
}
},
'飞猪': {
'login_url': 'https://hotel.fliggy.com/ebk/',
'room_mgmt_url': 'https://hotel.fliggy.com/ebk/room/stock',
'field_selectors': {
'房型选择': '#roomType',
'可售房数': '#availableRooms',
'价格': '#sellPrice',
'保存按钮': '#saveBtn',
}
},
}
def __init__(self):
self.drivers = {} # 每个平台一个浏览器实例
def sync_rooms(self, pms_data, platforms=None, dates=None):
"""
将PMS房态同步到各OTA平台
pms_data: PMS读取的房态数据
platforms: 要同步的平台列表(默认全部)
dates: 要同步的日期列表(默认未来30天)
"""
if platforms is None:
platforms = list(self.PLATFORMS.keys())
if dates is None:
dates = [datetime.now().date() + timedelta(days=i) for i in range(30)]
results = []
for platform in platforms:
logging.info(f"开始同步 {platform} 房态...")
config = self.PLATFORMS[platform]
try:
# 打开浏览器并登录
driver = webdriver.Chrome()
driver.get(config['login_url'])
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.TAG_NAME, 'form'))
)
# 登录操作(需要使用酒店的账号密码)
self._login_platform(driver, platform)
# 同步每个日期的房态
for date in dates:
date_str = date.strftime('%Y-%m-%d')
# 导航到房态管理页面
driver.get(f"{config['room_mgmt_url']}?date={date_str}")
time.sleep(2)
# 遍历每个房型
for _, row in pms_data.iterrows():
room_type = row['room_type']
available = int(row['available_rooms'])
price = float(row['rack_rate'])
try:
# 选择房型
room_select = driver.find_element(
By.CSS_SELECTOR, config['field_selectors']['房型选择'])
room_select.click()
time.sleep(0.5)
room_option = driver.find_element(
By.XPATH, f'//option[contains(text(), "{room_type}")]')
room_option.click()
# 填写可售房数
available_input = driver.find_element(
By.CSS_SELECTOR, config['field_selectors']['可售房数'])
available_input.clear()
available_input.send_keys(str(available))
# 填写价格
price_input = driver.find_element(
By.CSS_SELECTOR, config['field_selectors']['价格'])
price_input.clear()
price_input.send_keys(str(price))
# 保存
save_btn = driver.find_element(
By.CSS_SELECTOR, config['field_selectors']['保存按钮'])
save_btn.click()
time.sleep(1)
results.append({
'平台': platform,
'日期': date_str,
'房型': room_type,
'可售': available,
'价格': price,
'状态': '成功',
'同步时间': datetime.now().strftime('%H:%M:%S')
})
logging.info(f" {date_str} {room_type}: 可售{available} 价格{price}")
except Exception as e:
results.append({
'平台': platform,
'日期': date_str,
'房型': room_type,
'状态': f'失败: {str(e)[:50]}'
})
logging.warning(f" {room_type} 同步失败: {e}")
continue
driver.quit()
except Exception as e:
logging.error(f"{platform} 同步异常: {e}")
continue
# 保存同步日志
df = pd.DataFrame(results)
log_path = f'D:/酒店数据/同步日志_{datetime.now().strftime("%Y%m%d%H%M")}.xlsx'
df.to_excel(log_path, index=False)
success_count = len(df[df['状态'] == '成功'])
logging.info(f"房态同步完成: {success_count}/{len(df)} 成功")
return df
def _login_platform(self, driver, platform):
"""登录特定平台"""
# 实际使用酒店的账号密码
# 这里展示登录逻辑框架
try:
if platform == '携程':
driver.find_element(By.ID, 'username').send_keys('hotel_account')
driver.find_element(By.ID, 'password').send_keys('hotel_password')
driver.find_element(By.ID, 'loginBtn').click()
elif platform == '美团':
# 美团通常扫码登录
# 影刀可以通过Cookie保持登录态
pass
elif platform == '飞猪':
pass
time.sleep(3)
logging.info(f"{platform} 登录成功")
except Exception as e:
logging.error(f"{platform} 登录失败: {e}")
raise
# ========== 3. 智能价格策略 ==========
class SmartPricingEngine:
"""
智能定价引擎
根据入住率、竞品价格、节假日等因素自动调整价格
"""
def calculate_optimal_price(self, room_type, date, occupancy_rate,
competitor_prices, is_holiday=False):
"""
计算最优价格
策略:
1. 基础价 = 门市价 * 基础折扣(平日0.85,周末0.9)
2. 入住率调整:
- <30%: 降价10%抢流量
- 30-60%: 维持基础价
- 60-80%: 涨价5%
- >80%: 涨价15%
3. 竞品调整:不高于竞品均价10%
4. 节假日加价:假日系数1.3-2.0
"""
base_price = self._get_base_price(room_type, date)
# 周末判断
is_weekend = date.weekday() >= 5
weekend_factor = 1.15 if is_weekend else 1.0
# 入住率系数
if occupancy_rate < 0.3:
occupancy_factor = 0.9 # 降价
elif occupancy_rate < 0.6:
occupancy_factor = 1.0
elif occupancy_rate < 0.8:
occupancy_factor = 1.08
else:
occupancy_factor = 1.2 # 涨价
# 节假日系数
holiday_factor = 1.5 if is_holiday else 1.0
# 竞品参考封顶
if competitor_prices:
avg_competitor = sum(competitor_prices) / len(competitor_prices)
competitor_cap = avg_competitor * 1.10 # 不高于竞品均价10%
else:
competitor_cap = float('inf')
# 计算价格
optimal = base_price * weekend_factor * occupancy_factor * holiday_factor
optimal = round(optimal / 10) * 10 # 取整到10元
optimal = min(optimal, competitor_cap) # 不超过竞品上限
return optimal
def collect_competitor_prices(self, competitor_hotels, date):
"""
采集竞品酒店价格
从各OTA平台采集指定酒店+日期的价格
competitor_hotels: [{'name': 'XX酒店', 'ctrip_id': '123'}, ...]
"""
prices = []
for hotel in competitor_hotels:
try:
# 在携程/美团/飞猪搜索该酒店并提取最低价
# 这里使用简化的模拟数据
# 实际场景:浏览器自动化搜索酒店 → 提取价格
price = self._scrape_hotel_price(hotel, date)
if price:
prices.append(price)
except:
continue
return prices
def _get_base_price(self, room_type, date):
"""获取基础门市价"""
# 从PMS读取
df = pd.read_excel('D:/酒店数据/PMS/门市价表.xlsx')
match = df[(df['房型'] == room_type)]
if not match.empty:
return float(match.iloc[0]['门市价'])
return 300 # 默认值
def _scrape_hotel_price(self, hotel, date):
"""爬取指定酒店在指定日期的价格"""
# 实际实现:浏览器自动化
return None # 简化处理
# ========== 4. 预订自动确认与入住提醒 ==========
class BookingManager:
"""预订管理器"""
def auto_confirm_bookings(self, platforms=None):
"""
自动确认预订订单
规则:
- 有库存 → 自动确认
- 库存紧张 → 人工确认
- 无库存 → 自动拒绝
"""
if platforms is None:
platforms = ['携程', '美团', '飞猪']
confirmed_count = 0
for platform in platforms:
# 获取待确认订单列表
pending_orders = self._get_pending_orders(platform)
for order in pending_orders:
# 检查库存
available = self._check_room_availability(
order['房型'], order['入住日期'], order['离店日期'])
if available >= order['预订间数']:
# 库存充足,自动确认
self._confirm_order(platform, order['订单号'])
confirmed_count += 1
logging.info(f"自动确认: {platform} {order['订单号']} "
f"{order['房型']}x{order['预订间数']}")
elif available <= 0:
# 无库存,标记异常
self._flag_order_anomaly(platform, order['订单号'],
'库存不足')
else:
# 库存紧张,保留人工确认
logging.warning(f"库存紧张: {platform} {order['订单号']} "
f"需要{order['预订间数']}间, 仅剩{available}间")
return confirmed_count
def send_checkin_reminder(self, days_before=1):
"""
发送入住提醒
在入住前N天自动发送提醒短信/APP通知
提醒内容:
- 入住日期
- 酒店地址和联系方式
- 入住须知(带身份证、押金等)
"""
# 查询明天入住的订单
tomorrow = (datetime.now() + timedelta(days=days_before)).strftime('%Y-%m-%d')
# 获取订单信息
checkin_orders = self._get_checkin_orders(tomorrow)
reminders_sent = 0
for order in checkin_orders:
try:
# 构建提醒内容
message = self._build_reminder_message(order)
# 发送短信/APP通知
self._send_notification(order['手机号'], message)
reminders_sent += 1
except Exception as e:
logging.error(f"发送提醒失败: {order['订单号']} - {e}")
logging.info(f"入住提醒: 发送{reminders_sent}条")
return reminders_sent
def _get_pending_orders(self, platform):
"""获取平台待确认订单"""
# 实际从平台API或后台获取
return []
def _check_room_availability(self, room_type, checkin, checkout):
"""检查房型在指定日期段的可用房数"""
# 从PMS查询
return 10
def _confirm_order(self, platform, order_id):
"""确认订单"""
pass
def _flag_order_anomaly(self, platform, order_id, reason):
"""标记异常订单"""
pass
def _build_reminder_message(self, order):
"""构建入住提醒消息"""
return (
f"【入住提醒】尊敬的{order['姓名']},您预订的"
f"{order['酒店名']}({order['房型']})将于{order['入住日期']}入住。"
f"酒店地址:{order['地址']},电话:{order['电话']}。"
f"请携带身份证办理入住。祝您入住愉快!"
)
# ========== 5. 运营日报自动生成 ==========
def generate_daily_report(pms_data, sync_log, bookings):
"""
自动生成每日运营报表
报表内容:
- 当日入住率
- 各渠道预订量占比
- 平均房价(ADR)
- 每间可售房收入(RevPAR)
- 房态同步成功率
- 异常订单列表
"""
today = datetime.now().strftime('%Y-%m-%d')
# 计算关键指标
total_rooms = pms_data['total_rooms'].sum()
sold_rooms = pms_data['sold_rooms'].sum()
occupancy_rate = (sold_rooms / total_rooms * 100) if total_rooms > 0 else 0
# 平均房价
if bookings:
total_revenue = sum(b['金额'] for b in bookings)
adr = total_revenue / len(bookings) if bookings else 0
else:
adr = 0
# RevPAR
revpar = (adr * occupancy_rate / 100) if adr else 0
# 渠道占比
channel_stats = {}
if bookings:
for b in bookings:
channel = b.get('渠道', '未知')
channel_stats[channel] = channel_stats.get(channel, 0) + 1
# 同步成功率
sync_success = len(sync_log[sync_log['状态'] == '成功']) if not sync_log.empty else 0
sync_total = len(sync_log) if not sync_log.empty else 0
# 生成报告
report = f"""
========================================
酒店运营日报 - {today}
========================================
【核心指标】
入住率: {occupancy_rate:.1f}%
平均房价: ¥{adr:.0f}
RevPAR: ¥{revpar:.0f}
已售客房: {sold_rooms} 间
可售客房: {total_rooms} 间
【房态同步】
同步成功: {sync_success}/{sync_total}
【渠道分析】
"""
for channel, count in sorted(channel_stats.items(), key=lambda x: x[1], reverse=True):
pct = count / len(bookings) * 100 if bookings else 0
report += f" {channel}: {count}单 ({pct:.1f}%)\n"
report += """
========================================
自动生成 | 影刀RPA
========================================
"""
# 保存报告
report_path = f'D:/酒店数据/日报/运营日报_{today}.txt'
with open(report_path, 'w', encoding='utf-8') as f:
f.write(report)
logging.info(f"运营日报已生成: {report_path}")
print(report)
return report
三、常见问题速查(FAQ)

Q1: 房态同步后OTA平台显示的可售数不对?
现象:PMS显示大床房还剩5间,携程后台同步完显示3间。
原因:(1)平台有自己的预留/锁房逻辑,不是PMS写多少就是多少;(2)各平台的关房时间不同——有的提前24小时,有的当天关。
解决方式:(1)在同步时设置"保留房"——PMS可用15间,给携程分配8间、美团分配5间、飞猪分配2间,通过保留房机制避免超售;(2)每个平台设置不同的关房规则,在PMS中标注各平台的库存分配。
Q2: 多平台同时预订导致超售怎么办?

现象:携程和美团几乎同时接到同一个房型的预订,导致库存变负。
原因:RPA同步有延迟——在你读取PMS到写入OTA的几秒内,另一个平台可能已经产生了订单。
解决方式:(1)缩短同步频率——从每天1次改为每30分钟1次,减少时间窗口;(2)启用PMS的"直连"功能——如果PMS支持与OTA直连,通过接口同步比网页同步延迟小得多;(3)设置保留房——给每个平台的分配数始终小于实际可售数。
Q3: 美团/飞猪的登录需要扫码怎么办?
现象:美团酒店后台登录需要打开APP扫码,自动化流程被中断。

原因:美团为保障安全,网页版登录强制手机扫码。
解决方式:(1)首次登录扫码一次后,使用影刀的"Cookie保持"功能保存登录状态,后续运行直接加载Cookie,可以保持5-7天免登录;(2)在Cookie过期前一天的自动流程中加入"告警通知",提醒运营人员手动扫码续期;(3)使用影刀的"手动接管"模式——当检测到需要扫码时暂停流程并弹窗提醒。
Q4: 节假日价格策略不精准?
现象:节假日按固定系数1.5倍调价,结果发现隔壁酒店涨了3倍还满房了。
原因:固定系数无法反映真实供需——热门节假日(春节、国庆)和普通节假日(端午、中秋)的价值完全不同。

解决方式:(1)建立历史数据模型——分析过去3年各节假日的实际出租率和ADR,用历史数据推算合理价格区间;(2)采集周边竞品酒店节假日价格,作为调价参考;(3)分阶段调价——提前30天先挂高价,入住前7天根据预订率动态调整。
Q5: 同一个OTA平台有多个酒店账号怎么管理?
现象:一个运营人员管理3家酒店的携程账号。
temu店群自动化报活动案例
原因:连锁酒店或酒店管理公司通常管理多家门店。

解决方式:(1)为每家酒店创建独立的影刀流程文件,避免账号混用;(2)在Excel管理表中维护"门店-平台-账号-Cookie文件"的映射关系;(3)各酒店的同步流程串行或分时段运行,避免IP风险。
Q6: 平台页面改版后流程大面积失效?
现象:携程后台改版了,之前的所有元素选择器都找不到了。
原因:OTA平台的后台系统会不定期迭代更新。
解决方式:(1)使用影刀的"图像识别"而非"元素选择器"作为备用方案——图像识别的鲁棒性更高,但速度稍慢;(2)关注各平台的"商家公告"栏目,提前预知即将上线的改版;(3)每周运行一次自动化测试流程,用测试数据在各平台走一遍完整流程,提前发现问题。

四、推荐资源
- 携程/美团/飞猪酒店商家帮助中心:各平台的后台操作手册和API文档
- 酒店PMS系统接口文档:西软、中软、绿云等主流PMS的数据导出接口
- 影刀RPA"图像识别"教程:应对页面改版的备用方案
- 收益管理(Revenue Management)入门:理解定价策略的理论基础
- 携程ebooking API文档:如果酒店有技术能力,直连接口比网页同步更稳定
五、内容标签
酒店行业 RPA 影刀 OTA运营 房态管理 价格同步 预订自动化 收益管理 Python 酒店PMS
作者:林焱
影刀RPA深度使用者,两年以上实操经验,专注RPA在各行业的落地应用



224

被折叠的 条评论
为什么被折叠?



