Skip to content

景区客源地分析怎么做?用 IP 归属地统计游客来自哪个省(Python 实现) ​

文旅局的同事上周吐槽:下一年度的营销预算又要报了,但「外地游客到底从哪来」这个问题,局里靠的是抽问卷——现场拦游客填表,回收率不到三成,样本偏得离谱,报告写出来自己都不信。其实答案一直在自己手里:景区购票系统、官网、官方小程序,每一次登录和下单都会留下 IP。把 IP 归属地一聚合,「游客来自哪个省、哪个市、增长最快的是哪里」就全出来了,成本约等于零。

客源地分析(也叫游客来源地分析)是文旅营销的标配动作:钱往哪个省投、淡季靠哪个区域补量、高铁新线开通后客源结构怎么变,全看这张表。这篇文章用 Python 写一套轻量统计流程:从访问日志里抽 IP 批量查归属地,pandas 聚合成客源结构表,输出可直接贴进汇报材料。

一、客源地数据能解决什么问题 ​

景区/文旅项目的客源结构,直接决定投放策略:

  • 投放归因:抖音、小红书、携程的投放都是按区域定向的,不知道客源结构,预算只能全国撒网。知道「华东占 40%、且江苏涨得最快」,定向包就敢往华东加码;
  • 渠道核验:OTA 平台报的「区域脱敏数据」不透明,自建客源统计可以交叉验证渠道数据的真实性;
  • 淡旺季结构:北方景区冬天靠南方客人补量,客源月度变化能提前指导降价/大促节奏;
  • 新线路评估:高铁、新航线开通后,客源省份权重变化就是最直接的反馈指标。

数据源三种:官网访问日志(Nginx access log 带 IP)、官方小程序/公众号埋点、购票系统下单记录。前两种覆盖面最大,够做趋势分析;购票记录有真实交易地址可作交叉验证。

二、方案设计 ​

  1. 从日志里抽出去重后的 IP 列表(访问日志里同一 IP 出现几十次,必须先去重再查,省额度);
  2. 逐 IP 调 https://ip9.com.cn/get?ip=xxx 查归属地(省份 + 城市),带缓存和限速;
  3. pandas 按省份/城市聚合,统计游客访问量占比;
  4. 输出三张表:省份 Top10、城市 Top10、近 30 天各省趋势(可按天分组);
  5. 可视化可把结果 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