- 范志康:1300012971,信科微电子系
- 王鹏飞:1300013007,信科电子系
- 温凯:1300063704,心理学系
- 曾繁辉:1300062702,信科电子系
- 范志康:配置服务器及API编写、部署,配置封闭测试搜索引擎,开放测试爬虫开发、信息预处理
- 王鹏飞:
- 温凯:
- 曾繁辉:
- solr搜索引擎及API:Apache 2.4.6,PHP 5.6.22,CentOS 阿里云服务器
- 开放测试信息处理:Objective-C,Xcode 8.2.1,macOS
- 封闭测试信息处理,结果抽取:Python 3,Win 10/ macOS
- 使用solr作为封闭测试维基数据的搜索引擎和数据源
- 使用PHP编写爬虫作为开放测试的数据源
- 为了方便协同开发,使用PHP编写了一个API(封闭测试和开放测试均使用此API进行查询),API部署于search.fanzhikang.cn/api/,API程序和文档在压缩包中
search
文件夹,也可见GitHub range=local
时仅使用solr系统返回的数据(来自课程提供的维基百科XML),即封闭测试range=online
时使用爬虫返回的数据,即开放测试
使用了solr搜索引擎,配置在阿里云服务器上,查询官方文档,构造XML导入数据:
<dataConfig>
<dataSource type="FileDataSource" encoding="UTF-8" />
<document>
<entity name="page"
processor="XPathEntityProcessor"
stream="true"
forEach="/mediawiki/page/"
url="/var/www/html/solr/data/zhwiki-20161101-pages-articles-multistream-simplified.xml"
transformer="RegexTransformer,DateFormatTransformer"
>
<field column="id" xpath="/mediawiki/page/id" />
<field column="title" xpath="/mediawiki/page/title" />
<field column="revision" xpath="/mediawiki/page/revision/id" />
<field column="user" xpath="/mediawiki/page/revision/contributor/username" />
<field column="userId" xpath="/mediawiki/page/revision/contributor/id" />
<field column="text" xpath="/mediawiki/page/revision/text" />
<field column="timestamp" xpath="/mediawiki/page/revision/timestamp" dateTimeFormat="yyyy-MM-dd'T'hh:mm:ss'Z'" />
<field column="$skipDoc" regex=".*?\{\{简繁重定向\}\}$" replaceWith="true" sourceColName="text"/>
</entity>
</document>
</dataConfig>
并配置schema,关键部分是加载并且配置中文分词器SmartChineseAnalyzer
,下面节选schema.xml
部分关键代码:
<schema name="wiki" version="1.6">
......
<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="title" type="string" indexed="true" stored="false"/>
<field name="revision" type="int" indexed="true" stored="true"/>
<field name="user" type="string" indexed="true" stored="true"/>
<field name="userId" type="int" indexed="true" stored="true"/>
<field name="text" type="text_cn" indexed="true" stored="true"/>
<field name="timestamp" type="date" indexed="true" stored="true"/>
<field name="titleText" type="text_cn" indexed="true" stored="true"/>
<uniqueKey>id</uniqueKey>
<copyField source="title" dest="titleText"/>
......
<dynamicField name="*_txt_cn" type="text_cn" indexed="true" stored="true"/>
<fieldType name="text_cn" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer" />
</fieldType>
</schema>
完整的一次导入在服务器(CPU:1核,内存:1024 MB)上大约需要8小时,其中有两点需要特殊处理:
- 导入之前将所有文本通过OpenCC转换为简体中文,消除简繁体共存带来的问题
- 因为经过了简繁体转换,因此维基百科中“简繁重定向”词条无需保留,导入过程中抛弃
配置好后的solr服务器运行于search.fanzhikang.cn,其名为“wiki”的核就是本次试验所使用的核(注意:请勿任意操作,该页面具有操作solr系统的所有权限)。三个需要更改的配置文件import-wiki-data.xml
,schema.xml
,solrconfig.xml
见压缩包solr
文件夹
使用的第三方库:
- OpenCC:用于原始文档简繁体转换
尽管solr本身提供了查询的http接口,但是为了方便查询以及清洗掉维基百科的一些特殊格式,我们使用PHP封装了一个更高层次的API
使用的第三方库:
- PHP-Wikipedia-Syntax-Parser:用于PHP解析wiki正文格式,抽取infobox等
爬取的数据主要来自以下四个渠道,(谷歌由于服务器访问受限所以没有使用)
- 百度搜索:用于尝试提取最佳答案
- 百度知道:用于返回答案语料
- 搜狗搜索:用于尝试提取最佳答案以及返回答案语料
- bing搜索:用于尝试提取最佳答案以及返回答案语料
对于某些特定问题,搜索引擎会直接返回最佳答案或观点作为醒目推荐,这种情况下通过提取特定HTML元素中的内容,可以直接返回,并且将其作为最终答案。下面给出几个例子:
- 百度搜索:儿童节在哪一天,世界上最高的山是什么,北京大学的邮编是多少
- 搜狗搜索:奥巴马是哪国人,苹果公司的客服电话是什么,冬泳下水之前饮用白酒可以御寒吗
- bing搜索:意大利的官方语言,北京大学的地址在哪里,10的平方是多少
其中需要特别注意的是,搜狗有一个“立知”系统(参考此新闻报道),其直接返回推荐答案的频率和效果都是最好的,但是经过大量的测试发现,有些“立知”返回的答案,其在网页上并不可见,但是源代码中是存在的。可能是搜狗认为这些答案置信度不够高因此将其隐藏,但经过测试比对,它们仍然有相当高的正确率,因此这些答案也被采用
- 例如,使用搜狗搜索哪个海峡沟通了北冰洋与太平洋时,有时候其结果并不会显示出来,但是查看网页源代码会发现有一个
class=txt-box
的div
元素,其CSS属性为display:none
,但是其中的内容正是该问题的正确答案“白令海峡”
除了尝试提取最佳答案外,根据搜索引擎返回的每条信息,提取其中的文本,作为语料交给下一步处理
使用的第三方库:
- Simple HTML Dom:用于PHP爬虫快捷操作HTML元素
使用了一个Objective-C工程,将问题批量查询API并储存结果。项目代码见压缩包OnlineQA
文件夹,抽取的结果见OnlineQA/OnlineQA/data
文件夹。在这个过程中做适量的预处理,一是尝试更换问法直接抽取正确答案,二是为后面的结果提取减轻压力:
- 对于特定形式的问题,有的时候过于复杂的问句搜索引擎无法直接返回推荐答案,而如果适当简化则可以。例如百度搜索李商隐的诗《锦瑟》中“庄生晓梦迷蝴蝶”的下一句是什么,但是将问题简化为“庄生晓梦迷蝴蝶”的下一句是什么就可以直接得到答案。因此对于一次查询未得到推荐答案的问题,做适量正则处理再次查询,尝试得到推荐答案。部分规则如下:
".*?“(.*?)”.*?((下|上|前|后)(一|两|半)?句)" => "$1 的$2",
".*?《(.|[^《]*?)》(是|出自).*?的.*?(歌|曲).*" => "“$1” 是谁唱的",
- 对于返回的结果,我们发现有许多问题来自于《一站到底》节目题库,返回的结果也都是与《一站到底》相关的信息。对于这些信息,尝试使用正则直接提取出正确答案
- 运行工程的过程中还遇到了一个问题,就是搜狗搜索对IP的访问频率有限制,超过一定频率则IP会被封禁1~2小时不等。因此设置了每次查询后线程随机休眠一段时间的解决方案,以及多部署几套API于不同IP,发现被封后轮流切换
- 预处理最终结果(见
OnlineQA/OnlineQA/data/answers-final.html
,标绿的为已知答案):搜索引擎推荐答案:1163个,一站到底题库确信匹配:2910个