Python回归分析:探究广告投入与产品销量之间的量化关系

嗨,小伙伴们!我又活力满满地来找大家啦,今天咱们要开启一段超有趣的Python学习之旅。想象一下,咱们瞬间变身成“商业小侦探”,手握Python这把“神奇放大镜”,对着广告和销售数据仔细端详,就能找出广告投入与产品销量之间隐藏的量化关系,就好像发现了打开财富大门的密码,让商家知道怎么投广告最划算,是不是超有成就感?别等啦,跟着我这个还在摸索的“新手侦探”,咱们这就出发!

一、瞭望“数据江湖”——初识广告与销售数据

在拿起放大镜之前,咱们得先瞅瞅这广告与销售数据构成的“江湖”啥模样。这里面既有各个时间段、不同地区的广告投放费用,电视广告播了几秒、花了多少钱,网络广告投放了多少流量、成本几何,又有对应的产品销量,每天、每周甚至每月卖出去多少件,数据跟江湖里的暗流一样,错综复杂。就拿一款热门饮料来说,旺季时广告铺天盖地,销量蹭蹭上涨,淡季广告一缩,销量也跟着起伏,要是不分析这些数据,广告预算全靠瞎猜,赚多赚少全凭运气。

这时候,Python就像是咱们的“智能导航舟”,带着咱们在数据的江湖里航行,找到广告和销量关联的关键线索。

小贴士:多去翻翻品牌的财报、广告投放案例,感受下数据的繁杂。刚开始别被那些专业的广告术语、销售统计口径吓住,重点关注怎么挑出一个季度、一两个地区的关键广告和销售数据,把思路理顺。

练习题:找一篇介绍某知名化妆品品牌广告投放与销量分析的文章,记录下数据选取重点、分析思路,以及Python可能实现的初步数据筛选逻辑,类比理解回归分析思路。

二、握紧“工具百宝箱”——安装关键库

知道要干啥了,就得赶紧握紧咱们的“工具百宝箱”,这才能顺利启程。在Python里,有几个“神器”必不可少。首推pandas,它就像是给数据处理搭的“超级工作台”,不管是清洗广告销售数据,把那些错误、重复的“垃圾信息”清扫干净,还是整理数据,让杂乱无章变得井井有条,都轻松搞定,给咱们打造一个清爽的数据环境;再就是numpy,这可是处理数值计算的“数学天才”,算平均值、标准差这些统计量,又快又准,帮咱们摸清数据的基本特征;还有matplotlib,它能把数据可视化,就像把江湖里的航线画成地图,让咱们一眼看穿数据走势;statsmodels库更是重中之重,里面的回归分析工具就像一个个“智能预言家”,能帮咱们算出广告投入与销量之间的具体量化模型。咱们先来把它们安装好:

1pip install pandas
2pip install numpy
3pip install matplotlib
4pip install statsmodels

安装的时候可得千万小心,网络务必稳稳当当,要是断网,这安装准得“翻车”,只能干着急。偶尔statsmodels安装会碰到依赖包版本不兼容的问题,要是报错了,别慌,仔细瞅瞅错误提示,按照它说的办法解决就行。

小贴士:安装前务必去官方文档瞄一眼推荐版本,能少踩好多坑。要是报错,多去技术论坛搜搜,前辈们的经验能帮大忙。

练习题:在虚拟机里故意断网安装这四个库,看看会报啥错,熟悉熟悉常见的安装故障;然后用pandas读取一个简单的CSV格式广告销售数据文件(假设已有),动手熟悉基本操作。

三、奠基“数据基石”——理解数据类型与变量

握紧工具后,得给咱们的分析“奠基”,弄懂数据类型和变量咋用,这可是基础中的基础,就像盖房子的地基一样重要。数据类型好似不同的“收纳箱”,各有各的用处。整数型int,如同装整数的小盒子,广告投放天数、产品销售件数这些没小数点的数,就放里头;浮点型float呢,用来放带小数点的数,像广告投放金额、产品单价这些,经常精确到小数点后几位;字符串型str存文本,广告投放平台名称、产品型号这些文字信息都归它管。变量呢,就像是给这些“收纳箱”贴的标签,方便咱们找要用的数据。

比如说,咱们有几条简单的广告销售数据:

1product_model = "智能手环X1"
2ad_days = 15  # 广告投放天数
3ad_cost = 5000.5  # 广告投放成本,单位:元

要是一不小心,把ad_cost当成整数处理,比如直接舍去小数点后的数,那算广告成本效益、预估盈利这些事儿,可就全错了,数据不准,后续回归分析也得跟着出错。

小贴士:代码里变量名起得要有意义,一眼看过去,大概就能知道存的啥数据;定义变量的时候,数据类型千万别选错。

练习题:定义一个变量platform_name存广告投放平台名称,故意初始化为整数型,运行代码,看看数据处理啥情况;然后给变量名改得更清晰,动手熟悉下变量操作。

四、搭建“功能桥梁”——回归分析实战

有了基础,就得搭建“功能桥梁”,动手干起来了。先把广告销售数据准备好,用pandas构建DataFrame,就好比把零散的积木拼成规整的模型。假设咱们收集到下面这些简单的数据:

1import pandas as pd
2
3data = {
4    '地区': ['华东''华南''华北'],
5    '广告投入': [10001500800],
6    '产品销量': [200300150]
7}
8df = pd.DataFrame(data)

接着,咱们就轮到statsmodels闪亮登场了,用它来构建线性回归模型,探寻广告投入与产品销量之间的关系,原理有点像找一条最能贴合这些数据点的直线,来预测不同广告投入下的销量:

 1import statsmodels.api as sm
2
3# 添加常数项,这一步很重要,类似给直线找个起点
4X = sm.add_constant(df['广告投入'])
5y = df['产品销量']
6
7# 构建并拟合模型
8model = sm.OLS(y, X).fit()
9
10# 查看模型摘要,里面有很多关键信息
11print(model.summary())

这里model.summary()会输出一大堆信息,重点关注系数、P值这些。系数就像是告诉咱们广告投入每增加一单位,销量大概增加多少;P值越小,说明这个关系越靠谱。要是数据里有异常值,比如某个地区广告投入极少但销量奇高,没处理的话,模型结果可能就歪了。

小贴士:用线性回归模型时,数据要预处理,异常值、缺失值得处理好;多看看模型摘要,理解每个指标含义,别只看系数。

练习题:在数据里把华东地区的广告投入改成 5000 ,运行代码,看看模型结果啥情况;然后换用多项式回归(在statsmodels里也有相应方法)计算,动手熟悉下不同分析操作。

五、点亮“智能灯塔”——结果可视化与深入分析

最后,得点亮咱们的“智能灯塔”,让分析更完美。把回归结果可视化,用matplotlib画个散点图,横坐标是广告投入,纵坐标是产品销量,再把拟合的直线画上去,看看数据点与直线的贴合程度:

1import matplotlib.pyplot as plt
2
3plt.scatter(df['广告投入'], df['产品销量'])
4# 画出拟合直线,这里用到模型的预测值
5plt.plot(df['广告投入'], model.predict(X), 'r')
6plt.xlabel('广告投入')
7plt.ylabel('产品销量')
8plt.title('广告投入与产品销量关系分析')
9plt.show()

从图上能直观看到广告投入和销量的大致趋势,数据点越靠近直线,说明模型拟合得越好。深入分析方面,不能光看模型结果,还得结合市场动态、竞争对手广告等因素,比如竞品大打广告,你这边销量可能受影响,得重新评估。

小贴士:画图时多调整下图表样式,颜色、线条粗细啥的,让图更美观;分析结果多从不同角度思考,别轻易下结论。

练习题:给散点图的点设置不同颜色,区分旺季和淡季(假设数据有标识);再深入分析下某个特殊市场环境(如电商大促期间)的广告销售数据特点,动手熟悉下优化攻防。

小伙伴们,今天咱们学了好多实用的Python回归分析技能,从认识广告销售数据、握紧工具,到理解数据根基、实战分析,再到可视化深入,每一步都在向探究广告投入与产品销量之间的量化关系迈进。记得多动手练习,有任何疑问随时在评论区找我交流哦。祝大家学习顺利,Python技能更上一层楼!

【技术分享】探索DOM XSS一个trick的原理

前段时间P牛在他的星球发了一个XSS的小挑战(关于挑战的更多细节和解法见P牛的博客或者星球),我用的是DOM clobbering的方式完成。事后P牛给出了另一个trick,我看不懂但大受震撼,所以本文就来探讨一下这个trick的原理。

以下测试的步骤都是Win10 & Chrome 最新版(92.0.4515.131) 下进行的;作者水平有限,如有错漏,还请各位师傅指正。

挑战

小挑战的代码如下

<script> const data = decodeURIComponent(location.hash.substr(1));; const root = document.createElement('div'); root.innerHTML = data;
// 这里模拟了XSS过滤的过程,方法是移除所有属性,sanitizer for (let el of root.querySelectorAll('*')) { let attrs = []; for (let attr of el.attributes) { attrs.push(attr.name); } for (let name of attrs) { el.removeAttribute(name); } } document.body.appendChild(root); script>

可以看到这是个明显的DOM XSS,用户的输入会构成一个新div元素的子结点,但在插入body之前会被移除所有的属性。

解法

这里有两种解法,一种是绕过过滤的代码,另一种则是在过滤前就执行的代码

失败解法

有一些常见的payload在这个挑战里是无法成功,例如

,原因也很明显,onerror在触发前被过滤掉了。

绕过过滤

绕过过滤主要是为了使得Payload里面的属性不被清除,最终触发事件执行JS。具体做法正是DOM clobbering,但不是本文重点就不展开了,感兴趣的师傅可以看下Zedd师傅的文章,P牛的文章也有其他Payload,这里给出一个我的解法以供参考:

<form tabindex=1 onfocus="alert(1);this.removeAttribute('onfocus');" autofocus=true> <img id=attributes><img id=attributes name=z>form>

过滤前执行代码

另一种正确解法就是) we send SVGLoad events here if we can, otherwise // they'll be sent when any required loads finish SendSVGLoadEventIfPossible();}

这里有一个非常明显的判断IsOutermostSVGSVGElement,如果是最外层的svg则直接返回。注释也告诉我们了,最外层svg的load事件由LocalDOMWindow::dispatchWindowLoadEvent触发;而其他svg的load事件则在达到结束标记的时候触发。所以我们跟进SendSVGLoadEventIfPossible进一步查看。

bool SVGElement::SendSVGLoadEventIfPossible() {  if (!HaveLoadedRequiredResources())    return false;  if ((IsStructurallyExternal() || IsA(*this)) &&      HasLoadListener(this))    DispatchEvent(*Event::Create(event_type_names::kLoad));  return true;}

这个函数是继承自父类SVGElement的,可以看到代码中的DispatchEvent(*Event::Create(event_type_names::kLoad));确实触发了load事件,而前面的判断只要满足是svg元素以及对load事件编写了相关代码即可,也就是说在这里执行了我们写的onload=alert(1)的代码。

实验

我们可以将过滤的代码注释,并添加相关代码来验证这个事件的触发时间。

window.addEventListener("DOMContentLoaded", (event) => {    console.log('DOMContentLoaded')  });  window.addEventListener("load", (event) => {    console.log('load')  });

同时,我们将注入代码也再套嵌一层

可以看到结果不出所料,最内层的svg先触发,然后再到下一层,而且是在DOM树构建完成以前就触发了相关事件;最外层的svg则得等到DOM树构建完成才能触发。

小结

img和其他payload的失败原因在于sanitizer执行的时间早于事件代码的执行时间,sanitizer将恶意代码清除了。

套嵌的svg之所以成功,是因为当页面为root.innerHtml赋值的时候浏览器进入DOM树构建过程;在这个过程中会触发非最外层svg标签的load事件,最终成功执行代码。所以,sanitizer执行的时间点在这之后,无法影响我们的payload。

details标签

在P牛的文章里还简单提到了一个跟svg标签类似的,可以在Tui Editor里使用的payload,也就是;但我用小挑战的代码进行测试的时候却发现并不可行。所以,这里也值得探讨一下。

事件触发流程

首先触发代码的点是在DispatchPendingEvent函数里

void HTMLDetailsElement::DispatchPendingEvent(    const AttributeModificationReason reason) {  if (reason == AttributeModificationReason::kByParser)    GetDocument().SetToggleDuringParsing(true);  DispatchEvent(*Event::Create(event_type_names::kToggle));  if (reason == AttributeModificationReason::kByParser)    GetDocument().SetToggleDuringParsing(false);}

而这个函数是在ParseAttribute被调用的

void HTMLDetailsElement::ParseAttribute(    const AttributeModificationParams& params) {  if (params.name == html_names::kOpenAttr) {    bool old_value = is_open_;    is_open_ = !params.new_value.IsNull();    if (is_open_ == old_value)      return;
// Dispatch toggle event asynchronously. pending_event_ = PostCancellableTask( *GetDocument().GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE, WTF::Bind(&HTMLDetailsElement::DispatchPendingEvent, WrapPersistent(this), params.reason));
....
return; } HTMLElement::ParseAttribute(params);}

ParseAttribute正是在解析文档处理标签属性的时候被调用的。注释也写到了,分发toggle事件的操作是异步的。可以看到下面的代码是通过PostCancellableTask来进行回调触发的,并且传递了一个TaskRunner。

TaskHandle PostCancellableTask(base::SequencedTaskRunner& task_runner,                               const base::Location& location,                               base::OnceClosure task) {  DCHECK(task_runner.RunsTasksInCurrentSequence());  scoped_refptr runner =      base::AdoptRef(new TaskHandle::Runner(std::move(task)));  task_runner.PostTask(location,                       WTF::Bind(&TaskHandle::Runner::Run, runner->AsWeakPtr(),                                 TaskHandle(runner)));  return TaskHandle(runner);}

跟进PostCancellableTask的代码则会发现,回调函数(被封装成task)正是通过传递的TaskRunner去派遣执行。

清楚调用流程以后,就可以思考,为什么无法触发这个事件呢?最大的可能性,就是在任务交给TaskRunner以后又被取消了。因为是异步调用,而且PostCancellableTask这个函数名也暗示了这一点。

实验验证

可以做一个实验来验证,修改小挑战代码,将sanitizer部分延时执行。

const data = decodeURIComponent(location.hash.substr(1));; const root = document.createElement('div'); root.innerHTML = data; setTimeout( () => {     for (let el of root.querySelectorAll('*')) {      let attrs = [];      for (let attr of el.attributes) {       attrs.push(attr.name);      }      for (let name of attrs) {       el.removeAttribute(name);      }     }         document.body.appendChild(root) } , 2000)

代码修改前:

执行失败。

代码修改后:

可以看到,确实成功执行了事件代码。

那么回过头来想一下,为什么P牛测试Tui的时候直接成功,我却在修改前的挑战代码中失败?看一下Tui的处理这部分内容的相关代码。

export function sanitizeHTML(html: string) {  const root = document.createElement('div');
if (isString(html)) { html = html.replace(reComment, '').replace(reXSSOnload, '$1'); root.innerHTML = html; }
removeUnnecessaryTags(root); leaveOnlyWhitelistAttribute(root);
return finalizeHtml(root, true) as string;}

sanitizeHTML函数是处理用户输入的部分。比起挑战的代码,这里多了正则过滤,移除黑名单标签(removeUnnecessaryTags),不过不会移除所有标签而是留下了部分白名单标签(leaveOnlyWhitelistAttribute)。最神奇的地方来了,details标签也是黑名单的一员,这也是我一开始无法理解为何这个payload能成功执行的原因。但现在我们理清楚调用流程以后,可以有一个大胆的猜测:正是因为details在黑名单里,所以被移除以后其属性没有被直接修改,所以事件依然在队列中没有被取消。

再进行一个实验来验证,对挑战的代码做一些修改,增加移除标签的代码。

const data = decodeURIComponent(location.hash.substr(1));; const root = document.createElement('div'); root.innerHTML = data;
let details = root.querySelector("details") root.removeChild(details)
for (let el of root.querySelectorAll('*')) { let attrs = []; for (let attr of el.attributes) { attrs.push(attr.name); } for (let name of attrs) { el.removeAttribute(name); } }

成功执行了代码!

小结

所以我们可以得到结论,details标签的toggle事件是异步触发的,并且直接对details标签的移除不会清除原先通过属性设置的异步任务。

思考

对于DOM XSS,我们是通过操作DOM来引入代码,但由于浏览器的限制,我们无法像这样root.innerHTML = ""直接执行插入的代码,因此,一般需要通过事件触发。通过上面的例子,可以发现依据事件触发的时机能进一步区分DOM XSS:

立即型,操作DOM时触发。套嵌的svg可以实现

异步型,操作DOM后,异步触发。details可以实现

滞后型,操作DOM后,由其他代码触发。img等常见payload可以实现

从危害来看,明显是1>2>3,特别是1,可以直接无视后续的sanitizer操作。因此,师傅们可以研究浏览器的相关代码,通过这个方向来找到杀伤力更大的第一种或第二种类型的payload。

参考链接

一次对 Tui Editor XSS 的挖掘与分析

从Chrome源码看JavaScript的执行(上)

浏览器的工作原理:新式网络浏览器幕后揭秘

浏览器内核原理—Chromium Blink Html解析(2)

从Chrome源码看事件循环


- 结尾 -
精彩推荐
【技术分享】第二届"祥云杯" WEB WP
【技术分享】Electron的openExternal可控利用点分析
CEO为创业资金,竟招募黑客干起了勒索交易?
【技术分享】梨子带你刷burpsuite靶场系列之客户端漏洞篇 – 基于DOM的漏洞
戳“阅读原文”查看更多内容

本站内容来自用户投稿,如果侵犯了您的权利,请与我们联系删除。联系邮箱:835971066@qq.com

本文链接:http://news.xiuzhanwang.com/post/2653.html

发表评论

评论列表

还没有评论,快来说点什么吧~

友情链接: