Appearance
景区客源地分析怎么做?用 IP 归属地统计游客来自哪个省(Python 实现)
文旅局的同事上周吐槽:下一年度的营销预算又要报了,但「外地游客到底从哪来」这个问题,局里靠的是抽问卷——现场拦游客填表,回收率不到三成,样本偏得离谱,报告写出来自己都不信。其实答案一直在自己手里:景区购票系统、官网、官方小程序,每一次登录和下单都会留下 IP。把 IP 归属地一聚合,「游客来自哪个省、哪个市、增长最快的是哪里」就全出来了,成本约等于零。
客源地分析(也叫游客来源地分析)是文旅营销的标配动作:钱往哪个省投、淡季靠哪个区域补量、高铁新线开通后客源结构怎么变,全看这张表。这篇文章用 Python 写一套轻量统计流程:从访问日志里抽 IP 批量查归属地,pandas 聚合成客源结构表,输出可直接贴进汇报材料。
一、客源地数据能解决什么问题
景区/文旅项目的客源结构,直接决定投放策略:
- 投放归因:抖音、小红书、携程的投放都是按区域定向的,不知道客源结构,预算只能全国撒网。知道「华东占 40%、且江苏涨得最快」,定向包就敢往华东加码;
- 渠道核验:OTA 平台报的「区域脱敏数据」不透明,自建客源统计可以交叉验证渠道数据的真实性;
- 淡旺季结构:北方景区冬天靠南方客人补量,客源月度变化能提前指导降价/大促节奏;
- 新线路评估:高铁、新航线开通后,客源省份权重变化就是最直接的反馈指标。
数据源三种:官网访问日志(Nginx access log 带 IP)、官方小程序/公众号埋点、购票系统下单记录。前两种覆盖面最大,够做趋势分析;购票记录有真实交易地址可作交叉验证。
二、方案设计
- 从日志里抽出去重后的 IP 列表(访问日志里同一 IP 出现几十次,必须先去重再查,省额度);
- 逐 IP 调
https://ip9.com.cn/get?ip=xxx查归属地(省份 + 城市),带缓存和限速; - pandas 按省份/城市聚合,统计游客访问量占比;
- 输出三张表:省份 Top10、城市 Top10、近 30 天各省趋势(可按天分组);
- 可视化可把结果 CSV 拖进 ECharts 中国地图或 PowerBI,本文只写到表格输出。
精度说明:IP 归属地是城市级,对「游客来自哪个省/市」这种客源结构统计完全够用;它代表的是「网络出口所在地」,用 4G 漫游、公司专线的游客会有一小部分偏差,看结构和趋势不受影响。
三、Python 实现
python
import csv
import glob
import time
import requests
import pandas as pd
CACHE = {}
LAST = 0.0
def query_geo(ip):
"""查归属地,带缓存 + 限速(免费版 60 次/分钟/IP)"""
global LAST
if ip in CACHE:
return CACHE[ip]
now = time.time()
if now - LAST < 1.1:
time.sleep(1.1 - (now - LAST))
try:
r = requests.get(f"https://ip9.com.cn/get?ip={ip}", timeout=5)
d = r.json()
LAST = time.time()
if d.get("ret") != 200:
return None
CACHE[ip] = d["data"]
return d["data"]
except Exception:
return None
def extract_ips(log_path):
"""从 Nginx access log 抽 IP,去过重"""
ips = set()
with open(log_path, encoding="utf-8", errors="ignore") as f:
for line in f:
parts = line.split()
if parts and parts[0].count(".") == 3:
ips.add(parts[0]) # 第一列通常是客户端 IP
return list(ips)
def main():
# 1. 收集所有日志里的 IP 并批量查归属地
rows = []
for log in glob.glob("access*.log"):
for ip in extract_ips(log):
geo = query_geo(ip)
if geo:
rows.append({
"ip": ip,
"prov": geo.get("prov", ""),
"city": geo.get("city", ""),
"isp": geo.get("isp", ""),
})
df = pd.DataFrame(rows)
if df.empty:
print("没有解析到有效 IP,检查日志路径")
return
# 2. 客源省份分布
prov_stat = df.groupby("prov").size().sort_values(ascending=False)
print("=== 客源省份 Top10 ===")
total = len(df)
for prov, cnt in prov_stat.head(10).items():
print(f"{prov:6s} {cnt:6d} 次 {cnt / total * 100:5.1f}%")
# 3. 客源城市分布
city_stat = df.groupby(["prov", "city"]).size().sort_values(ascending=False)
print("\n=== 客源城市 Top10 ===")
for (prov, city), cnt in city_stat.head(10).items():
print(f"{prov}-{city:8s} {cnt:5d} 次")
# 4. 落盘,供地图/BI 工具使用
prov_stat.rename("count").reset_index().to_csv("keyuandi_prov.csv",
index=False, encoding="utf-8-sig")
print("\n已输出 keyuandi_prov.csv(省份客源表)")
if __name__ == "__main__":
main()日志格式是 Nginx 默认格式就能跑:每行第一列是客户端 IP。跑之前先看一眼日志里有多少个去重 IP——比如 2000 个 IP,按免费版 60 次/分钟算,脚本会跑半个多小时,建议第一次只喂近 7 天的日志;之后每天增量跑,新增 IP 往往只有几百个,几分钟完事。
四、落地注意事项
- 先按「去重 IP 数 ÷ 60」估算耗时:免费版 60 次/分钟/IP,日志大就先抽样或分区跑,别让任务挂一整天;
- 数据口径要统一:访问日志含爬虫和监控探针,会污染客源结构。建议先按 UA 过滤掉蜘蛛(Googlebot、Bingbot 等),再统计真实访客;想更准可以只统计「访问首页以外页面 ≥2 次」的 IP;
- 趋势比绝对值重要:单看某省占比容易误判,拉 30 天趋势,看「哪个月哪个省突然涨」,那才是投放见效的信号;
- 辅助决策,不替代调研:客源地统计回答「人从哪来」,回答不了「为什么来」,游客画像、偏好还是得靠问卷和评论分析补;
- 合规:只做聚合级客源统计,不留单 IP 与个人行为的关联明细,符合最小化原则。
总结
客源地分析不需要买第三方数据服务,景区自己的日志就是金矿。把 IP 抽出来、查归属地、按省聚合,三步就能得到投放决策最需要的客源结构表。脚本直接套用:日志放同目录,python keyuandi.py 跑完出 CSV。查归属地用免费版 https://ip9.com.cn/get?ip=xxx(60 次/分钟,批量记得去重 + 缓存 + 限速),访问量分布在内部的系统,免费额度基本够用。官网:https://www.ip9.com.cn