2012年12月1日星期六
搜索引擎三巨头之争 Google凭借两项改进胜出
从精准到推荐:大数据时代重构网络广告商业模式
从精准到推荐:大数据时代重构网络广告商业模式
现在的网络广告,发展空间还很大,因为他们把99%的赚钱空间都扔了。随着大数据的发展,这些钱会被下一代一一捡回来。到那时,人们对于从精准到推荐的道理,会认识得更清楚。
未来广告将是这样的,广告对于不同的人,呈现不同的内容。按照这个精准标准衡量,现在几乎所有的广告,在将来都已经被淘汰了。
精准是相对于消费者需要而言的。由于不精准,所以消费者不需要。精准的问题,就是如何真正挖掘出消费者个性化需求的问题。大数据时代提供了什么样的契机?精准广告是否将向生活方式推荐的方向发展?让我们从未来学观点展望一下网络广告商业模式的重构。
马上将时髦起来的RTB
我们首先从最传统的说起。当然,从未来学尺度看,明年才流行的,就是我们这里说的最传统的(或叫正当红的、最时髦的等等)。否则未来淘汰什么呢?
据说今年是中国广告的RTB元年。RTB(Real Time Bidding),即实时竞价广告,是正当红、最时髦的广告热点。
RTB跟传统形式比较起来,广告批发市场(Ad Exchange)上卖的不是传统意义上的广告位了,而是访问这个广告位的具体用户。这个看起来,比过去的广告位精准多了。以前,如果电梯里,上来一个没牙的老头,你在电梯里打的牙膏广告肯定瞎了。现在,相当于盯住的不是广告,而是那个看广告的人。需要盯住这个用户的兴趣爱好,投其所好,才能产生最大的收益。RTB相当于把炙手可热的用户,拍卖给广告商。广告主冲的是购买更好的广告位和广告曝光率。
新的精准广告形式的出现,也带来广告管理方式的变革。在海外,人们建设了需求端平台DSP,帮助广告代理公司实现互联网广告的优化投放。随后,广告供应端平台SSP、数据管理平台DMP等细分平台也不断出现和发展,使广告管理手段越来越先进。
数据化进程也在加快,DMP(数据管理平台)和Data Exchange(数据交易平台)的出现,会帮助广告主、广告代理商和媒体主更好地整理数据和应用数据,从而为RTB中用户的价值计算、广告的针对性投放提升效果,进一步推动RTB产业的发展。
RTB是怎么实现精准的呢?假设潜在客户在浏览某网页面,某网向广告交易平台(Ad Exchange)请求广告。交易平台向所有需求端平台(DSP)发出公告,“某网有访客,要不要向他发广告”。DSP请求数据管理平台(DMP)帮助分析这位访客,并根据结果进行出价决策。Ad Exchange为出价高的DSP匹配相关广告代码。
在中国,易传媒为代表的新型网络广告企业正成为这一领域的佼佼者。易传媒先后推出了包括AdManager广告操作系统 、ASP供应端广告平台和ADP需求端广告平台在内的整合数字广告平台。ADP整合DAS和DSP二大需求端广告操作系统,是专为广告主/代理商开发的需求端广告技术平台;ASP是专为中国互联网和移动互联网媒体主打造的技术平台。
据Pubmatic数据,2011年RTB方式的购买在美国已超过10亿。IDC预计2012年RTB会占全部展示类广告的20%。到今年7月为止,根据Accordant Media统计的2012年第二季度的全球RTB市场份额较去年同期提升128%。
当前,广告商围着RTB分田分地正忙,整天惦记着如何在RTB方式中向Ad Exchange,Ad Network,DSP之间分配资源以发大财。
因为是在谈未来学,我们得谈谈这种即将时髦的东东,将来过时在什么地方。
RTB和所有传统广告一样,仍然是生产者的大喇叭。是以生产者为中心,用推技术向消费者强推的广告。只不过,过去打的是散弹,一打一片,现在比较能一枪一准了。消费者接受广告的方式,仍然跟“躺着挨枪”差不多。他不是找上门来,而是实在躲不过去。
当真正的大数据(而不仅仅是“很大的数据”)到来后,广告业会发生什么进一步的变化呢?
基于语义网的广告
其实,要说大数据,也不过是它背后一直在拱的那个力量,推动到表面上的东西。要说清大数据,就需要把同样跟它一样浮在表面上的东西,如WEB、移动、定位、2.0等等,联系在一起看,还原出推动变化的背后之手。
现在还没有一个更好的词,比语义网(Semantic Web)这个概念更适合把上面的东西串起来。语义网是伯纳斯·李——就是伦敦奥运会开幕式上那位互联网传奇人物——提出来的。语义网首先符合大数据的标准,是异构数据的聚合;其次它又是2.0的,代表着从WEB的P2P到移动互联网的Html5的前沿;它还是自组织自协调的社会网络计算,还是其它许多许多。不过,语义网就象概念跑车,它不是开到马路上实用的。它更象一种气场,把各种众生之物笼罩在它之下,发出灵光。
当我们用语义网这盏聚光灯,照向广告的时候,会发现广告的根基在动摇。
现有广告的原型,仍然是“纸”这样的媒介,这张“纸”(无论是文字、音频、视频)只不过是一块屏幕,是打给受众看(被动接受)的。语义网却完全颠覆了介质本身,它不再仅仅是一张纸,或一块屏,更象是中医的经络,只不过是语义的经络,“意义经络”,用网络串接起来的语义的经络。任何广告人也没有见过这样的事:你见过纸长了经络吗,你见过电梯屏能通向经络吗?俗称大数据、WEB之类的东东,最终都可以被用于打通,广告的任督二脉。只有把消费者心中的意义打通,精准才有的放矢。下一代广告的精准就不再仅仅象RTB、LBS这样定位到人,而是顺着意义经络,直接定位到心,作用于人心的向背。
这样说,可能显得比较空玄。让我们还是从身边容易理解的事情说起。口碑广告,大家都是知道的。口碑广告在技术上不算先进,但是有一点,它钻入人心的能力非常强,可以把人心打通。这就是意义的感应在起作用,也就是人心与人心,在一定信息通道作用下,彼此信任产生认同。口碑传播在低技术条件下,效率有限。因为,口耳相传效果虽好,但比不上广播、电视、报纸传播得快,传播得广。广告业虽也利用口碑传播,但更多是作为辅助手段。
但如果同语义网结合起来,就彻底不同了。它将对现在传统的广告中间商构成冲击。广告将演化为一场把广告商边缘化的大众狂欢。
我们观察到,口碑传播正是没有技术的P2P,是点对点互动传播。现有的广告不是点对点模式的,而是主从模式,更象老师对学生(RTB之前是学生满地跑,老师抓不住;RTB以后是老师逮住学生,一通猛灌);点对点相当于学生告诉学生(喻用户告知用户),没有老师(喻广告商)在场。
大数据时代第一个巨大变化,是把P2P改造成了Html5,相当于可以在技术层面之上,把应用P2P化。将来移动互联网上的应用,可以象口碑传播那样,彼此之间发生感应,产生泛广告化的趋势。Html5相当于把所有水库的大坝(比喻操作系统、客户端甚至平台)都毁掉,让WEB应用自己与自己产生拓扑联系,就象终端不走服务器,自己与自己相联一样。博客、微博、微信等,只不过是这个浪潮在原始阶段的时髦预演。这些自媒体都正在通向“自广告”。
没有大坝把水组织起来,水(喻应用)不就泛滥成灾了吗?换句话说,没有广告商居间,广告不就乱套了吗?不会,语义网规划了一条应用自组织的路,我称之为应用感应,也就是Web App与Web App之间,只要打通意义经络,就可以象心灵感应那样,自组织、自协调起来。SNS就是这场变革的预演。我们已经从中看到了规律,小世界网络反映的就是这种明显的规律,这样的规律在这类去中心化的组织形态中,发挥着过去只有统治者、AGENT才能起到的关键作用。将来的广告,也是Real Time的,只不过不是RTB,而是TRE(实时一切)的。它更加强调此时、此地的精准对撞,并且快聚快散,速配化。
广告除了不会大乱外,还会向更加精准的方向自发演进。因为还有大数据在。
精准不精准,对于大数据来说,不能满足于仅仅锁定人——LBS也可以锁定到人,支付也可以锁定到人——它的真正长处是可以锁定意义,也就是被LBS、支付、RTB锁定的那些人的头脑里的想法,这些想法在当地与此下的焦聚点。简单说,就是实现从人身的精准到人心的精准。
在这一过程中,网络广告将会发生从1.0向2.0的飞跃,这既是机遇,更是挑战。
首先,万维网本身升级为语义网,会彻底改变网络广告的生存环境。现有万维网与电视、大街等的广告环境,都是圈占一块人来人往的热闹之处,相对集中地投放广告。语义网采用给内容加入标记的方式构筑WEB环境,相当于将使广告屏之内和广告屏之外的界限被打破,如果你能想像这样的事发生,你就会明白这种界限的打破意味着什么:你走在大街上,低头系鞋带,发现鞋带断了,这时忽然脚前的砖亮了,上面显示你右前方50米第一个柜台有配你这双鞋的鞋带。实际上,语义网会使每个网页都成为这样一块智能的广告砖。广告人必须适应从集中投放,向离散化投放的转变。
其次,现有的数据都是集中在数据中心(如数据平台)进行计算,这种中央计算模式相对于2.0仍然是传统模式,真正的一对一精准广告是不可能用这种方式完成的。有人可能会说,一对一只是(对细分到某一程度)比喻的说法,真正的一个对一个网上营销永远也不可能经济地实现。错,这只是中央计算模式的单方面结论。如果考虑到语义网泛在化,包括移动化的事实后,数据分析发生的一个戏剧性的变化,可能是从中央计算模式,向本地计算的转移。也就是说,每个人的手机,可能成为自主计算中心。广告的最核心计算,可能在去中心化的地方完成。你可能会摸我的脑门,看我是不是发烧或说胡话。如果是这样,我只好说,这笔钱要等你12岁以下的后代去挣了。
我们的后代,跟我们的做法有什么不一样呢?他们的大数据,是两点计算(本地+中心计算),而不是我们的一点计算(中心计算)。举个例子就清楚了。比如拿一道题来考我们两代人:细分到条数来卖咖啡,广告该怎么打?我们这一代可能这样回答,我们从数据中心计算结果中,把顾客分为10条装的一类,40条装的一类,分开打广告;稍为改进,还可以算出常买40条装的人,活动规律是什么,给他来个RTB;我们的后代可能根本不是这么想的:他们的想法很可能是用优惠券诱使顾客打开(本地)手机chick in(那时的隐私保护是很严的哟),开放咖啡数据0.001秒,根据每个人手机私有云上独一无二的专属数据计算结果,直接告诉你,我知道你,是17条咖啡的常客;你,今天下午有个会,而今晨5点才睡觉,需要2条特浓咖啡发给你一个只属于你一个人的广告。让一个产品的广告,对每个人内容都不一样,这个超级难题,只花0.000001度电的成本就轻松实现了。
日本野村证券分析这种趋势,辨识出这是一种叫“产消逆转”的大趋势。意思是,过去集中模式中,我们的经济过程,都以生产者为起点,以消费者为终点;未来分散模式中,消费者成了起点,生产者成了终点。对大数据,对广告来说,产消逆转将导致头脚倒立的新型广告的出现。我们不知道还会有什么新鲜事情会发生,也许PUSH技术将成为过街老鼠,建立在客户服务器基础上的广告公司将象建立在沙滩上的房子一样垮掉,而基于PULL技术的广告将成为新的时尚。
重构网络广告商业模式的远景
再次声明一下,这里作为重构对象的网络广告,还并没过时,准确说,还有待未来两三年去发生、去时髦。这里之所以提前解构它们,是为了帮助读者看清几十集后的大结局,好提前做好心理准备;也为错过当前这网鱼的人们,预测一下未来。
上面说过,未来广告的核心变化方向,是从现在每个人看同样内容的广告,演化为每个人看不同内容的广告。语义网之所以重要,就在于它使内容,从死的(无标记的),变为活的(有标记的)。从难以加工出意义的(非智慧的),变为容易加工出意义的(智慧的)。但语义网究竟只是技术。生产力再牛,也得靠商业帮忙落地。大数据对网络广告商业模式的冲击,最终将落脚于何处?我们不妨大胆想象,让实践来小心求证。
广告说到底,要做的事情就是推荐。考虑商业模式的变化,先要从根子上想一想,同样是做推荐这件事,还有什么样更好的方法。过去没有条件想,现在有了大数据,有更好的工具和手段了,我们可以去想改变做事的方式。至少可以思考,新概念的推荐,可能或可以是什么样。
从广告界忙晕了头的状态中跳出来,跳到庐山之外来想广告这件事,不免会发现广告这件事作为推荐活动的与生俱来的一些缺点。我们观察自然的推荐活动,可以发现人们在接受推荐的时候,真正需要的是什么。两相对比,就会得到一些未来更贴近消费者的商业模式的线索。有时候,行内人也许会觉得这些不是问题,或是问题但永远解决不了,但有了大数据,谁知道呢?不要让钱都跑了,才明白。
首先,为什么只有1%的人忙活广告,而不是99%的人参与广告服务?实际意思是:广告推荐为什么总是生产者教导模式,为什么不能是让所有消费者参与推荐呢?
比如分析一个女友挽着另一个女友挑衣服这个常见的场景。如果我们把帮助挑衣服的女友,视为一个广告服务者。她的角色中至少包含了两个没花广告公司成本的职能:一是她在介绍产品信息,这是广告提供的基础产品,其属性是媒体功能;二是她在帮助女友拿主意(咨询),这是广告提供的增值服务,其属性是渠道功能。两者都是一对一精准化的,因此是高附加值的。
美国IT女王戴森在名著《2.0版——数字化时代的生活设计》中,曾把上面那种帮人家挑衣服的女孩,称为“生活方式设计师”。未来会出现成千上万个这样的自由职业。那时众包模式还没有出现,广告商还不可能大规模组织这种不要工资的生活设计师,加入到推荐者的行业中来。未来,进入人人时代,无组织的组织力量,会以产消逆转的方式,把消费者组织起来,投入到2.0的广告中。在这个模式中,广告商挣的主要不是媒体的钱,而是商务的钱。换句话说,他以前以为互联网只是媒体(挣广告费),未来可以当作渠道了(挣服务费)。把看漏的那一半互联网,找补回来。
你不可能为每个上街购物的人速配出自小长大的发小相伴,办法是用众包的方式,在地球村上为她找出最合适的生活方式设计师。具体分析可以看出,这一模式的实现,完全依赖于大数据。因为要让一位陌生的生活方式设计师为你的顾客推荐方案,设计师需要了解这个顾客与别的顾客有什么不同。我们前面说的手机数据语义网本地计算,就可以提供充分的数据支持。未来广告商要在商业模式上做的工作,是搭建与顾客本地数据对接的数据采集与分析平台,并且同拥有众多生活方式设计师接口的众包平台进行对接。
其次,为什么只有1%的公司打广告,99%的公司不打广告。这是上一个疑问,反过来问。现有广告的商业模式,因为是集中模式,最大的商业缺陷,是丧失了99%打广告的企业。99%的企业极为渴望打广告,从哪里看出来呢?他们中的一些人,整天在电线杆和车站牌上贴狗皮膏药,并与市容城管进行艰苦卓绝的战斗。如果没有打广告的强烈愿望作支撑,是不可能有这种近乎爬雪山过草地的劲头的。
但是,这99%的企业,并不想往中央电视台投广告,他们心目中的理想媒体,应该具有电线杆的某种特征。下一代的网络广告,就是要模仿出具有同样分散程度的分布式的、符合HTML5特点的“电线杆子”。例如,日本有6000万用户,习惯了二维码的广告形式。只要一个简单的刷的动作,整个过程就完成了。我看照片,有的还真是把二维码印在商店门口的电线杆上。内容估计是:来吧,向右两门,某某打折。当然,别真的把思路局限在电线杆上了。比方,也可以创新出一种形式,每个柜台上安一个按纽,模仿我第二炮兵部队发射精确致导“导弹”——打折券,对LBS锁定的50米范围的目标客户的手机,进行定向发射。背后的功课是,这部手机携带的号码,被系统cookie监测到出发前,曾上网集中搜索阿迪达斯某新款的价格,而那一款现在正趴在你柜台后第三排第二个格子里。
由于篇幅所限,不能展开谈,但仅从有限谈到的这些,我们看到,现在的网络广告,发展空间还很大,因为他们把99%的赚钱空间都扔了。随着大数据的发展,这些钱会被下一代一一捡回来。到那时,人们对于从精准到推荐的道理,会认识得更清楚。
语义技术让大数据变成“智慧数据”
语义技术让大数据变成“智慧数据”
从奇异值分解(SVD)看潜在语义索引(LSI)
从奇异值分解(SVD)看潜在语义索引(LSI)
隐性语义索引
发信人: walt@ncicbbs (瓦尔特), 信区: chinese 标 题: ◎ 隐性语义索引 发信站: 国家智能机中心曙光站 (Thu Apr 11 12:02:51 1996) 转信站: ncicbbs 隐性语义索引 .Walt. 1. 引言 自然语言文本中的词汇(术语)具有一词多义(polysemy)和一义多词(synonymy)的特点. 由于一词多义, 基于精确匹配的检索算法会报告许多用户不要的东西; 由于一义多词, 基于精确匹配的检索算法又会遗漏许多用户想要的东西. 下面是一个例子: 设Doc1, Doc2, Doc3是三个文件. 一些术语在这三个文件中的出现情况如下表: Doc1 Doc2 Doc3 -------------------------------------------------- access X document X retrieval X X information X* X* theory X database X indexing X computer X* X* -------------------------------------------------- 这里, 假定用"information" 和"computer"作为主题词进行检索, 那么Doc2和Doc3与之 精确匹配, 因而中选. 然而, Doc2是用户并不想要的文件, Doc1才是想要的查不出来, 不想要的倒查了出来. 这说明精确匹配不能很好地反映用户的意图. 那么有没有更好的 办法呢? 当然, 如果能基于自然语言理解来做这件事, 那一切问题就都没有了. 问题是: (1) 自然语言理解的目前水平还是有限度的; (2) 即使用自然语言理解, 效率也会很低. 我们希望找到一种办法, 既能反映术语之间内在的相关性, 又具有较高的效率. Bellcore以Dumais为首的研究小组提出了一种称为"隐性语义索引"的方法, 试图绕过 自然语言理解, 用统计的办法达到同样的目标. "隐性语义索引"的英文名字为 "Latent Semantic Indexing", 简称LSI. 2. LSI的做法 首先, 以术语(terms)为行, 文件(documents)为列做一个大矩阵(matrix). 设一共有 t行d列, 矩阵名为X. 矩阵的元素为术语在文件中的出现频度. 数学上可以证明: X可以分解为三个矩阵T0, S0, D0'(D0的转置)的积. 其中T0和D0的 列向量都是正交归一化的, S0是对角矩阵. T0是t*m矩阵, S0是m*m矩阵,D0是d*m矩阵, m是X的秩. 这种分解叫做单值分解(singlar value decomposition,简称SVD). X=T0*S0*D0' 一般要求T0, S0, D0都是满秩的. 不难做到把S0的元素沿对角线从大到小排列. 现在, 把S0的m个对角元素的前k个保留, 后m-k个置0, 我们可以得到一个新的近似的 分解: Xhat=T*S*D' 奇妙的是, Xhat在最小二乘意义下是X的最佳近似! 这样, 我们实际上有了一个"降维"的 途径. 下面要说明, T, S, D三个矩阵在文件检索中有重要的应用价值. 一个遗留问题是k到底取多大. k越大失真越小, 但开销越大. k的选择是按实际问题的 要求进行平衡的结果. 给定矩阵X, 基于X可以问三类同文件检索密切有关的问题: (1) 术语i和j有多相似? (2) 文件i和j有多相似? (3) 术语i和文件j有多相关? 第一类问题是术语的类比和聚类问题; 第二类问题是文件的类比和聚类问题; 第三类问题是术语和文件的关联问题. 下面我们用Xhat来进行这三类比较. 3.1 比较两个术语 做"正向"乘法: Xhat*Xhat'=T*S*D'*D*S*T'=T*S^2*T' (D'*D=I, 因为D已经是正交归一的). 它的第i行第j列表明了术语i和j的相似程度. 3.2 比较两个文件 做"逆向"乘法: Xhat'*Xhat=D*S*T'*T*S*D'=D*S^2*D' (T'*T=I, 因为T已经是正交归一的). 它的第i行第j列表明了文件i和j的相似程度. 3.3 比较一个文件和一个术语 恰巧就是Xhat本身. 它的第i行第j列表明了术语i和文件j的相关联程度. 3.4 查询 可以把主题词的集合认为是一个虚拟的文件. 查询的任务说白了是把这个虚拟的文 件和其他文件做相似性比较, 挑选最相似的出来(挑选到什么程度打住, 要看实际问题 的要求) ********************* 以上极简略地介绍了LSI的做法. 实验表明, 这一做法可以收到很好的效果, 在基于 内容的查询和信息过滤等方面有很好的前景.
Google推出语义食谱搜索
Google推出语义食谱搜索
漫话中文自动分词和语义识别(下):句法结构和语义结构
在前一篇文章中,我们看到了,利用概率转移的方法,我们可以有效地给一句话分词。事实上,利用相同的模型,我们也能给每一个词标注词性。更好的做法则是,我们直接把同一个词不同词性的用法当作是不同的词,从而把分词和词性标注的工作统一起来。但是,所有这样的工作都是对句子进行从左至右线性的分析,而句子结构实际上比这要复杂多了,它是这些词有顺序有层次地组合在一起的。计算机要想正确地解析一个句子,在分词和标注词性后,接下来该做的就是分析句法结构的层次。
名词性短语 → 形容词性短语+的+名词性短语
名词性短语 → 动词性短语+的+名词性短语
名词性短语 → 名词性短语+的+名词性短语
⋯⋯
动词性短语 → 动词性短语+了
动词性短语 → 介词短语+动词性短语
⋯⋯
⋯⋯
电话 被 窃听 的 房间 找到 了
新:词性 = 形容词,能作补语 = False ,能作定语 = True ⋯⋯
⋯⋯
踢:词性 = 动词,能带宾语 = True ,能带补语 = True ⋯⋯
污染:词性 = 动词,能带宾语 = True ,能带补语 = False ⋯⋯
排队:词性 = 动词,能带宾语 = False ,能带补语 = False ⋯⋯
⋯⋯
皮球:词性 = 名词,能作主语 = True ,能作宾语 = True ,能受数量词修饰 = True ⋯⋯
⋯⋯
那么,这样一来,计算机是否就已经能从句子中获取到理解语义需要的所有信息了呢?答案是否定的。还有这么一些句子,它从分词到词义到结构都没有两可的情况,但整个句子仍然有歧义。考虑这句话“鸡不吃了”,它有两种意思:鸡不吃东西了,或者我们不吃鸡了。但是,这种歧义并不是由分词或者词义或者结构导致的,两种意思所对应的语法结构完全相同,都是“鸡”加上“不吃了”。但为什么歧义仍然产生了呢?这是因为,在句法结构内部,还有更深层次的语义结构,两者并不相同。
去 <施事,目标>
淹没 <动力,受事>
看到这里,想必大家会欢呼,啊,这下子,在中文信息处理领域,从语法到语义都已经漂亮的解决了吧。其实并没有。上面的论元语义角色的模型有很多问题。其中一个很容易想到的就是隐喻的问题,比如“信息淹没了我”、“悲伤淹没了我”。一旦出现动词的新用法,我们只能更新论元结构:
然而,配价模型也仅仅解决了动词的语义问题。其他词呢?好在,我们也可以为名词发展一套类似的配价理论。我们通常认为“教师”是一个零价名词,而“老师”则是一个一价名词,因为说到“老师”时,我们通常会说“谁的老师”。“态度”则是一个二价的名词,因为我们通常要说“谁对谁的态度”才算完整。事实上,形容词也有配价,“优秀”就是一个一价形容词,“友好”则是一个二价形容词,原因也是类似的。配价理论还有很多更复杂的内容,这里我们就不再详说了。
不过,在实际的产品应用中,前面所说的这些问题都不大。这篇文章中讲到的基本上都是基于规则的语言学处理方法。目前更实用的,则是对大规模真实语料的概率统计分析与机器学习算法,这条路子可以无视很多具体的语言学问题,并且效果也相当理想。最大熵模型和条件随机场都是目前非常常用的自然语言处理手段,感兴趣的朋友可以深入研究一下。但是,这些方法也有它们自己的缺点,就是它们的不可预测性。不管哪条路,似乎都离目标还有很远的一段距离。期待在未来的某一日,自然语言处理领域会迎来一套全新的语言模型,一举解决前面提到的所有难题。
漫话中文自动分词和语义识别(上):中文分词算法
最简单的,也是最容易想到的自动分词算法,便是“最大匹配法”了。也就是说,从句子左端开始,不断匹配最长的词(组不了词的单字则单独划开),直到把句子划分完。算法的理由很简单:人在阅读时也是从左往右逐字读入的,最大匹配法是与人的习惯相符的。而在大多数情况下,这种算法也的确能侥幸成功。不过,这种算法并不可靠,构造反例可以不费吹灰之力。例如,“北京大学生前来应聘”本应是“北京/大学生/前来/应聘”,却会被误分成“北京大学/生前/来/应聘”。
不过,上述算法归根结底,都是在像人一样从左到右地扫描文字。为了把问题变得更加形式化,充分利用计算机的优势,我们还有一种与人的阅读习惯完全不同的算法思路:把句子作为一个整体来考虑,从全局的角度评价一个句子划分方案的好坏。设计自动分词算法的问题,也就变成了如何评估分词方案优劣的问题。最初所用的办法就是,寻找词数最少的划分。注意,每次都匹配最长的词,得出的划分不见得是词数最少的,错误的贪心很可能会不慎错过一些更优的路。因而,在有的情况下,最少词数法比最大匹配法效果更好。若用最大匹配法来划分,“独立自主和平等互利的原则”将被分成“独立自主/和平/等/互利/的/原则”,一共有 6 个词;但词数更少的方案则是“独立自主/和/平等互利/的/原则”,一共只有 5 个词。
他/说/的确/实/在理 (罚分:1+1+1+2+1 = 6 )
他/说/的确/实在/理 (罚分:1+1+1+1+2 = 6 )
但是,随便拿份报纸读读,你就会发现我们之前给出的测试用例都太理想了,简直就是用来喂给计算机的。在中文分词中,还有一个比分词歧义更令人头疼的东西——未登录词。中文没有首字母大写,专名号也被取消了,这叫计算机如何辨认人名地名之类的东西?最近十年来,中文分词领域都在集中攻克这一难关。
邓颖超生前使用过的物品
语义搜索:Google的人类知识图谱
语义搜索:Google的人类知识图谱
第二章, 微格式:语义标注和常义冲突
随着“网络”一词的不断革新,微格式(microformat)是前进中重要的一步,因为它提供了一种机制能嵌入“聪明的数据”到网页中,并且易于内容提供者来实现。简单的说,微格式是规定了如何增加结构化数据到网页中,且不用修改原网页。这一节将主要介绍微格式,并且深入一些实例将用到XFN(XHTML Friends Network),geo, hRecipe 以及 hReview等微格式。特别的,我们将从博客链接中挖掘人的关系,从网页中抽取坐标,从foodnetwork.com中解析菜单,并且分析对这些菜单的评论。本章中示例代码的实现没有处理全部的解析细节,但足够能将你领上道了。以微格式如geo或hRecipe来标记数据被称作社会化数据,它是令人感兴趣的,且社会化数据将不断增加。在写这本书的时候,几乎有一半的网页开发页报告有用到微格式,microformats.org社区刚刚庆祝了它的5岁生日。google报告有94%概率,富摘要信息(Google在搜索结果里引入的一种新的网站信息显示方式)中包含了微格式,如果google宣传了这点,我们将看到微格式重要的增长;事实上,通过ReadWriteWeb,google想要看到50%的网页包含了某种形式的语义标注,并且鼓励公司通过奖励的形式来支持这一动机。不管怎么说,你将看到更多的微格式,如果你关心网络的话。让我们进入正题吧。
XFN和朋友关系
语义网的狂热者预言像FOAF(friend of a friend --一种实体论描述人与人之间的关系,活动和活动之间的关系)这样的技术可能有一天会成为催化剂,促使很多社交网络联合起来对抗如Facebook这样的平台。虽然这些所谓的语义网技术如FOAF目前还达不到这点,但也不用太惊讶。如果你知道一些关于网络的短暂历史,那么你就会认识到这个革新是不容易的,因为网络极其分散的性质让它很难一蹴而就(参见第10章),然而,变化正在持续的,稳固的,向前进化着。微格式的出现就是为填补网页上“智能数据”的空白,它是一个很好的示范带给已有的技术以规范的标准。在这种特定情况下,它拉近了传统网络(基于易于人类阅读的HTML4.01)和语议网络(更加友好,易于机器解释)的距离。
微格式的好处在于,它提供了一种方法去嵌入这样的数据:社交网络,日程,摘要,书签;并且是反向兼容的。微格式的生态系统是多种多样的,如geo就发展得很快,并随着搜索引擎,社交媒体网站,博客平台而大受欢迎,而其它微格式则发展缓慢。当正写这本书的时候,重要的微格式开发正在进行中,包括google宣称他们在宣摘要信息中支持hRecipe.表2-1提供了一个总结对一些受欢迎的微格式。更多的示例请看http://microformats.org/wiki/examples-in-the-wild
表2-1, 一些受欢迎的技术云去嵌入结构化数据到网页中
还有其他很多微格式你可能会碰到,但是一个好方法是,认准池塘中最大的那条鱼,如Google, Yahoo!, 以及Facebook.越多的人用到微格式,它就越可能成功,越对数据挖掘有用。
通过XFN导出社交联系
随着一点梗概关于微格式是如何适应整个网络空间,下面让我们转向一个操作性的XFN(可能是目前你所遇到的最受欢迎的微格式)程序。如你所知,XFN是一种方式,来标识与他人之间的朋友关系,以在其他标签中加入rel属性。XFN常用在博客中,特别是博客链接插件中,像WordPress提供的那样。考虑如下的HTML内容,如示例2-1, 这可能会出现在博客的友链中。
示例2-1,XFN标注示例
- <div>
- <a href="http://example.org/matthew" rel="me">Matthew</a>
- <a href="http://example.com/users/jc" rel="friend met">J.C.</a>
- <a href="http://example.com/users/abe" rel="friend met co-worker">Abe</a>
- <a href="http://example.net/~baseeret" rel="spouse met">Baseeret</a>
- <a href="http://example.net/~lindsaybelle" rel="child met">Lindsay Belle</a>
- </div>
从以上内容的rel标签,可以很明显的看出这些人之间的关系。名叫做Matthew的家伙,有一帮朋友,一位妻子,一个小孩,他和这些朋友中的一个是同事,他在日常生活中和大家见面(这情况在网上也许不会发生)。除去规范的语法外,这就是XFN的全部内容。好消息是,虽然它非常简单,但在大规模情形下,它非常有用,由于它是结构化的数据。除非你碰到一些数据严重的过时--如两个最好的朋友变成了敌人,而忘记了更新他们的博客链接--XFN给出了非常准备的信息关于两个人之间的联系。已知大部分博客平台支持XFN,有很多的信息可以去发现。坏消息是,XFN只能告诉你这些信息,如果要得到更多的信息,需要借助其它的微格式。
让我们来看一个简单的获取XFN数据的例子,它类似于rubhub(http://rubhub.com/,一个社会化的搜索引擎,它爬取和索引大量的网站信息通过XFN)提供的服务,你可能想看一下在线的XFN工具在进入下一节之前。
一个宽度优先的XFN数据爬虫
让我们通过XFN获取社交数据, 并以此建立一个社交图谱。已知XFN可以嵌入任何可信的网站,坏消息是我们要做一些网站爬取工作。好消息是,虽然很繁琐,但BeautifulSoup包帮你最小化了这些麻烦。示例2-2的代码涉及到Ajaxian( 一个很受欢迎的博客关于现代化的网站开发),作为图的基础。在运行它之前请用easy_install安装BeautifulSoup.
示例2-2,从网页中爬取XFN内容(microformats__xfn_scrape.py)
- # -*- coding: utf-8 -*-
- import sys
- import urllib2
- import HTMLParser
- from BeautifulSoup import BeautifulSoup
- # Try http://ajaxian.com/
- URL = sys.argv[1]
- XFN_TAGS = set([
- 'colleague',
- 'sweetheart',
- 'parent',
- 'co-resident',
- 'co-worker',
- 'muse',
- 'neighbor',
- 'sibling',
- 'kin',
- 'child',
- 'date',
- 'spouse',
- 'me',
- 'acquaintance',
- 'met',
- 'crush',
- 'contact',
- 'friend',
- ])
- try:
- page = urllib2.urlopen(URL)
- except urllib2.URLError:
- print 'Failed to fetch ' + item
- try:
- soup = BeautifulSoup(page)
- except HTMLParser.HTMLParseError:
- print 'Failed to parse ' + item
- anchorTags = soup.findAll('a')
- for a in anchorTags:
- if a.has_key('rel'):
- if len(set(a['rel'].split()) & XFN_TAGS) > 0:
- tags = a['rel'].split()
- print a.contents[0], a['href'], tags
传入一个URL(包含XFN)给这个脚本,它会返回名称,关系类型,以及指向其朋友的url,如下所示输出:
- Dion Almaer http://www.almaer.com/blog/ [u'me']
- Ben Galbraith http://weblogs.java.net/blog/javaben/ [u'co-worker']
- Rey Bango http://reybango.com/ [u'friend']
- Michael Mahemoff http://softwareas.com/ [u'friend']
- Chris Cornutt http://blog.phpdeveloper.org/ [u'friend']
- Rob Sanheim http://www.robsanheim.com/ [u'friend']
- Dietrich Kappe http://blogs.pathf.com/agileajax/ [u'friend']
- Chris Heilmann http://wait-till-i.com/ [u'friend']
- Brad Neuberg http://codinginparadise.org/about/ [u'friend']
假如这些URL中包含XFN或其它有用的信息,我们可以直接跟踪这些链接,系统的建立更全的社交信息图谱。这种方法就是下一个示例要用到的:以宽度优先的方式的建立图谱,如示例2-3的伪代码所示。
- Create an empty graph
- Create an empty queue to keep track of nodes that need to be processed
- Add the starting point to the graph as the root node
- Add the root node to a queue for processing
- Repeat until some maximum depth is reached or the queue is empty:
- Remove a node from the queue
- For each of the node's neighbors:
- If the neighbor hasn't already been processed:
- Add it to the queue
- Add it to the graph
- Create an edge in the graph that connects the node and its neighbor
注意用这种方法创建的图会自动建立双向的边(如果这样的边存在的话),而不用任何的额外操作,以此来发现相互的朋友很有用。示例2-4是示例2-2的改进版本,它通过跟踪XFN中的超链接来建立NetworkX图。运行示例代码,即使仅为2的深度也能得到一张相当大的图,这取决于朋友网络中XFN的受欢迎程度。同时建立一张图片,观察它,并在它上面运行各种图的度量(见图2-1)。
- # -*- coding: utf-8 -*-
- import sys
- import os
- import urllib2
- from BeautifulSoup import BeautifulSoup
- import HTMLParser
- import networkx as nx
- ROOT_URL = sys.argv[1]
- if len(sys.argv) > 2:
- MAX_DEPTH = int(sys.argv[2])
- else:
- MAX_DEPTH = 1
- XFN_TAGS = set([
- 'colleague',
- 'sweetheart',
- 'parent',
- 'co-resident',
- 'co-worker',
- 'muse',
- 'neighbor',
- 'sibling',
- 'kin',
- 'child',
- 'date',
- 'spouse',
- 'me',
- 'acquaintance',
- 'met',
- 'crush',
- 'contact',
- 'friend',
- ])
- OUT = "graph.dot"
- depth = 0
- g = nx.DiGraph()
- next_queue = [ROOT_URL]
- while depth < MAX_DEPTH:
- depth += 1
- (queue, next_queue) = (next_queue, [])
- for item in queue:
- try:
- page = urllib2.urlopen(item)
- except urllib2.URLError:
- print 'Failed to fetch ' + item
- continue
- try:
- soup = BeautifulSoup(page)
- except HTMLParser.HTMLParseError:
- print 'Failed to parse ' + item
- continue
- anchorTags = soup.findAll('a')
- if not g.has_node(item):
- g.add_node(item)
- for a in anchorTags:
- if a.has_key('rel'):
- if len(set(a['rel'].split()) & XFN_TAGS) > 0:
- friend_url = a['href']
- g.add_edge(item, friend_url)
- g[item][friend_url]['label'] = a['rel'].encode('utf-8')
- g.node[friend_url]['label'] = a.contents[0].encode('utf-8')
- next_queue.append(friend_url)
- # Further analysis of the graph could be accomplished here
- if not os.path.isdir('out'):
- os.mkdir('out')
- try:
- nx.drawing.write_dot(g, os.path.join('out', OUT))
- except ImportError, e:
- # Help for Windows users:
- # Not a general purpose method, but representative of
- # the same output write_dot would provide for this graph
- # if installed and easy to implement
- dot = []
- for (n1, n2) in g.edges():
- dot.append('"%s" [label="%s"]' % (n2, g.node[n2]['label']))
- dot.append('"%s" -> "%s" [label="%s"]' % (n1, n2, g[n1][n2]['label']))
- f = open(os.path.join('out', OUT), 'w')
- f.write('''''strict digraph {
- %s
- }''' % (';\n'.join(dot), ))
- f.close()
- # *nix users could produce an image file with a good layout
- # as follows from a terminal:
- # $ circo -Tpng -Ograph graph.dot
- # Windows users could use the same options with circo.exe
- # or use the GVedit desktop application
图2-1,Ajaxian网站XFN数据的双向图,注意在点“http://ajaxian.com/"和点"Dion Almaer"的边“Me"暗示Dion Almaer拥有这个博客。
尽管很简单,但这个图很有趣。它连接了八个点,以叫做“Dion Almaer"的人为中心点。如果再爬深一层或更多层,将在图中引入更多的结点来连接更多的人。单从这个图中,是不清楚Dion 和 Ben Galbraith 是否有更亲密的关系比起其他人来,因为"同事(Co-worker)"和"朋友(Friend)"关系的不同,但我们可以去爬取Ben的XFN数据,从中取得“同事”关系的数据,再爬取其同事的“同事”关系数据,从而建立一个谁和谁一起工作的社交网络。见第6章,挖掘同学和同事关系。
宽度优先技术的简要分析
我不想中断主题来花很长时间分析这项技术,但由于这个示例是本书的第一个严格意义上的算法,不是最后一个,因此有必要仔细去测验一下它。一般来说有两个标准去测验一个算法:效率和有效性,或者说,性能和质量。
一般的算法性能分析牵涉到它的最坏情况下时间和空间复杂度--换句话说就是,执行程序要发的时间,执行时占用的内存空间。我们采用的宽度优先方法在本质上是宽度优先搜索,虽然我们没有特别的去搜索什么,因为没有必要除了去扩展图到最大的深度或达到我们需要的结点。如果我们搜索一些特定的东西而不是只爬取链接,这就被认为是真正的宽度优先搜索。事实上,所有的搜索都有一些既定的标准,由于有限的资源的限制。因此宽度优先搜索的一种变异称作有边的宽度优先搜索,它引入了一个深度的限制,如我们所做的那样。
对于宽度优先搜索(或宽度优先爬取),时间和空间的复杂度在最坏情况是b^d,b是图中的分支因子,d是深度。如果你在纸上草拟一个示例,考虑一下它,这会很有意义。如果图中每个结点有5个邻居,你只想要1的深度,最终你将得到6个结点, 即根结点和它的五个邻居。如果所有的这五个邻居也有五个邻居,当你扩展到下一级时,就得到了总共31个结点,即根结点,根结点的五个邻居,以及根结点的每个邻居的五个邻居。这样的分析看起来很迂腐,但是在大数据的工程领域,能够利用已有的数据集做粗略的分析是更加重要了。表2-2提供了一概览关于b^d的增长
表2-2,图的结点数随着分支因子和深度增加而增加
前面的讨论主要是关于算法理论上的复杂度,而最终要参考的度量是算法在实践中给定数据集上的性能。对代码大概的分析发现它的主要是I/O影响了其性能,因为大部分时间发在了等待urlopen返回数据来处理。在后面的一个示例, “多线程会话” 介绍和示范了用一个线程池来改善性能,以增加少量的复杂度为代价。
在这个分析中最后一项要考虑的是结果的质量。从质量分析的观点来看,由虚拟的视图可以看出这实际上是一个连接人的图谱。任务完成了吗?是的,但总是有改善的空间的。有一点要考虑的就是,结果中URL的细小差别会在图中多个结点中出现,虽然它们指向相同的内容。例如,如果涉及到Matthew的链接有http://example.com/~Matthew和http://www.example.com/~Matthew, 图谱中将出现两个不同的结点, 虽然它们可能指向相同的内容。幸运的是,XFN定义了一个rel="me"的特别值,来辨别可合并项。Google的社交图谱API就是用这种方法来连接用户的各种属性的,这里有许多的示例用rel="me"来允许用户在属性中添加外链。另外一问题(要小得多)就是URL的末尾是否有‘/’,大部分设计良好的网站会将一个转化为另一个,因此这就不成为问题了。
幸运的是,别人已经注意到了这个问题,并做了一些努力。SocialGraph Node Mapper(社交图谱结点映射)是一个有趣的开源项目,它可以标准化URL, 包括结尾的'/','www'是否出现等。它也可以辨别各种社交网站的不同URL指向同一个人, 如http://blog.example.com/Matthew和http://status.example.com?user=Matthew指向同一个人在某个社交网站上。
地标:A Common Thread for Just About Anything(不知这句什么意思。。。)
认为像geo, hRecipe这样的微格式对社交数据挖掘没有用而忽略他们是一个大错误。虽然单独的geo数据是没有用的,但它是很得要的将完成不同的数据集以相同的地理坐标联系在一起。地标数据是无所不在的,它们在社交数据结合上发挥了很多作用,因为将空间中的位置作为一个线索可以来聚类人们到一起。真实生活和网上生活的距离就被进一步接近,并且任何的社交化数据都与现实中的个体相连。例如,它可能是相当可怕的,你可能知道一个人她在哪里住,她在家做什么食物,甚至这些菜谱是由什么组成的。这一节来过一些示例,查找,分析,可视化地理和菜单数据,很快你将想到要怎么用它们。
维基百科的文章 + 谷歌地图 = 路线图 ?
一个最简单的微格式去嵌入地理数据到网页中的是geo, 具体的表现是通过vCard(提供了一种方式来描述位置)的属性geo。有两种方式可以嵌入geo微格式, 示例2-5中的HTML小片段说明了以这两种技术来描述富兰克林--田纳西州最好的一个小镇。
示例2-5,简单的geo标注
- <!-- The multiple class approach -->
- <span style="display: none" class="geo">
- <span class="latitude">36.166</span>
- <span class="longitude">-86.784</span>
- </span>
- <!-- When used as one class, the separator must be a semicolon -->
- <span style="display: none" class="geo">36.166; -86.784</span>
示例2-6用一个简单的程序分析geo微格式从MapQuest Local页面,来展示如何从geo格式的内容中抽取坐标。
示例2-6从MapQuest Local中抽取geo数据(microformats__mapquest_geo.py)
- # -*- coding: utf-8 -*-
- import sys
- import urllib2
- from BeautifulSoup import BeautifulSoup
- import HTMLParser
- # Pass in a URL such as http://local.mapquest.com/franklin-tn
- url = sys.argv[1]
- try:
- page = urllib2.urlopen(url)
- except urllib2.URLError, e:
- print 'Failed to fetch ' + url
- raise e
- exit()
- try:
- soup = BeautifulSoup(page)
- except HTMLParser.HTMLParseError:
- print 'Failed to parse ' + url
- exit()
- geoTag = soup.find(True, 'geo')
- if geoTag and len(geoTag) > 1:
- lat = geoTag.find(True, 'latitude').string
- lon = geoTag.find(True, 'longitude').string
- print 'Location is at', lat, lon
- elif geoTag and len(geoTag) == 1:
- (lat, lon) = geoTag.string.split(';')
- (lat, lon) = (lat.strip(), lon.strip())
- print 'Location is at', lat, lon
- else:
- print 'No location found'
用微格式的实现是巧妙然而在某各程度上意义深远:当一个正在读一篇文章关于一个地方如富兰克林,直觉的知道在地图上的一点指示这个镇的位置,一个机器就不能如此容易的得出这样的判断了,除非有特殊逻辑来针对各种样式匹配。但这样的页面抽取是非常脏的建议,且当你想要将所有的可能性都搞定的时候,你会发现你还是漏了一个。嵌入语义到网页中来标注非结构化数据,能够使机器人也能很轻易的明白,它消除了歧义,并降低了爬虫开发者的门槛。它是一个双赢的局面对消费者和生产者来说都是,希望能在这方面有更多创新。
绘制geo数据通过microform.at 和 google地图
在你从网页中发现有趣的geo数据的那一刻,每一件想要做的事也许就是可视化它。例如,考虑“美国国家公园列表”的维基百科文章,它显示了一个漂亮的国家公园的表格视图,并以geo格式来标了它们,但是它是不是更漂亮,去快速的将它们装载到一个交互工具中作可视化的观察?很好,microform.at就是一个极好的小的网络服务,它从给定的URL中抽取各种类型的微格式,并返回各种有用的格式的数据。它提供了各种选项去判断和交互同网页上的微格式数据,如图2-2
图2-2, http://microform.at的结果对于维基百科的文章“美国国家公园列表“
如果有得选, KML(Keyhole Markup Language)输出是最易于可视化geo数据的。你可以下载Google Earth然后在本地加载KML文件,或者输入一个URL指向KML数据的到google地图搜索框中,无需其它操作即可建立它。在microform.at显示的结果中,点击"KML"触发一个文件下载,此文件可以在Google Earth 中使用, 你也可以右键复制这个链接给Google Maps。图2-3显示了Google Maps的虚拟视图对于http://microform.at/?type=geo&url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FList_of_U.S._na
tional_parks--前面提到的维基百科文章的KML结果,也就是基本的url:http://microform.at和类型以及查询的url参数。
图2-3, Google Maps的结果显示所有美国的国家公园当传给它microform.at的KML数据
从包含语义标注(如geo数据)的维基百科文章中提取有用的数据,并可视化它, 这是一个很强大的分析能力,因为它可让我们很快洞悉重要的东西而只是做很少的工作。浏览器插件如Firefox Operator add-on 目标就是进一步降低这种劳动。在本节中只能讲这么多了,一个简洁的方法是发上一个小时左右的时间将本节中美国国家公园的数据同你的LinkedIn网络中联系人数据混合,从中你可能会发现怎么让下一次的出差更加有趣些。(见“从地理上了聚类你的网络“在第193页的例子,怎样通过使用k-means技术来查找类别并计算这些类别的中心点来获取和分析geo数据)
切分菜谱(为了健康)
由于google的富摘要信息的启动,将会有越来越多的微格式数据,许多的著名的美食网站做了很大的努力来暴露他们的菜单和评价通以hRecipe和hReview的形式。考虑这个潜在的可能性,一个虚拟的网上约会服务,它爬取博客和其它的社交网站,企图将配对人们去约会吃饭,某个人可能想要有权限查看特定的另一个的地理位置信息和菜单信息,这可能对增加第一次约会的成功机率有重大意义。人们能够被配对通过两个标准:一个人同另一个住的有多远;他们吃哪种类型的食物。例如,你可能会喜欢同肉食者约会吃饭而不是一个素食主义者。食物的偏好能够被用来增强商业创意。然而我们不会去创建一个新的网上数据服务,但我们想要激发你去创建它。
Food Network网站正完全的拥护以微格式来使整个网络更好,他暴露菜单信息以hRecipe并hReview格式来展现对菜单的评价。 这一节示范搜索引擎怎么从Food Network的菜单和评价中解析结构化数据以用来索引或分析。虽然我们不对菜单和评价中的纯文本数据作分析,或是持久的存储抽取的信息,后面的章节将会示范这些的如果你感兴趣的话。特别是,第3章介绍了CouchDB,一个很好的方法去存储和分享数据(和分析数据)--从包含微格式的网页中抽取的数据,第7章介绍一些基本的自然语言处理,你可以用来深入的分析评价数据。
示例2-6的一个改编版本来解析hRecipe格式的数据,如示例2-7.
示例2-7,从泰国菜中解析hRecipe数据(microformats__foodnetwork_hrecipe.py)
- # -*- coding: utf-8 -*-
- import sys
- import urllib2
- import json
- import HTMLParser
- import BeautifulSoup
- # Pass in a URL such as
- # http://www.foodnetwork.com/recipes/alton-brown/pad-thai-recipe/index.html
- url = sys.argv[1]
- # Parse out some of the pertinent information for a recipe
- # See http://microformats.org/wiki/hrecipe
- def parse_hrecipe(url):
- try:
- page = urllib2.urlopen(url)
- except urllib2.URLError, e:
- print 'Failed to fetch ' + url
- raise e
- try:
- soup = BeautifulSoup.BeautifulSoup(page)
- except HTMLParser.HTMLParseError, e:
- print 'Failed to parse ' + url
- raise e
- hrecipe = soup.find(True, 'hrecipe')
- if hrecipe and len(hrecipe) > 1:
- fn = hrecipe.find(True, 'fn').string
- author = hrecipe.find(True, 'author').find(text=True)
- ingredients = [i.string
- for i in hrecipe.findAll(True, 'ingredient')
- if i.string is not None]
- instructions = []
- for i in hrecipe.find(True, 'instructions'):
- if type(i) == BeautifulSoup.Tag:
- s = ''.join(i.findAll(text=True)).strip()
- elif type(i) == BeautifulSoup.NavigableString:
- s = i.string.strip()
- else:
- continue
- if s != '':
- instructions += [s]
- return {
- 'name': fn,
- 'author': author,
- 'ingredients': ingredients,
- 'instructions': instructions,
- }
- else:
- return {}
- recipe = parse_hrecipe(url)
- print json.dumps(recipe, indent=4)
用一个示例URL,如Alton Brown’s acclaimed Pad Thai recipe(奥尔顿受欢迎的泰国菜),你应该会得到如示例2-8所示的结果。
示例2-8, 示例2-7中泰国菜的解析结果
- {
- "instructions": [
- "Place the tamarind paste in the boiling water and set aside ...",
- "Combine the fish sauce, palm sugar, and rice wine vinegar in ...",
- "Place the rice stick noodles in a mixing bowl and cover with ...",
- "Press the tamarind paste through a fine mesh strainer and add ...",
- "Place a wok over high heat. Once hot, add 1 tablespoon of the ...",
- "If necessary, add some more peanut oil to the pan and heat until ..."
- ],
- "ingredients": [
- "1-ounce tamarind paste",
- "3/4 cup boiling water",
- "2 tablespoons fish sauce",
- "2 tablespoons palm sugar",
- "1 tablespoon rice wine vinegar",
- "4 ounces rice stick noodles",
- "6 ounces Marinated Tofu, recipe follows",
- "1 to 2 tablespoons peanut oil",
- "1 cup chopped scallions, divided",
- "2 teaspoons minced garlic",
- "2 whole eggs, beaten",
- "2 teaspoons salted cabbage",
- "1 tablespoon dried shrimp",
- "3 ounces bean sprouts, divided",
- "1/2 cup roasted salted peanuts, chopped, divided",
- "Freshly ground dried red chile peppers, to taste",
- "1 lime, cut into wedges"
- ],
- "name": "Pad Thai",
- "author": "Recipe courtesy Alton Brown, 2005"
- }
收集餐馆的评价
这一节结束我们对微格式的研究。Yelp的一个很受欢迎的服务,它用到了hReview,因此顾客留下的关于餐馆的评价是被暴露的。示例2-9示范了怎么抽取hReview信息从Yelp网页中。一个示例URL在代码中,它代表了一个泰国餐馆如果你有机会去的话一定不想错过的。
示例2-9,从泰国菜单中解析hReview数据(microformats__yelp_hreview.py)
- # -*- coding: utf-8 -*-
- import sys
- import re
- import urllib2
- import json
- import HTMLParser
- from BeautifulSoup import BeautifulSoup
- # Pass in a URL that contains hReview info such as
- # http://www.yelp.com/biz/bangkok-golden-fort-washington-2
- url = sys.argv[1]
- # Parse out some of the pertinent information for a Yelp review
- # Unfortunately, the quality of hReview implementations varies
- # widely so your mileage may vary. This code is *not* a spec
- # parser by any stretch. See http://microformats.org/wiki/hreview
- def parse_hreviews(url):
- try:
- page = urllib2.urlopen(url)
- except urllib2.URLError, e:
- print 'Failed to fetch ' + url
- raise e
- try:
- soup = BeautifulSoup(page)
- except HTMLParser.HTMLParseError, e:
- print 'Failed to parse ' + url
- raise e
- hreviews = soup.findAll(True, 'hreview')
- all_hreviews = []
- for hreview in hreviews:
- if hreview and len(hreview) > 1:
- # As of 1 Jan 2010, Yelp does not implement reviewer as an hCard,
- # per the spec
- reviewer = hreview.find(True, 'reviewer').text
- dtreviewed = hreview.find(True, 'dtreviewed').text
- rating = hreview.find(True, 'rating').find(True, 'value-title')['title']
- description = hreview.find(True, 'description').text
- item = hreview.find(True, 'item').text
- all_hreviews.append({
- 'reviewer': reviewer,
- 'dtreviewed': dtreviewed,
- 'rating': rating,
- 'description': description,
- })
- return all_hreviews
- reviews = parse_hreviews(url)
- # Do something interesting like plot out reviews over time
- # or mine the text in the descriptions...
- print json.dumps(reviews, indent=4)
截取的结果数据如示例2-10,它包括点评者,从hCard微格式结点中解析出来的。
示例2-10, 示例2-9的部分结果
- [
- {
- },
- "reviewer": "Nick L.",
- "description": "Probably the best Thai food in the metro area...",
- "dtreviewed": "4/27/2009",
- "rating": "5"
- ...truncated...
- ]
有无限的创新会发生,当你将极客和食物组合在一起,Cooking for Geeks这本书受欢迎就是个明证,也是O’Reilly出版的。随着美食网站改进并增加他们的API, 我们将在该领域有更多的创新。
总结
如果你无法记住本章中其它的东西,请记住微格式是一种方法来包装标注以暴露各种结构化数据,如菜单,联系人信息,人与人之间的关系。微格式有巨大的潜力因为它允许我们用已有的内容使得数据显式的且合适的达到预定的目标。期待到微格式在未来几个月中重大的增长。你也会吃惊于基于HTML5 microdata的创新随着HTML5市场份额的增加。如果你发现自己还有空余的时间,请看一下Google's social graph API, 它不同于Facebook的RDFa-based Open Graph protocol ,或是第9章中的Graph API,它包含XFN, FOAF和其它公开声明的联系的索引。