杨冬 516030910538
罗雨 516030910530
刘思辰 516030910528
常峰 516030910521
本组大作业的设计成果为,一针对电子产品品牌的搜索引擎(LOGO searcher)。主要功能为,输入目标品牌或电子产品的相关信息——包括品牌名称,品牌LOGO(图片),产品类别,产品名称,产品功能,品牌地区等——得到相关度较高的品牌信息和产品搜索结果。
从设计成果出发,目标产品应当拥有以下模块:
- 品牌信息爬取
- 文字信息索引搜索
- 图片信息搜索
- 用户界面
由如上展示可看出,我们的成果已基本实现设计目标,包括以品牌为基础的搜索,针对品牌logo的图片搜索,以产品类型为基础的搜索,以产品属性为特征的搜索。直接搜索“日本”,我们可以得到日本知名耳机品牌“JVC”的简介,同时下方还附有其主要热门产品的型号及其相关信息 产品仍然有许多不足,包括信息还可以进一步扩充,推荐排序不够完善等。
利用课堂上所学习的爬虫知识爬取以“中关村在线”为主的信息。
我们的产品针对的是电子电器相关的品牌信息。所以爬取策略应与之相对应,比如爬取的时候地址url符合re.compile('http://\w+.zol.com.cn/manu_\d+.*')
这个页面包含了某个品牌的某个类型的产品。相关的代码可以在crawler_thread.py
中获取。
在上一步爬取的html中我们可以获取品牌名称,品牌简短介绍,以及热门产品的部分信息。
但是这种url对应的页面排版不一致,需要分类讨论,获取品牌名称,logo图片地址的方法相同,但是获取热门产品及其相关信息的方法不同,所以需要分类讨论。所以在process_all.py
中有两个函数getPartialInfoFromType1()
以及getPartialInfoFromType2()
对应爬取两种不同网页的某些信息。
其中获取品牌对应的公司信息主要依靠百度百科的资源,但是品牌以及公司的名字千奇百怪,比如“苹果”就是一个多义词,我们可以通过内容中是否有“公司”、“企业”、“集团”等相关词汇来判断获取到的内容是否为所需。另外有一些品牌中英文混合,比如MSI微星,Acer宏碁,需要分隔后再爬去相关信息。相关代码可以在crawler_company.py
中获取。
在上一步中获取的热门产品信息存在严重缺失,由于这些只是来源于品牌页面,而不是产品的详细页面,所以图片或者价格缺失比较常见,所以在上一步的过程中,我记录了产品对应的详细页面的url,储存在prod_detail_url.txt
中,运行prod_img_price_thread.py
可以获取产品的大图,以及参考价格。以便在搜索结果页面展示。
最后运行mk_prod_info_dic.py
与makeup_missing.py
可以对原本的信息进行完善。
- 数据量相对于个人工作来说比较大,所以采取多线程操作。大致能减少2~3倍的时间。
- 爬取网页信息时随机获取ua,一定程度上减少被拒绝的概率。
- 如果被拒绝,无法获取页面,则将其暂时储存到
cannot_get.txt
然后再寻找原因,再对其中网页重新爬取。重复几次,直到其中的产品数目小于一定的值。 - 对于品牌的信息获取还不够全面,有些品牌的信息无法从百度百科获取,就只能一句话带过;相对应产品只有图片和价格,还可以获取更多的信息。
索引与搜索的analyzer:使用默认的SmartChineseAnalyzer。在建立索引中,由于SmartChineseAnalyzer效果不佳,故使用jieba分词以达成中文分词需求。在Searchfiles中仍使用SmartChineseAnalyzer,效果符合要求。
analyzer = SmartChineseAnalyzer(Version.LUCENE_CURRENT)
if len(tmp) > 4:
goods = '\n'.join(tmp[4 :])
for i in range(len(tmp)):
if i > 3:
tmp3 = tmp[i].split()
content.extend(jieba.cut(tmp3[1]))
content = ' '.join(content)
尝试使用lucene一般用文档加权手段setBoost(对Field),以减少对品牌名等重要信息的搜索的干扰,但效果不佳。改用暴力加权,即在搜索文档中多次添加品牌名和产品种类。
for i in range(20):
content.append(name1)
content.append(name2)
- 加权手段。
虽然一直在尝试对Field用setBoost,但由于时间不足(事前没有规划好)一直弄不好,最后不得不换用原本的暴力加权法。如果能用好自带加权手段,结果应该会更加精准。 - 排序
由于缺少热度、销量等讯息,不能达成很好的排序效果。
图片搜索部分主要由两部分构成,第一部分是利用我们在课程中学习的canny算法对图像进行边缘检测,第二部分是canny处理后的二值图像几何不变矩(Hu矩)的计算与匹配。
Canny算法在课程中已经涉及就不加赘述。
图片的Hu矩的计算基于以下定义:
图片的p+q阶矩和p+q阶中心矩
其中x,y分别为像素点的横纵坐标
这七个矩构成了一幅图片的特征向量,在匹配时有以下三种匹配度计算方法,经过比较,发现第三种的正确率相对更高,我也选择的这种。
- 由于我们组做的是关于logo的搜索,而logo又是人工制造的,相对自然图片更加简单,而且边缘非常清晰,所以我选择的是较为简单并且对边缘敏感的canny算法,然后对边缘图像进行相似度计算。
- 由于Hu矩有旋转伸缩平移不变性,在理论上应该有较好的效果。
实际上的效果不是很理想,主要有以下原因:抗噪声能力弱,非图片的边缘噪声会使得图片的矩发生变化。
- 算法对于图形性的logo识别能力较强,偏向于文字的偏弱。
- 计算出的7维向量有着不同的数量级,选择的匹配方法很重要,如果只是使用求距离的方法,最后相当于直接忽视其中部分数据的影响,而且网上能够找到的三个算法的准确度也不是很高,仍然有出入,这是结果不理想的主要原因,所以我们算法最主要的改进点就在找到一个合适的匹配算法。
文字部分主要使用了和前面中期整合差不多的方法,构建表单然后get,在主程序中运行文字检索函数,然后返回结果给模板即可。具体代码可在start.py
中获取。
图像检索中使用了POST的方式,首先将上传的图片保存在一个固定的位置,然后图像搜索的函数会固定搜索某个位置的图片,然后在整个函数运行后,删除掉保存的图片。以下为代码示例。
class img_res:
def POST(self):
x = web.input(myfile={})
filedir = './tmp'
if 'myfile' in x:
filepath = x.myfile.filename.replace('\\', '/')
# filename = filepath.split('/')[-1]
filename = 'test.jpg'
fout = open(filedir + '/' + filename, 'wb')
fout.write(x.myfile.file.read())
fout.close()
f = img_run()
res=[]
for i in f:
grand = open(unicode(('brands/'+i), "utf8"), 'r')
grand_list = grand.readlines()
x=[]
for k in range(len(grand_list)-7):
x.append(grand_list[k])
res.append(x)
for i in range(len(res)):
for j in range(len(res[i])):
if j == 0 or j > 3 :
res[i][j] = res[i][j].split('\t')
os.remove('tmp/test.jpg')
return render.img_res(res)
我们组在界面上确实花了一些时间,甚至找出一个人专门负责界面以及最后的整合,使得整个的大作业少了一些粗糙感,整体上看起来更为赏心悦目。
- 触摸反馈
及时地反馈用户的操作,对鼠标放置的尽可能多的元素都有反馈,点击可交互文字时文字都会有轻微的变化,只是这些变化都集中在颜色,亮度,饱和度等等这些不容易产生“动”感的动画。如下图所示 - 阴影和层级
通过不同的阴影卡片,不同的层级展示内容,而不是将内容一股脑儿全部扔出来,结果就是非常直观,简洁易懂。
整个网页的兼容性较差,如果遇到一些奇怪的内容,可能会出现排版问题。
本程序主要利用上课所学习的爬虫知识获取网络中的信息,利用pylucene建立索引,利用webpy搭建网站。在图片搜索方面在上课所学习的canny检测的基础上利用了图片的其他特性来进行图片索引。有一定的效果,但是由于规模比较小,数据信息不够索引建立的还不够全面,图片搜索效果还有待提高,在以后的学习中可以再加入机器学习的方法提高其辨识能力。
另一方面,组员之间相互合作,尽职尽责,每个人都功不可没。