搜索推荐技术在电商导购领域的应用(二):爬虫
文/高扬
什么是爬虫?
爬虫只是一种形象的比喻,不是树上爬来爬去的那种……爬虫是一种自动获取网页内容的程序,是搜索引擎的重要组成部分,是数据处理的第一个环节。大体上,可以有传统和垂直两种类型,传统的就是google、baidu大搜索爬虫,本篇介绍是的电商垂直爬虫。
做一个简单的爬虫很容易,你只要写下面这行代码:
wget //www.meituan.com/
完了……
看,很简单吧,大家都觉得写一个爬虫很简单,也预示着爬虫攻城湿苦逼日子的开始……
上面这行代码给学生讲讲课是够用的,但实战还需要解决很多问题,爬虫的基本工作有抓取、抽取、存储,我们分别说一说。
(一)抓取
1. 编码识别&翻页
互联网上的网页大多是http协议的,但编码每家都不一样,有utf-8,gbk,gb2312等等。如果无视编码问题,会使部分网页下载后是乱码,导致无法使用。
通常网页head标签内会标记编码,例如 搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术,但总有不靠谱的网站维护者不遵守标准,实际编码并不一定是utf8的。我们研发了一种通用技术解决方案:编码智能识别。我们内置了每个编码的常用字符集二进制码,然后将网页内容二进制化,找出匹配度最大的,作为这个网页的真正编码。这个方法很有效,99%+的准确率,就是稍微耗费点CPU,我们基本都用这个方法来识别网页编码,无视charset标签。
另外,很多电商商品页面,部分区域是ajax异步加载的,用POST的也比较多,直接使用wget、curl有诸多不便,需要支持。我们对其进行了整体封装,内部称这个类库为httpfetcher。
2. 智能的调度、更新
电商网页变化比较频繁,特别是商品价格字段,有时候几乎几分钟一变。上亿的商品库做到每个商品都能及时更新是很困难的,如何用有限资源,抓取最应该更新的商品,是一个难题。以商品更新为例,我们采用基于卖场场景的调度方式,不同的卖场场景更新频率不同,每一个爬虫负责特定场景的抓取任务,称作一个环。发现环负责粗粒度发现,更新环负责粗粒度更新,秒杀环负责限时抢购类的细粒度更新,如下图
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术3. 重复抓取规避
带宽,恩,带宽……一谈到带宽,爬虫湿都双眉紧锁。创业小公司,不是BAT那种不差钱的主,在北京这种地方带宽是很贵滴,如果能省下一点点带宽,那年终奖都出来了滴。gzip压缩这种优化标准套餐后,我们还需要解决重复网页抓取,节约带宽。
我们管理的网址是亿级别的,普通hashmap在内存中存不下,需要使用Bloom Filter了。
Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。
下面我们具体来看Bloom Filter是如何用位数组表示集合的。初始状态时,BloomFilter是一个包含m位的位数组,每一位都置为0。
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术为了表达S={x1, x2,…,xn}这样一个n个元素的集合,Bloom Filter使用k个相互独立的哈希函数(HashFunction),它们分别将集合中的每个元素映射到{1,…,m}的范围中。对任意一个元素x,第i个哈希函数映射的位置hi(x)就会被置为1(1≤i≤k)。注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。在下图中,k=3,且有两个哈希函数选中同一个位置(从左边数第五位)。
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术在判断y是否属于这个集合时,我们对y应用k次哈希函数,如果所有hi(y)的位置都是1(1≤i≤k),那么我们就认为y是集合中的元素,否则就认为y不是集合中的元素。下图中y1就不是集合中的元素。y2或者属于这个集合,或者刚好是一个falsepositive。
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术Bloom Filter的这种高效是有一定代价:
1.在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。不过对于爬虫场景,来判断一个网址是否被抓取过来说,这种误判率是可以接受的。
2. 无法unset。如果一个网页较早前被抓取过,因为某种原因想再抓一次,BloomFilter下无法重新清状态再抓取。我们在实践中,采用的是定期清空BloomFilter,在带宽浪费和unset之间做了一个平衡。
此外,抓取还要解决防封禁等。
(二)抽取
垂直爬虫所涉及的网页空间相对比较集中,对数据结构化要求较高,需要通过模板来解决。我们定义并使用了四种模板:URL模板,列表模板,商品模板和点评模板,这里简单说说前三种。
URL模板
1. URL归一化
商品URL可以标识一个唯一商品,在网站上同一个商品可能有不同的url,原因可能多方面的,一是入口不一样,列表页,搜索页点击过去的页面是不一样的,二是URL后面经常带有统计信息。这样会导致库里大量的商品重复以及BloomFilter失效,所以需要采用某种规则,将这些URL转化为同一类型的URL,被我们成为URL归一化。
2. URL特征
制定不同的规则来判定是列表url还是商品url,方便采取不同的抓取策略。列举一些URL模板例子:
商品URL模板
www.jd.com|^/product/[0-9]+.html.*$
book.jd.com|^/[0-9]+.html.*$
列表模板
表明当前的为一个列表模板,需要通过模板获取的信息为商品总页数
一个列表链接意味着这个链接上面都是一个跳出的链接,通过获取所有的链接,然后与当前网站商品的URL特征匹配,可以获得纯净的商品URL链接,从而控制了发现的链接的准确性。
当一个页面可以翻页的时候,种子URL模板如下
//www.jd.com/products/652-828-1107-0-0-0-0-0-0-0-1-5-[xx].html
爬虫收到这种类型的链接,按照初始默认起始页,步长爬起第一个页面。
//www.jd.com/products/652-828-1107-0-0-0-0-0-0-0-1-5-1.html。爬取完毕以后,通过列表模板获得商品总页数,写入到链接对象持久化。然后将这个链接新发现的商品链接和自身写入URLDB。当爬虫在次从URLDB取到这个链接时,发现这个链接是可以翻页的连接,于是按照当前的pageIndex,pageStep计算出下一个页面。然后爬取下一个页面。
商品模板
早期商品模板主要使用Xpath正则、JS子模板及自定义的表达式来完成商品信息的解析。Xpath正则很常见,自行百度。JS子模板主要用来解决一些技术的问题
1,实现在解析某个页面时调用其子页面(ajax),因为有时某些商品信息(如点评量),在商品页的html代码中是不存在的,需要调用该商品页的子页面(如点评页)才能获取;
2,实现从页面中抽取内容传递给某个中间变量(虚拟的模板项),因为有时某个最终结果需要同时对两个中间结果做处理才能得到,需要支持创建多个中间变量。
JS子模板这个名字不好,最初未来解决javascript带来的问题,后面也就懒得改了。随着技术的迭代优化,逐渐使用一种自研的脚本语言(内部代号,behemoth)来简化模板抽取工作。鉴于脚本语言的灵活性,behemoth几乎能做到任何程度的处理,可用来抽取商品SKU,点评等。目前我们正逐渐替换成behemoth。
behemoth极大简化了模板编写工作,隐藏诸多技术信息(例如常用的xpath、正则封装),让模板编写者只关注业务逻辑。此外,大多数电商网页,都存在着盘根交错的ajax调用,behemoth先执行整个网页的dom渲染后再进行抽取工作,每个抽取模板是一个codeunit,极大降低了模板编写复杂度。
behemoth也是有代价的,由于大量的渲染工作,抽取一个网页的时间是之前的几倍。总体上,好处还是大于坏处。
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术 搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术这是我们早期一些模板的示例。
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术这是目前模板的演化现状。
对了,还有一个问题,模板这么多,如果模板失效了怎么检测呢?我们为此研发了一个自动检查机器人,将有问题的模板定期挑出来,让人工修改更新。这种架构,维护几千家网站不成问题。
此外,我们正在研发第三代抽取技术,模板工作将会进一步再减少50%+。
(三)存储
需要管理的网址链接是百亿级,商品是10亿级,为了方便分析和读取,需要支持随机写和顺序写的存储系统,关系型数据无法满足我们的需求,需要no-sql型。早期我们自研了一套文件系统——ministore,随着数据量的增长,维护的成本比较高,中期我们且换到了Cassandra,到现在我们使用改造后的Hadoop+redis。有关Cassandra、Redis、Hadoop文章很多,两个系统各有特点,这里不展开说。
小结
搜索引擎爬虫 电商搜索优化 电商搜索引擎 搜索技术要写好一个爬虫要干这么多活,绝对是一个脏活累活有木有,堪称技术界的活雷锋有木有。
一个优秀的爬虫对搜索引擎发挥着极其重要的作用,它是核心数据的源头,处理的越好,对后续的处理帮助越大。