互联网技术人员广告行业生存手册

2013年转入广告行业,先在Omnicom,当时正闹着和Publicis合并,部门被拆于是跟着同事一起转到WPP,最后在电通安吉斯稳定的度过了余下时间。如今再次转行,以后时间有限,所以先总结期间所见所想,希望能为转行到广告行业的互联网技术人员(如果有的话)提供一些参考。

首先,广告跟互联网一样都是分工细化的行业,某人在广告公司上班就让他给你的微店做宣传跟让搞IT的人给你修电脑装修QQ空间一样,所以要先界定个范围:我是在媒介购买的广告公司内从事程序化广告产品相关的工作。然后,根据下图再做进一步说明:

媒介购买就是帮最左边的广告主,通过中间的各种平台/工具(DSP/SSP等),让广告主的广告能展示给媒体上的消费者程序化广告一开始特指以竞价拍卖的方式,购买每一次广告展示的机会。现在变成只要是非直接采买的广告都会说自己是程序化广告了,边界比较模糊。

本文名曰“生存手册”,其实互联网技术人员转到广告行业是Hard模式到Easy模式,大多数情况下会过得比以前轻松,相比互联网公司的996,媒体的三班倒,广告行业不少百年老店都是良心企业了。

“我们还没有找到其它盈利模式,但我们可以先卖广告”

这是针对投资人的固定说辞。国内大多数互联网产品变现太难,广告是最容易想到的盈利模式。我转入广告行业的动机就是想了解广告公司是如何赚钱的。当然,特指那些在媒体上花钱投广告、从事媒介购买工作的广告公司。

现在我已经知道他们如何赚钱的了,可惜没什么借鉴意义。代理费仍然很重要,4A还没沦落到0代理费的地步。但媒体返点也很重要,碰上某些吝啬的广告主,要不是靠返点,广告公司都得赔本了。大广告主们每年上亿的广告预算,广告公司当然也得准备几个亿做垫款。而且国际化品牌只会找拥有全球渠道的广告公司。所以只要现有的商业模式不变,很难出现新的竞争者,广告主最多只是从一个广告集团换到另一个广告集团而已。

如今再反思我之前的说辞,广告对于所有互联网产品都是合适的盈利模式吗?没有明确受众属性的工具类产品,大多数情况下只能拿到“一刀9999级”这种劣质广告。不依托现有的巨头们,独立成长为一个大的线上流量入口,短期内实在看不出这种可能性。线下的广告空间又非常有限。多半有人想过在最近大热的共享单车上投放广告,先不考虑操作性,反正我是绝对不会骑一辆带着“专注产妇科不孕不育玛丽亚医院”广告的单车上街的……下次再谈到盈利模式的时候,我宁可设计几个不那么招人厌的付费产品方案,也不会直奔广告了。

所以,为广告公司提供技术解决方案或产品,是技术人员从广告行业赚钱的方案之一。广告公司没必要去做技术的脏活累活。跟他们合作有很多优点:广告公司通常营销商务能力强,而技术属性的公司这方面普遍要弱一些;技术风险小,广告公司很少自己提出技术要求,都是客户(广告主)提出,这些要求大多都是相当成熟且市场上已经有案例存在的,不会做冒险的技术尝试;不用担心产品被广告公司抄袭,他们最多要求白标,不会花大力气自己研发;广告是个相当坚挺的行业,通常人们认为经济下行就要降低广告预算,但一样有宝洁的肥皂剧反例,所以通常广告投入的预算占比是相对稳定的,能养活那么多公司,也说明整个盘子足够大。当然,有那么多公司,竞争的激烈程度也可想而知。在广告行业这几年,都是你方唱罢我方登场,图表上的公司换了不少。

“广告是魅力制造商,由社会嫉妒心态推动”

这是约翰•贝格(John Berger)的说法,各种艺术评论总是会批判广告:现代社会充斥着广告造成的虚假,在最常见的广告中所有人都是快乐的。刚入行时我也找了寥寥可数的几本“经典书籍”来认真阅读,结果基本是浪费时间:营销方式变化太快,几年前出版的书就没什么实践意义了;受众似乎也不像奥格威说的那么聪明,地铁里还是回荡着“58同城城城城”“百姓网网网网”的吆喝。还好媒介购买的工作,不用挖空心思吸引消费者的注意,主要还是说服广告主,技术是其中的一种手段,或者说是故事的一个素材。

在各种广告行业会议上,经常会有人在PPT里引用这句话:50%的广告预算浪费了,不知道浪费在哪儿。然后提出一个解决方案:先是RTB,后来是PMP,现在是PDB,穿插着大数据、社会化媒体优化等热词。未来不知道还会出现什么,不变的还是那句话。而且原因绕来绕去大多都是两点:牵涉公司太多或投放不够精准。

这其实是个问题链:他们都宣称自己能实现更精准的投放,如何更精准的投放?当然是准确的识别访客,展示适合他的广告;如何识别访客?你做投放的当然不能你说了算,得由第三方数据提供商或是由平台提供访客画像;怎么说明是精准的?当然要找监测公司来评定……其中的每个问题又都可以衍生出另一条故事线:第三方数据提供商和平台提供的访客信息,怎么证明足够准确可以作为精准投发的依据?BAT说自己有大数据,创业公司说自己有先进的、专门针对特定领域的人工智能分析;有了监测报告,怎么证明广告跟消费者最终的购买行为有关联?每家都有自己的归因分析模型……

广告行业中,技术是为故事服务的。有些问题看起来是技术问题,但只从技术上着手肯定解决不了。广告主需要的是完整的、没有漏洞的故事,不懂也不需要懂技术的细节。所以在技术方案做到60%左右的阶段,将其宣传为90%以上,让它在一个100%完善的故事里出场,将这个故事投入市场,才是广告行业一贯的做法。

一开始我对这种做法感觉有些别扭,可能是因为技术背景的人对方案的鲁棒性有强迫症吧。后来发现如果不尽快投入市场,toB的新产品/方案,需求其实很难准确掌握。不先把故事说圆满,也很难获得资源继续推进开发。事实证明只要迭代不被影响,开发的质量也还能保证。问题是,这些方案实际投入使用的周期和时间点,不是广告公司决定的,而是客户。因此大多数情况下,给技术人员的准备时间会非常短,突然增加大量的技术性的backlog,如果之前准备不足很容易出错。

这种讲故事为主的做法隐瞒了部分事实,但没有造假、欺诈,算是一种不讨人厌的策略,相比某些吸引消费者的手段来说干净不少,当然这种手段也不需要在广告行业学习,互联网行业明显走得更远。如果真的说广告是嫉妒心态推动的,某些互联网产品似乎是诸多更糟糕的要素推动的。

“我们总是高估两年内、低估十年内发生的变革”

如果是追求技术或产品的工程师,目前广告公司对于技术和产品的重视程度并不高,他们仍然在熟练的运用讲故事的方式、故事的素材、各种渠道,技术和产品还不是核心竞争力。各种广告技术公司倒是不错的选择。如果是想换个行业学习,锻炼各种商业技能,广告行业还是很适合的。

方向上,这两年经过媒体的大肆宣传,广告主们肯定都知道大数据、机器学习、人工智能了,自然会要求广告代理公司从这些点寻找方案,这将会是接下来一段时间主要的故事素材。传统媒体的广告预算逐步转到数字媒体,PC转到Mobile,户外广告比重增加,也是不需要重复的废话。更远的就很难判断了,广告行业变化很快。但讲故事这个有趣的传统应该不至于会被抛弃,虽然现在最热门的故事是各种“情怀”。

至于行业中的各种黑幕和八卦,最近报道比较多,我也就不凑热闹了。如前所述,传统行业的公司里各种制度比较完善,所以也更规矩一些。虽然我是干一行黑一行,但在广告行业遇到很多优秀的人,智慧且谨慎,充满活力又有前瞻性,并不比我在其他行业遇到的优秀人物逊色,只要他们还在广告行业,我想问题总归是能解决的。

GraphQL+Django提供基本API

GraphQL是Facebook去年开源的一套数据查询语言,对于大型系统,GraphQL提供一种灵活的访问通用数据的方式。当时正好有一个项目,需要前端调用后台的数据(Express+Postgresql)实现图表展示,我就从Postgresql里把数据导出成JSON文件(后来用Promise改成直接查询数据库),前端直接用GraphQL获取自己需要的数据,后台就省掉了制作API、写SQL的工作,运行至今基本稳定。

当时GraphQL还没有针对Django的modules,前两个月发布了graphene-django,做的事情不多,但可以搭配其他插件例如django-filters来实现更丰富的查询。GraphQL的优势在于查询语句的通用性和易读性,相比REST架构来说时间较短还不够成熟,如果是复杂需求后台的开发量仍然不小,现阶段在生产环境用Django做一个API服务还是rest framework等成熟的方案会更好。本文实现一个针对Django自带的User Model的简单API。

环境:

Django 1.10.4
graphene-django-1.2.0

Setup

安装graphene-django,如果是新建Django项目,创建后执行migrate创建表,再创建admin用户:

编辑settings.py,将graphene-django加入INSTALLED_APPS:

再编辑urls.py,导入GraphQLView,加上graphql的设置:

执行runserver,这时候访问/graphql会报错,显示没有提供schema。

Schema

在dmyz目录中新建schema.py文件,目录结构如下:

编辑schema.py文件,导入DjangoObjectType和graphene,处理django自带的User Model,代码如下:

最后编辑settings.py,加上配置:

现在重新访问/graphql,可以看到自带的前端界面,在左上方文本框中输入:

运行(点击上方▶️按钮)返回admin用户的username和email。右侧的Docs会显示Query信息,下划线命名也会自动转成驼峰命名。

查询语句完整格式是 query 名称{},上面的查询语句省掉了query,因为GraphQL默认会作为query语句执行,如果是mutation就需要加上了。关于查询语句可以参考官方文档:http://graphql.org/learn/queries/

Schema & Type & Field

无论后台是Python还是Nodejs,查询语句都是一样的,但不同语言对Schema的实现方式不同。Schema定义数据的呈现结构,包含各种Types,其中Query和Mutation是两个特殊的Type,Type通过Fields指定返回的数据字段。以之前的代码为例,代码中定义了Type(Query)和Fields(users),指定resolve来处理数据。

Filter

先增加一个测试用户,在后台添加或者执行:

执行之前的查询语句会返回两条记录。修改schema.py的修改Query:

执行新的查询语句,指定id:

新的查询语句用逗号分隔字段,增加了名字,执行后只返回id=2的用户:

Mutation

先继承graphene.Mutation创建一个类,定义输入的参数,再处理成ObjectType传递给Schema:

查询语句必须申明是mutation,传入Input中定义的字段,还要定义返回的内容:

执行后返回数据:

Afterword

如前文提到的,GraphQL不如REST架构历史悠久,但也有自己的独特优势:一是返回结果跟查询语句相关,对于返回的结构能有清晰认识,REST请求时并不知道会返回JSON还是XML,也不知道会返回那些字段;二是数据变动更灵活,不需要像以前一样v1/v2…vn来标明版本,采用单一查询入口更容易扩展。GraphQL是React生态的一部分(还有Relay),支持上目前来看是可以保证的。

[转]谈史学论题选择

讲者:严耕望 教授

日期:一九八零年十月廿三日(星期四)

时间:五时至六时三十分

地点:新亚书院人文馆十二室

谈史学论题选择

研究历史,首要的是选择题目。就小范围说,是一篇专门论文题;放大些说,是研究范围,也可说就是:面。无论是一篇论文题或一个研究的广面,对于研究的成绩及其所发生的影响都有极大关系。关于此一问题,我想分下面几点谈论。

(一)具体问题与抽象问题

历史上的某些问题比较具体,某些问题比较抽象,当然这很难划分,但无可否认,可作大体的划分,例如就大范围说,学术思想文学艺术问题比较抽象,政治经济民族社会问题就比较具体。这其中也各有程度的不同,例如学术又比思想具体些,政制又比政事具体些。前辈学人中,如陈寅恪先生喜欢讲比较具体些的问题,少论抽象问题。这在俞大维先生写的《怀念陈寅恪先生》一文中已明白讲到。

综观陈先生的全部论著明显的有此倾向。再如陈援庵先生,更是如此。他的《中西回史日历》,不但具体,简直就是一项机械工作,其功不细!我个人也喜欢研究具体问题。我的主要工作涉及两大范围,一是政治制度,二是历史人文地理,都是具体性,少涉抽象性。

我认为研究工作,为把稳起见,最好多做具体问题,少做抽象问题。研究具体问题,用可靠史料,下深刻功夫,一定能获得可观的成绩,而且所获成绩比较容易站得住脚,不容易被人否定,也就是说较容易成为定论;但抽象问题,虽然同样用可靠史料,同样下深刻功夫,但所获成绩就不一定能站住,也就不容易成为大家都接受的定论。因为具体问题的证据也比较具体,较容易作客观的把握,需要主观判断的成份较少,但抽象问题的证据往往也比较抽象,较难作客观的把握,需要主观判断的成份较多。主观判断的成份较多,在作者本身言,就比较容易走上主观意向,作错误的判断。在读者而言,在其它的研究者而言,也各参入主观成份,有不同的认识,作不同的判断。因此人各有一是非,上焉者可成一家之言,但很难得到大家都能承认的公论。然而一般人都比较喜欢讲抽象问题,尤其现在一般青年更似有此倾向。这或许是因为对于抽象问题,容易发议论,提意见,讲起来比较可以自由发挥想象力,甚至于仅得少数数据,一知半解,也可以主观的贯连,痛快淋漓的发挥一番,满足自己丰富的发表欲,至于具体问题,总认为繁难,不易见功。但实际上,具体问题似难实易,抽象问题似易实难。因为具体问题,可以肯定的说,一分耕耘,一分收获;抽象问题,虽然原则上也是如此,但不必如此。也许自己辛苦经营,以为发千古之未覆,心满意足,但他人看来可能付之一笑。

然则大家都搁置抽象问题不予研究吗?我实并无这意。不过就一般人言,以正时弊。若是对于抽象问题实有浓厚兴趣,自信天份极高,能见人所不能见,自亦可从事抽象问题的研究。不过要特别警觉,谨慎从事。天份高,功夫深,能谨慎,所得成果,纵然不能得到公认,但若能真正一家之言,也就是一项成就。
(more…)

用django-webpack-loader实现Django和Webpack的绑定

django-webpack-loader是Django去年的热门App之一,实现Django和Webpack的绑定。做法很简洁,就是用一个nodejs模块处理Webpack的输出,Django再去读取这个模块生成的JSON文件,加载对应的bundle文件就行了。最近在cygwin里重新部署了一次,分享其中的关键点。环境是:

  • Django 1.9.5
  • NodeJS v6.0.0
  • Win7(64)+Cygwin2.4.1

场景是将现有的一个Django项目,转成用Webpack来处理静态文件。因为没有执行npm init,安装时会提示找不到package.json。使用Webpack的人应该都知道自己在干什么,所以一些常见的基础配置、错误提示等本文不会涉及。

Install & Settings

首先用npm/pip安装相关的包:

然后设置Django(settings.py),把webpack_loader加到INSTALLED_APPS中,指定templates和static目录,新增WEBPACK_LOADER:

这样Django会从/static/js/dist/读取Webpack生成的文件。接下来配置Webpack,在目录中新建webpack.config.js,内容如下:

Usage

在当前目录执行webpack(全局或是项目),会自动读取/static/js/src/index.js,生成/static/js/dist/main.dev.js。当然,现在会报错提示找不到entry,要创建对应的js文件,以下只写了import语句来验证loader已经加载:

执行后在dist中生成js文件。现在要自动把生成的文件(bundle)加载到模板,django-webpack-loader使用template tag的方式来实现。创建一个模板文件,内容为:

这样生成的js文件就会以script标签的形式自动插入当前模板了。Webpack执行的结果(通过webpack-bundle-tracker)会写入webpack-stats.json文件中,Django运行时会读取这个json文件,如果出错会抛出500错误。

Extension

render_bundle也接受参数来指定文件扩展,可以处理css文件。例如要在Django中使用AdminLET,执行以下步骤可以通过Webpack来加载:

上列命令安装AdminLET和需要的loader,因为css文件中通常都会指定图片文件(还是字体等),所以还需要url-loader/file-loader。extract-text-webpack-plugin用来把css合并到一个文件中。

修改webpack.config.js

最后修改/static/js/src/index.js文件和模板文件:

可以看到按钮已经应用了样式。

Python读取Outlook的日历

公司的会议预订系统使用Outlook,要读取这些预订记录传到部署在阿里云的数据库。Exchange服务器没办法从外网访问,就在内网某台机器上写了一个脚本,定时执行,来从Outlook中读取日历上的预订记录。

模块:pywin32 (http://sourceforge.net/projects/pywin32/)
环境:Windows 2008 + Outlook 2013 + Python 2.7.8

Filder & Items

因为从来没做过Windows开发,所以先看了一遍文档,主要的概念是Folder(https://msdn.microsoft.com/EN-US/library/ff866772.aspx)。

Folders

所以其实就是要读取Outlook中名为Calendar的Folder,要获得会议预订记录,取出Folder的Items进行迭代就可以了:

(more…)

Linux设置iBeacon支持微信摇一摇

今年微信的摇一摇开始支持iBeacon了,淘宝上冒出一堆卖iBeacon基站的店铺,价格也比之前便宜许多。只是在开发测试阶段,直接用现有的设备会更简单,手头有一个USB蓝牙适配器,于是用Linux机器改造成iBeacon基站了,本文记录设置的步骤。前两年iBeacon刚发布的时候,有篇很火的文章指导如何用Raspberry搭建iBeacon基站(DIY Beacon with a Raspberry Pi),本文有参考那篇文章,只想实现iBeacon基站,不关注微信设置,也建议直接看原文。

蓝牙设备需要支持BLE(Bluetooth Low Energy,低功耗蓝牙)且是4.0版,我买的这个是CSR8510芯片,淘宝价格30元以内。以下设置在Ubuntu 14.04,Openwrt(ZTE Q7+r47026)和Raspbian(cb799af)中测试通过。一些APP(例如beacon-toolkit)和脚本(例如linux-ibeacon)可以简化配置。

微信设置

首页进入微信摇一摇后台(https://zb.weixin.qq.com/nearby/index.xhtml),点击左侧『页面管理』进入页面点击,点击『添加页面』按钮新增一个页面,跳转到dmyz.org:
Add page

(more…)

React.js最佳实践

虽然标题是最佳实践,其实是这一年来自己挖的各种坑,填了之后发现“其实这样写就不会有坑了”的总结…环境为桌面浏览器,因为目前后端还是Python为主,暂时没机会在NodeJS中使用各种便利的特征/插件。

代码顺序

定义一个React组件通常是包含以下代码:

以空行分割,有4个部分:

(more…)

关于中国色网站的说明

很早之前在一个日本网站上看过一个很不错的颜色排版方式,于是做了一个中国版的http://zhongguose.com/。我个人不认为山寨是什么丢人的事(企鹅可恨在于抢人饭碗而不在于山寨),直接在右下角注明了。完工后就任其自生自灭,也没太关注。结果前段时间在微博上被转发后,当天访问量突然飙升到10w级,随之而来相关讨论和邮件(怎么就没人捐钱呢╮(╯_╰)╭),有鼓励有质疑,故在此统一回应。

先说容易懂的,关于颜色数值和名字。前人研究请参看:《色譜》與《中國的傳統色》、《中國色名綜覽》之色名比較研究。数值是从《色谱》和其他书上取的。其中标注星号的例如天青,出处为东方出版社2013年版的《中国颜色》,这本书算不上权威,FB上有读者质疑它使用了曾启熊教授的研究而没有注明,所以只取了这书上很少的几种颜色作为补充。

通常来说,礼仪的场合古人倾向使用纯度很高颜色(肖世孟. 先秦色彩研究[D]. 武汉大学 2011),这些颜色在现代印刷、显示的场景中就太重了。因此经典的五色——青、赤、黄、白、黑——没有单独列出来,名字直白或者生活中常见的颜色也没有选入(例如大红、灰白)。一言蔽之就是有缺漏但不会有冗余,只取前人研究过有据可查的而不去自己臆造。

我的本业是Web开发,色彩研究我是外行,如果有更好的数据来源或是更权威的颜色数值请告知,我会及时更新的。

然后说说网站。网站13年就完工了,那时代码里还充斥着-webkit-的写法,这两年HTML5规范确定后就用新的方式重写了,除IE这种不支持mask的奇怪浏览器,其他浏览器都能正常浏览。功能不复杂,没有使用jQuery之类的DOM框架,react那些视图绑定的框架就更用不上了。也考虑过是否基于它来扩展社区类的功能,但没有更好的想法前还是保持现状了。个人最希望出现一位有爱的设计师,做一个更中式风格的设计。一来可以去掉右下角的Shanzai标注,二来实现一个国内的漂亮网站,在这个被微信拉底了整体设计风格,充斥着华而不实Campaign网站的大环境中,总是一个很好的示范吧。

最后的重点,感谢所有给我建议、鼓励,喜欢这个网站的网友们!

Python,Javascript(NodeJS),PHP之间的AES加密解密

场景是现有的几个项目,微信企业号是PHP,广告数据处理平台是Python,其中的Web API部分是NodeJS。现在这几个应用之间要传递数据,基于安全考虑先用AES加密,接收后做解密处理。本来预想是一个很简单的工作,库都是现成的,但发现网上的代码要么是不全,要么是padding处理不一致,所以最后还是自己看文档来写的,分享其中的核心代码,有类似需求可以直接拿去用。

AES

AES的介绍可以参看Wikipedia: 高级加密标准。这种加密方式需要指定Key(密钥)和IV(初始化向量),解密时使用同样的Key和IV进行解密。其次需要padding(填充字符),网上一些代码是用空格或者大括号做padding,解密后再用rstrip/rtrim/replace清掉,但用标准的PKCS会更好。

(more…)

Django+MySQL安装配置详解(Linux)[更新为1.8.2版]

Perface

Django是一个开源的Web应用框架,由Python写成,并于2005年7月在BSD许可证下发布。Django的主要目标是使得开发复杂的、数据库驱动的网站变得简单。Django采用MVC设计模式注重组件的重用性和“可插拔性”,敏捷开发和DRY法则(Don’t Repeat Yourself)。在Django中Python被普遍使用,甚至包括配置文件和数据模型。本文介绍Django在Linux(Ubuntu 14.04.2)+MySQL(5.5.43)环境下安装、配置的过程,包括安装、运行、添加应用的所有流程,最终建立一个可以从MySQL读取文章并显示的Django应用。文章面向刚接触Python/Django的初学者,所以安装过程都以默认环境为主,用pip可以简化安装过程。

Install

首先下载Django,得到Django-1.8.2.tar.gz,后解压并安装

$ wget https://www.djangoproject.com/m/releases/1.8/Django-1.8.2.tar.gz
$ tar xzvf Django-1.8.2.tar.gz
$ cd Django-1.8.2
$ sudo python setup.py install

如果提示缺少setuptools还要下载安装setuptools(建议提前安上,因为在安装MySQL for Python的时候也会用到)。
(more…)