← 返回蜂巢洞察

程序化广告的运作原理

大多数关于 程序化广告 的教程都只停留在网页横幅这个层面。其实这很可惜,因为一旦你将这种概念应用到现实世界中,它会变得有趣得多。 如果你之前从未从事过广告行业的工作,也无需担心。你不需要任何广告行业的背景知识就能理解这些内容。只要你能阅读基本的Python代码,那就已经具备了所需的一切条件。 广告本身只是实现这一目标的一种手段而已。真正重要的技能是:如何将现实世界中那些杂乱无章的信息转化为软件能够处理的数据。 在这篇文章中,你将会了解什么是程序化广告,以及为什么它在网页上能够取得如此好的效果。你还会明白,为什么将这种技术应用到户外广告牌上实际上是一个数据建模的问题,并且你会通过编写简单的Pyt

大多数关于程序化广告的教程都只停留在网页横幅这个层面。其实这很可惜,因为一旦你将这种概念应用到现实世界中,它会变得有趣得多。

如果你之前从未从事过广告行业的工作,也无需担心。你不需要任何广告行业的背景知识就能理解这些内容。只要你能阅读基本的Python代码,那就已经具备了所需的一切条件。

广告本身只是实现这一目标的一种手段而已。真正重要的技能是:如何将现实世界中那些杂乱无章的信息转化为软件能够处理的数据。

在这篇文章中,你将会了解什么是程序化广告,以及为什么它在网页上能够取得如此好的效果。你还会明白,为什么将这种技术应用到户外广告牌上实际上是一个数据建模的问题,并且你会通过编写简单的Python代码来实践每一个步骤。

我们将涵盖的内容:

什么是程序化广告?

广告活动包含两个方面:一方面是拥有广告位的一方,比如新闻网站;另一方面则是希望投放广告的一方,比如鞋类品牌。几十年来,连接这两者通常需要通过电话、电子邮件和纸质文件来完成。

而程序化广告则用软件取代了这种人工操作的方式。没有人需要亲自去协商广告的投放位置,系统会自动分析广告位的情况,并在几毫秒内决定是否购买以及以什么价格购买。

当你打开一个网页时,这个网页会告诉广告交易平台某个广告位是空闲的,同时还会提供关于该页面及浏览者的相关信息。广告商的系统会实时参与竞标,最终胜出的广告会在页面完全加载之前显示出来。这个过程被称为实时竞价,而且每天会有数十亿次这样的交易发生。

有两个术语可以帮助你更好地理解这一概念:“库存”指的是卖家可以提供的广告位数量;“展示次数”则是指一个人看到某条广告的次数。

在互联网上,这种模式的运用速度非常快,而且几乎毫不费力。了解其中的原因是很有必要的。

为什么互联网让程序化购买变得如此简单

互联网在无意中使程序化购买成为可能。每一个网页广告位都带有预先设定的结构:它们都有地址,也就是URL,同时也有在页面上的具体位置。这些广告位都存在于一种结构明确的文档中,这种文档就是文档对象模型。此外,这些广告位还能反馈广告是否已经加载并被人看到。

从诞生之初,整个互联网环境就具备被机器读取的能力,因为当时就已经有机器在为它提供服务了。当自动购买系统出现时,它已经拥有一个结构清晰、便于操作的平台——因为这种媒介本身就已经完成了所有的数据建模工作。

请记住这一点,这是理解整个道理的关键。

广告牌带来的问题

现在,把同样的思路应用到广告牌上吧。你会发现,广告牌根本不具备那种方便的数据结构。

一个物理广告牌并不会标明自己的位置或朝向方向,也不会说明本周的收费标准或是否还有空位。在它的历史中,这些信息都存在于价格表和销售人员的记忆中,而不是任何程序可以查询的数据中。

关键在于:户外广告这种媒介之所以落后于互联网,并不是因为它的效果较差,而是因为它没有可供机器读取的信息接口。目标受众本来就在那里,但缺乏结构化的数据才是真正的问题所在。

因此,将程序化购买模式应用到现实世界中,真正的关键并不在于复杂的竞价算法,而在于数据建模。

让我们具体来看一下这个过程。整个流程分为四个步骤,每个步骤都体现了许多工程问题中常见的处理方式。

第一步:确定实体身份

同一个广告牌可能会出现在不同供应商提供的数据集中。每个供应商会给它起不同的名称,并给出略有差异的坐标信息。在进一步进行处理之前,必须先将这些物理对象统一成一条记录。这个过程被称为记录关联,在这里主要是根据地理位置来进行关联。

一个简单的判断方法是:如果两个记录所处的位置相近,并且名称相似,那么就可以认为它们代表的是同一个广告牌。要计算两个坐标之间的距离,可以使用哈弗辛公式。

from math import radians, sin, cos, asin, sqrt

def haversine_m(lat1, lon1, lat2, lon2):
    """计算两个坐标之间的距离,单位为米。"""
    lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
    a = sin((lat2 - lat1) / 2) ** 2 + \
        cos(lat1) * cos(lat2) * sin((lon2 - lon1) / 2) ** 2
    return 6371000 * 2 * asin(sqrt(a))

def same_panel(a, b, max_distance_m=25):
    close = haversine_m(a["lat"], a["lon"], b["lat"], b["lon"]) <等于 max_distance_m
    similar_name = a["name"].lower().split()[0] == b["name"].lower().split()[0]
    return close and similar_name

vendor_a = {"name": "I-95 North Bulletin", "lat": 25.7907, "lon": -80.1300}
vendor_b = {"name": "I-95 Bulletin #4421", "lat": 25.7908, "lon": -80.1301}

print(same_panel(vendor_a, vendor_b))  # True – 同一个广告牌,对应同一条记录

在实际系统中,会使用更为模糊的匹配规则以及更多的数据信号,但原理是一样的。首先需要进行实体识别;否则,后续的所有步骤都会导致重复计算同样的信息。

步骤2:将位置视为可计算的上下文来建模

一个坐标本身并不能代表目标受众。要想让某个广告位对购买系统来说具有实际意义,就必须将其表示为一个带有方向信息的点。然后,通过结合道路交通数据,才能判断究竟有哪些人会经过这个广告位。

有用的信息是需要通过计算得出的,而不是预先给定的:

def estimate_impressions(panel, traffic_by_road):
    """根据道路交通数据估算每日广告展示次数。"""
    daily_vehicles = traffic_by_road[panel["road_id."]
    # 只有朝广告位正面方向行驶的车辆才能看到它
    facing_share = 0.5
    avg_occupancy = 1.4  # 每辆车上的乘客人数
    return int(daily_vehicles * facing_share * avg_occupancy)

panel = {"id": "P-4421", "road_id": "I-95-N", "facing": "south"}
traffic = {"I-95-N": 120000}

print(estimate_impressions(panel, traffic))  # 84000

请注意发生了什么:原始数据只不过是地图上的一个点,而经过处理后,它就变成了购买系统可以用来进行分析的信息——也就是84,000次每日预计的广告展示次数。这种处理方式在数据分析中非常常见:原始数据加上外部信息,就能生成有用的信息。

步骤3:将可用性表示为时间安排,而不是布尔值

一个网络广告位要么当前是可使用的,要么不可使用。在代码中,这种状态通常用布尔值来表示,即“真”或“假”。然而,广告位的实际使用情况往往是按时间段来安排的,其状态也会随着时间的推移而发生变化。库存数量也是会被预订、释放的,因此我们需要实时数据,而不是某个静态的快照。

from datetime import date

class PanelSchedule:
    def __init__(self):
        self.bookings = []  # 一个包含(开始时间, 结束时间)元组的列表

    def book(self, start, end):
        if not self.is_available(start, end):
            raise ValueError("该时间段内广告位不可使用")
        self.bookings.append((start, end))

    def is_available(self, start, end):
        return all(end < b_start or start > b_end
                   for b_start, b_end in self.bookings)

schedule = PanelSchedule()
schedule.book(date(2026, 8, 1), date(2026, 8, 14))

print(schedule.is_available(date(2026, 8, 10), date(2026, 8, 20)))  # False
printschedule.is_available(date(2026, 8, 15), date(2026, 8, 31)))  # True

这个道理在很多地方都适用。每当你对现实世界进行建模时,都要思考某个数值是否真的是固定的。实际上,很多数值都会随着时间而发生变化。可用性、库存数量以及座位分布情况等等,其实都属于需要按时间段来表示的信息,而不是简单的布尔值。

步骤4:将价格表示为函数,而不是一个固定的数字

价目表上只显示了一个数字,但实际价格会根据日期、需求以及剩余库存量等因素而发生变化:

def price_for(base_rate, start, weeks_out, occupancy):
    """根据交货期和需求来计算价格。"""
    demand_multiplier = 1 + occupancy          # 市场越繁忙,价格越高
    urgency_discount = 0.9 if weeks_out > 8 else 1.0  # 提前预订可享受折扣
    return round(base_rate * demand_multiplier * urgency_discount, 2)

print(price_for(base_rate=3000, start=date(2026, 12, 1),
                weeks_out=19, occupancy=0.85))  # 4995.0
print(price_for(base_rate=3000, start=date(2026, 8, 1),
                weeks_out=2, occupancy=0.40))   # 4200.0

一旦价格计算成为一种函数,软件就能瞬间对比成千上万的广告位和日期。而这正是自动购买功能所需要的。

整合各项要素

现在你们已经解决了实体识别、目标受众分析、实时可用性检测以及动态定价等问题。有了这些基础,程序化数字户外广告在现实世界中的运作方式与在线上是一样的。软件可以规划跨各种物理屏幕的广告活动,自动购买广告位,近乎实时地进行调整,并测量广告效果。

其实,这一切都与互联网本身无关。关键在于这些数据是否具有结构化的框架,也就是说,这些数据是否拥有明确的格式,能让软件理解每条数据的含义。一旦为现实世界的数据建立起这样的结构化框架,自动化处理也就成为了可能。

通过练习培养直觉

进行这种练习并不需要特殊的权限。你可以选择任何一份杂乱无章的、基于地理位置的公共数据集,然后按照上述四个步骤来操作。一个很好的数据来源是OpenStreetMap,它提供了数百万个真实地点的免费数据。

首先,通过合并描述同一实际对象的记录来消除重复数据;其次,为每个数据点添加计算得出的上下文信息,而不要将原始记录视为完整的信息;第三,将某个字段建模为会随时间变化的变量,而不是固定值;最后,将处理后的结果封装在一个清晰的接口中,比如一个类或一个小型的API,以便其他系统能够使用它。

虽然你并没有真正构建出一个广告平台,但你已经掌握了那些让自动购买功能得以扩展到新领域的关键技能。

总结

一个好的抽象模型在面对现实世界时依然能够发挥作用。

当你在一个整齐有序的环境中初次学习某个概念时,很容易认为这种整齐性就是该概念本身的一部分。但实际上往往并非如此。只有当你将这个概念应用到混乱的环境中去,看看它是否仍然能够正常运作时,你才能真正了解这个概念的通用性。

程序化购买技术就是一个很好的例子——它从屏幕上转移到了道路旁的墙上。在其他领域,你也可能会发现类似的规律:一个充满潜力的领域,只需要一个结构化的框架就能被有效利用。

希望你们喜欢这篇文章。你可以通过在LinkedIn上与我联系。

相关文章

技术实践

如何将结构化数据(JSON-LD)添加到Next.js网站中并进行验证

结构化数据对搜索引擎来说一直都非常重要。当搜索引擎读取你的页面时,它看到的是HTML代码:标题、段落、日期以及名称等等。 结构化数据能够直接告诉搜索引擎这些内容的含义。例如,这个页面是一篇文章,这是它的标题,这篇文章是由某个人撰写的,并且发布于某个特定的日期。 对我来说,搜索引擎优化并不是一个需要简单勾选的步骤。我希望自己创建的产品和网站能够被人们找到,并且在被找到之后也能被正确理解。 因此,只要合适,我就会在每一页中添加结构化数据。而且我认为,随着人工智能系统开始根据网页内容来回答问题,结构化数据的重要性只会越来越明显,而不会减弱。 在Next.js应用程序中,没有任何工具会自动为你生成这些

阅读全文
技术实践

如何向英国税务海关总署的“数字化纳税”API提交季度更新报告

每年有四次,所有参与“税收数字化”计划的个体经营者和房东都必须向英国税务海关总署提交一份收入与支出的汇总报表。 2026-27纳税年度的第一个截止日期是8月7日,这个期限已经过去;第二个截止日期则是11月7日。每份汇总报表都需要通过软件向英国税务海关总署发送一次API请求,而本教程正是专门讲解如何正确完成这些请求操作的。 您无需事先阅读任何其他资料即可跟随本教程进行操作。每次进行“税收数字化”集成时所需的一次性设置信息(包括沙箱应用程序、OAuth 2.0访问令牌以及防欺诈相关配置)已在下方的简要总结中列出,同时每段代码示例中也都会使用到一些辅助函数。 如果您想深入了解这些设置步骤,我曾在 之

阅读全文
技术实践

如何为您的开发团队制定端点数据丢失预防策略

开发人员的笔记本电脑中存储着比大多数人意识到的更多敏感数据:API密钥、数据库登录凭据、测试环境的配置信息,有时甚至还包括为了“测试”而下载的正式生产环境数据副本。 最终用户要为75%的内部数据丢失事件负责,而这些事件大多属于意外情况,并非恶意行为所致。对于开发团队来说,这种风险主要集中在终端设备上——也就是那些用于编写代码、进行测试以及部署代码的机器上。 以下是制定保护这些终端设备的策略的方法,而且这一策略不会影响团队的正常工作效率。 目录 如何制定终端数据丢失预防策略 步骤1:确定终端设备中敏感数据的存放位置 步骤2:将访问控制作为基础措施来实施 步骤3:强化终端设备的操作系统安全 步骤4

阅读全文
技术实践

当怀孕改变了用户的正常生理状态时,可穿戴设备该如何做出相应的调整?

你的智能手环或手表具备许多实用功能,比如能够记录你平时的生理数据,并在这些数据发生变化时及时提醒你。 静息心率、心率变异性、体温以及呼吸频率等因素都能帮助形成这些生理数据的画像,不过不同的设备会对这些测量结果进行不同的解读。 在怀孕之前,这些指标通常会保持稳定的状态,但怀孕会改变其中一些指标的正常范围。 我每天早上第一个打开的应用程序就是Oura,因此我很清楚一个评估结果的准确性会对我的一天产生多大的影响。如果“准备程度得分”过低,我会觉得自己做错了什么,但实际上并没有。现在想象一下,在你的身体正在经历一系列正常的生理变化期间,连续几周都看到这样的评估结果会是什么感觉…… 这就是我想在这里讨论

阅读全文