李健
(战略支援部队信息工程大学,洛阳471003)
网络爬虫是按照一定规则自动获取Web信息资源的计算机程序[1-2]。根据目标资源的位置不同,网络爬虫可以分为浅层爬虫和深层爬虫。通过超链接能够直接到达的为浅层数据,需要用户登录、提交表单、异步加载等操作才能获得的为深层数据[3-4]。研究发现,Web中的深层数据量远远超过浅层数据[5-6],因此深层爬虫就显得十分重要。目前,异步加载技术广泛使用,这给网络爬虫的开发带来一些困难。对此,可采用模拟浏览器的方法进行采集——让浏览器内核去处理那些复杂的技术细节,爬虫只需要模拟用户操作,等待目标数据返回[7-9]。在爬取过程中,可通过DOM路径实现对元素的定位和数据的抽取[10-12]。
随着人工智能技术的发展,机器翻译的准确率也不断提高,很多互联网公司(如谷歌、百度、微软等)都提供了在线翻译服务。对于普通用户来说,网页翻译是最主要的服务形式,而且是完全免费的。网页翻译虽然免费,但是往往会限制单次翻译的字数。对于少量翻译任务,我们可将原文复制到翻译页面就可以获取翻译结果;但对于较大规模的翻译任务,若仍采用手动方式(逐段复制粘贴)则显得十分低效。对此,可以采用“多次少取”的方式解决大规模语料的自动翻译问题。本文将设计并实现一个基于Gecko浏览器内核的翻译爬虫——借助“谷歌翻译页面”实现自动批量翻译。
网页浏览器(Web Browser)简称浏览器,是一种用于检索并展示万维网信息资源的应用程序。用户所看到的网页都是经过浏览器解析、渲染后呈现出的结果,而并非原始网页数据。浏览器的核心功能就是解析网页,解析对象主要包括HTML、CSS和JavaScript,分别对应网页的内容、样式和行为。浏览器解析网页的基本过程如图1所示[13]。
图1 浏览器解析网页的过程
资源加载后,浏览器会将HTML数据解析成DOM树,将CSS数据解析成CSS规则树,还可以通过执行JavaScript代码对它们进行操作。解析完成以上对象,浏览器引擎通过DOM树和CSS规则树来构造渲染树(Render Tree),结合其他资源最终生成页面展示效果。
浏览器内核是指浏览器的核心部件,主要包括页面渲染器和JS解析器。页面渲染器负责把数据转换为用户在屏幕所看到的样式,JS解析器负责解释和执行网页中的JS代码[14]。表1列出了常见浏览器内核[15-18]。
表1 常见浏览器内核
Trident是由微软公司开发的浏览器内核,随Internet Explorer 4.0首次发布(也称IE内核)。Trident目前仍然是主流的浏览器内核之一,并被广范应用于其他非IE浏览器。WebKit是由苹果公司开发维护的开源浏览器内核,所包含的WebCore引擎和JSCore引擎都是从自由软件衍生而来。Chrome和Opera浏览器早期也曾采用WebKit内核,由于某些原因Google公司从WebKit中分支出自己的Blink内核,随后Opera公司也宣布将转向Blink内核。Gecko是一个能够跨平台使用开源项目,该内核最早由Netscape公司开发,现在由Mozilla基金会维护[19]。
GeckoFx是对Gecko内核的.NET封装,提供完善的编程接口,这使得.NET程序员可以在WinForm或WFP程序中方便地使用Gecko内核。在Visual Studio中通过NuGet包管理器可直接安装GeckoFx。
在DOM标准下,HTML文档中的每个成分都是节点:大到整个HTML文档,小到每个HTML标签,甚至底层的纯文本都被看作一个结点[20]。GeckoFx核心类之间的关系如图2所示。其中实线表示继承关系,虚线表示包含关系。
图2 GeckoFx核心类
在GeckoFx框架中,GeckoNode表示所有DOM结点的基类,GeckoDomDocument用于描述DOM文档,GeckoDocument用于描述Html文档,Gecko Element用于描述DOM元素,Gecko Html Element用于描述HTML标签元素。Gecko Web Browser是一个Web浏览器控件(可直接显示在WinForm窗体中),其DomDocument和Document属性分别属于GeckoDomDocument和Gecko Document类型。通过上述对象,可以实现页面的加载和导航,元素的查询和修改。
为了便于描述,我们将任务简化为对中文词表的翻译,每次提交一个词条进行翻译,翻译完成后可导出双语词表。并具体规定如下:中文词表按行存放于文本文件中(对应全部翻译任务),每次提交一行文本进行翻译(对应单次翻译任务),翻译结果以Excel格式导出。翻译爬虫的总体架构如图3所示。
图3 翻译爬虫架构
根据上述思路,爬虫工作流程如图4所示:首先使用浏览器控件加载翻译页面;然后提示用户选择并导入中文词表;每次从待翻译词表中取出一个词条,复制到翻译网页的原文输入框,等待翻译结果返回,从译文输出框读取结果;若翻译任务全部完成则导出结果,否则继续翻译下一词条。
图4 翻译爬虫工作流程
爬虫界面如图5所示:使用分隔容器(SplitContain⁃er)将主窗体分为左右两个区域,左侧为用户操作区,包括两个按钮(导入、导出)和一个DataGridView控件;右侧是翻译页面加载区,GeckoWebBrowser控件充满整个区域。
图5 翻译爬虫主界面
我们在.NET平台下使用C#语言编写程序,实现了谷歌翻译爬虫的全部功能。下面将介绍关键模块的实现。
爬虫启动后,首先需要初始化Gecko运行环境,才能使用GeckoWebBrowser控件加载页面。其主要代码如下:
上述代码定义了一个GeckoWebBrowser类型的成员变量(browser),表示Gecko浏览器控件;页面跳转后为浏览器控件添加Document Completed事件,以保证网页加载完毕才能导入词表。
翻译爬虫的关键步骤就是要模拟用户操作,在浏览器页面中完成原文的输入和译文的读取。通过Firefox开发者工具箱查看页面元素(如图6图所示),可以发现Google翻译页面的原文输入框为一个
图6 查看网页元素(输入框)
同理,我们也可以找出“译文输出框”的定位:一个class属性值为“tlid-translation translation”的元素。调用Gecko Dom Document.Get Elements By Class⁃Name()方法可获取具有该class属性值的元素列表。测试发现:该网页中具有上述class属性值的元素是唯一的(仅表示输入框),这将使程序处理变得简单。“写入原文”和“读取译文”的主要代码如下:
本研究发现,石河子大学本科生学习动机居中等程度。经过对数据的进一步分析发现,总平均分小于临界的比例相当大,共96人,即有52.17%的大学生学习动机水平不高;总平均分高于4分的(学习动机较强)仅4人,占总人数的2.17%。从总体上看,本研究证实了当今大学生学习动机偏低,只有在能力追求维度上得分接近4分,其余均不到3分。因此,需要在教育教学中重视石河子大学本科生的学习动机。
在上述代码中,Translate函数被声明为async(异步的),并在函数中使用了await语句。借助await语句可采用同步编程风格实现异步功能,当程序执行到await语句时并不会引起中主线程(UI线程)的阻塞,而是将之后的代码动态封装成一个回调函数,待任务结束后自动调用。这样既能控制翻译任务的执行步骤,又不会造成窗体假死(无法响应用户操作)。需要说明的是,网页结构并非固定不变,若谷歌翻译页面改版,则需要重新定位输入、输出框位置。
当点击“导入中文词表”按钮时,会提示用户选择词表文件,并将其读入列表中;然后调用BatchTranslate方法完成批量翻译,原文列表将作为参数传入。主要代码如下:
在爬虫程序中,我们借助一个bool类型的变量(stopTag)来控制翻译任务的启停。初始状态下stop⁃Tag默认为false(表示不停止),按钮文本为“导入中文词表”;若此时按下按钮则启动翻译任务,并将按钮文本改变为“停止”,任务完成后文本自动恢复;若用户在任务执行过程中按下“停止”按钮,stopTag将被置为true(表示停止),程序检测到stopTag的变化则停止当前任务。由于在BatchTranslate方法中使用了await语句等待翻译结果,因此该方法也被声明为async。批量翻译的执行过程如图7所示。
图7 程序运行效果
待全部翻译任务完成后,单击“导出双语词表”按钮可将翻译结果以Excel格式导出,具体实现代码这里不再列出。
分析发现,爬虫执行过程中最耗时的步骤就是等待翻译结果,而且每次翻译需要等待的时间并不固定,这受原文长度、网络条件、服务器负载等因素的影响。因此,设置一个适当的等待时间十分重要:若时间太短则翻译尚未完成,若时间太长则影响爬虫效率。此前的程序每次固定等待2秒,一般情况下这个时间足够长但效率偏低。我们希望能够在译文返回后尽快读取结果,在改进方案中采用“轮询检测法”判断翻译结果是否返回。改进后的代码如下:
上述代码中的Clear Dest Text方法用于清空译文(具体代码从略),在写入原文之前先调用此函数,以避免上次翻译结果干扰到本次判断。为验证“轮询检测法”的性能,我们将其与“定时等待法”进行对比测试。
图8 “定时等待法”性能统计
对于“轮询检测法”,我们统计了采用不同“轮询间隔”对程序性能的影响(如图9所示)。统计结果表明:此方法总能保证100%的“采准率”,平均翻译时长也明显优于“定时等待法”。同时,我们发现轮询间隔并非越小越好(设定在0.1秒左右较为合适),因为间隔时间越小就意味着轮询次数越多,而轮询本身也需要消耗系统资源。
图9 “间隔轮询法”性能统计
为了进一步说明“定时等待法”难以兼顾准度和效率,我们采用“间隔轮询”法对同一组词条(共13个)进行5次翻译测试,并将用时分布情况绘制成“箱线图”(如图10所示)。
图10 翻译等待时间分布
统计结果显示:虽然平均翻译时间都在1秒左右,但每次总有几个词条偏离平均值较远。翻译等待时间的不稳定性正是“定时等待法”效率不高原因。
本文所介绍的“谷歌翻译爬虫”不仅实现了既定功能,达到了预期效果;而且进行了算法优化,提高了采集效率。测试表明:本文所提出的方案是行之有效的,可以为同类爬虫的开发提供技术参考。笔者在项目实践中发现,越来越多的网页采用异步加载技术,传统的网络爬虫难以发挥作用。模拟浏览器的方式可以屏蔽内部技术细节,减化爬虫开发的复杂度。其中,如何模拟用户操作(如输入文本、选择列表、点击按钮、滚动页面等),是实现爬虫功能的基础;如何判断目标数据返回,是提高爬虫效率的关键。