Skip to content

舆情监测怎么做地域分布?Python 按 IP 归属地统计热点来源地 ​

某消费品牌出了负面舆情,公关总监十分钟后就要知道「这波声量主要从哪来」——是广东的维权群在发酵,还是北方某个城市的记者带了节奏。市面上的舆情 SaaS 一签就是几万一年,还要对接数据源。对于已经有内容库的中型公司,这事用 Python 和一张 IP 归属地表就能搭起来,花不了半天。

这里说的舆情地域分布,跟网站访问日志统计是两回事:日志统计回答「谁访问了我的站」,舆情分析回答「讨论声量集中在哪」。数据源是平台自己的舆情库——帖子、评论、转发的记录,每条带着发布者 IP 和互动数据。把 IP 换成省份,再按互动量加权聚合,就是一张地域声量榜。

一、为什么 IP 归属地能撑起舆情地域分析 ​

一条舆情条目的地域属性,来自发布者当时的公网出口 IP。三个层次:

  • 单条归属:评论「这牌子翻车了」的人在北京,这条就计给北京;
  • 聚合声量:一个省的所有条目按互动数(点赞/评论/转发)加权求和,得到「地域热度」;
  • 趋势对比:同一事件不同阶段跑一遍,看声量重心有没有从 A 省迁移到 B 省——这对判断「是区域性事件还是全国性事件」极有用。

加权是有讲究的:一条被转了两万次的原帖,分量应该重过一百条没人理的评论。权重按内容类型设就行,原帖 1.0、评论 0.3、转发 0.5 这类初始值,跑两轮数据再调。

二、方案设计 ​

  1. 舆情库导出条目 JSON:IP、内容类型、互动数;
  2. 逐条查归属地,命中缓存直接取,未命中才调接口(免费版 60 次/分钟,批量必须循环 + 限速 + 缓存);
  3. 按省份累加「互动数 × 类型权重」;
  4. 排序输出 Top 榜,可选导出 CSV 给可视化。

三、Python 实现 ​

python
import json
import time
import requests
from collections import defaultdict

# IP9 免费版 60 次/分钟/IP,缓存 + 限速是批量查询的标配
CACHE = {}
LAST_CALL = 0.0

def query_geo(ip):
    global LAST_CALL
    if ip in CACHE:
        return CACHE[ip]
    try:
        now = time.time()
        if now - LAST_CALL < 1.1:
            time.sleep(1.1 - (now - LAST_CALL))
        r = requests.get(f"https://ip9.com.cn/get?ip={ip}", timeout=5)
        d = r.json()
        LAST_CALL = time.time()
        if d.get("ret") != 200:
            return None            # 非法 IP / 限速,跳过该条
        CACHE[ip] = d["data"]
        return d["data"]
    except Exception:
        return None

# 内容类型权重:原帖最重,评论/转发递减。按实际传播形态调。
WEIGHT = {"post": 1.0, "comment": 0.3, "repost": 0.5}

def region_heat(items):
    prov_hot = defaultdict(float)
    skipped = 0
    for it in items:
        geo = query_geo(it["ip"])
        if not geo:
            skipped += 1
            continue
        prov = geo.get("prov") or "未知"
        hot = it.get("hot", 1) * WEIGHT.get(it.get("kind", "post"), 1.0)
        prov_hot[prov] += hot
    return prov_hot, skipped

def main():
    with open("yq_items.json", encoding="utf-8") as f:
        items = json.load(f)  # [{"ip":"1.2.3.4","kind":"post","hot":120}, ...]
    hot, skipped = region_heat(items)
    print(f"共 {len(items)} 条,归属地查询失败跳过 {skipped} 条\n")
    print("地域声量 Top 榜:")
    for i, (prov, h) in enumerate(
            sorted(hot.items(), key=lambda x: -x[1])[:15], 1):
        print(f"{i:2d}. {prov:<6s} {h:8.1f}")
    # 导出 CSV,喂给图表工具做舆情地图
    with open("region_heat.csv", "w", encoding="utf-8-sig") as f:
        f.write("省份,热度\n")
        for prov, h in sorted(hot.items(), key=lambda x: -x[1]):
            f.write(f"{prov},{h:.1f}\n")

if __name__ == "__main__":
    main()

这段原型的关键在 query_geo:缓存命中直接返回,没命中才发请求,间隔 1.1 秒压住免费额度;单条查询失败只跳过该条,不会让整个批次崩掉。region_heat 是核心逻辑——按省份累加加权热度,数据量几百几千条时秒级出榜。跑完把 region_heat.csv 丢进图表工具或叠到地图上,就是一张舆情地图。

四、落地注意事项 ​

  • 转发链会稀释地域属性:一条原帖被转发,转发的 IP 归属地是转发者所在省,不是发帖者。想分析「源头地域」就只统计原帖的 IP;想看「扩散地域」才纳入转发——口径不同,榜单结论完全不同,做之前先定好。
  • IP 是城市级,不是精确位置:归属地给到省市足够支撑舆情地域分析,想精确到区县需要 VIP 版 area 字段;别拿它定位具体个人。
  • 代理 IP 要识别:舆情库里大量条目如果来自机房/代理(ip_type 为 IDC),可能是刷量号在干扰声量,统计时单列一组对比,别混进真实网民声量。
  • 阈值从数据里来:权重和 Top 榜截断数(15 名)都是初始值,跑两期舆情后按「跟业务判断是否一致」校准;不加权的省份排名和加权的往往差很多,这也是加权存在的意义。
  • 合规:处理的是已入库的舆情数据,IP 属性和内容关联分析要限定在本平台数据范围内,明确处理目的(地域声量分析),不对外输出单条 IP 明细。

总结 ​

舆情地域分布不是舆情 SaaS 的专利,用 https://ip9.com.cn/get?ip=xxx 免费接口 + 一个 Python 脚本就能跑通:条目 IP → 归属地省份 → 互动加权聚合 → Top 榜和 CSV。接口免费版 60 次/分钟够中小舆情库用,权重、口径(源头 vs 扩散)、代理过滤这三件事想清楚,结果就能直接上汇报。觉得免费额度紧,或要区县粒度、ip_type 识别代理,再看官网 https://www.ip9.com.cn 的 VIP 说明。先拿最近一期舆情导出跑一遍,对照业务判断调好权重,这套轻量方案能顶掉一半的 SaaS 咨询费。