记得2010年10月9号,淘宝全网搜索引擎一淘网上线,当时不怎么关注,只是在网站上看到过新闻而己,前两个月,觉得是时候走确定自己以后要走的方向了,于是决定以后加入到搜索的行列中,此时开始关注一淘网的技术,来打开搜索的大门。

那么做搜索引擎要做哪些内容呢,以前有人也这样问过nutch,lucene,hadoop之父Doug Cutting,他回答大致应该分为以下几部分:

1)  攫取(fetching):就是把被指向的网页下载下来。通常就是日常所说的网络爬虫的工作。

2)  数据库:保存攫取的网页信息,比如那些网页已经被攫取,什么时候被攫取的以及他们又有哪些链接的网页等等。

3)  链接分析:对刚才数据库的信息进行分析,给每个网页加上一些权值(比如PageRank,WebRank什么的),以便对每个网页的重要性有所估计。不过,在我看来,索引那些网页标记(Anchor)里面的内容更为重要。(这也是为什么诸如Google Bombing如此高效的原因

4) 索引(Indexing): 就是对攫取的网页内容,以及链入链接,链接分析权值等信息进行索引以便迅速查询。

5) 搜索(Searching): 就是通过一个索引进行查询然后按照网页排名显示

  根据这几部分,我们可以自己写一个简单的购物搜索引擎。

  第一步:抓取数据

  目前开源的网络爬虫有很多,nutch,heritrix都可以算是其中的佼佼者。这里我们先自己动手写一个简单的。

  仔细看淘宝的网站,会发现,基本上商品信息比较全的页面全都是以item.taobao开头的,那我们用chrome的开发人员工具分析一下页面结构。

如下图:

图片内容为:<div id="J_itemViewed" catid="1512" data-value="{"itemId":"10810071223","xid":"","pic":"i3/T1EJahXm4kXXb3Ses._112056.jpg","price":"334800","itemIdStr":"","title":"HTC S510e/Desire S g12 安卓2.3系统 大陆行货 联保 现货"}"></div>  

  我们可以看到,在这个名为J_itemViewed的div中,我们可以取到商品的三个最基本的信息,商品名称,商品价格,商品图片地址。那么现在的目标很明确,就是在抓取item.taobao的网页,然后找到id为J_itemViewed的div标签,取出里面的商品信息。那么动手吧。

  我们写一个Crawler的类,这个类要做以下几件事:

  1.提取网页链接

  2.碰到包含有item.taobao的链接,分析网页内容,提取信息,将商品信息存储入本地

  首先来看提取网页链接,如下图所示:

  

  提取网页上的链接一般是从一种初始网页开始了,比如说我从www.taobao.com开始,然后分析网页的结构,取得里面的所包含的链接,然后根据这个链接,去提取更深层次的链接。如此循环,到一定条件停止。在抓取网页的时候,网络蜘蛛一般有两种策略:广度优先和深度优先。广度优先是指网络蜘蛛会先抓取起始网页中链接的所有网页,然后再选择其中的一个链接网页,继续抓取在此网页中链接的所有网页。这是最常用的方式,因为这个方法可以让网络蜘蛛并行处理,提高其抓取速度。深度优先是指网络蜘蛛会从起始页开始,一个链接一个链接跟踪下去,处理完这条线路之后再转入下一个起始页,继续跟踪链接。对于垂直搜索来说,通常采用广度优先策略。下面简单列出抓取与提供信息的方法,具体的看附近里的源程序。 

GetUrlThread.class(链接抓取)

链接抓取

/**     * 搜集网页上的链接     *      * @param pageurl     * @throws ParserException     * @throws IOException*/public void retrieveLink(String url) throws ParserException, IOException {        URL pageurl = new URL(url);        Node node = null;        Lexer lexer = new Lexer(pageurl.openConnection());while (null != (node = lexer.nextNode())) {if (node instanceof TagNode) {//提取a标签                if (((TagNode) node).getTagName().toLowerCase().equals("a")) {//得到href地址                    String link = ((TagNode) node).getAttribute("href");if (link != null && !"".equals(link)) {//遇到空地址解析下一个                        if (link.length() < 1) {continue;                        }//遇到#地址解析下一个                        if (link.charAt(0) == '#') {continue;                        }//遇到邮件地址解析下一个                        if (link.indexOf("mailto:") != -1) {continue;                        }//遇到js方法解析下一个                        if (link.toLowerCase().indexOf("javascript") != -1) {continue;                        }// 处理相对路径,地址中没有://就说明为相对路戏                        if (link.indexOf("://") == -1) {if (link.charAt(0) == '/') {                                link = "http://" + pageurl.getHost() + link;                            } else {                                String file = pageurl.getFile();if (file.indexOf("/") == -1) {                                    link = "http://" + pageurl.getHost() + "/"+ link;                                } else {                                    String path = link.substring(0, link                                            .lastIndexOf("/") + 1);                                    link = "http://" + pageurl.getHost() + path+ link;                                }                            }                        }

int index = link.indexOf("#");if (index != -1) {                            link = link.substring(0, index).toLowerCase();                        }//碰到包含有item.taobao的地址,记录下来待搜集商品数据                        if (link.contains("item.taobao")) {if (!CommonData.urlInfo.contains(link)&& !CommonData.urlInfoed.contains(link)) {                                logger.info("找到记录:" + link);                                CommonData.urlInfo.add(link);                            }                        }//将搜索到的信息放到内存中                        if (!CommonData.urlCollection.contains(link)&& !CommonData.urlVisited.contains(link)) {                            System.out.println("link:" + link);                            CommonData.urlCollection.add(link);                            CommonData.linkcount++;//记录已经搜集到了多少链接                        }

                    }                }            }        }        CommonData.urlVisited.add(url);        CommonData.urlCollection.remove(url);    }

  

GetInfo.class(信息提取)

信息提取

/**     * 提取信息     *      * @param url     * @return     * @throws ParserException*/private void retrieveInfo(String url) throws ParserException,            IllegalArgumentException, IOException {        URL myurl = new URL(url);        Node node = null;        Lexer lexer = new Lexer(myurl.openConnection());while (null != (node = lexer.nextNode())) {if (node instanceof TagNode) {//找到id为J_itemViewed的结点,提取其value值                if ("J_itemViewed".equalsIgnoreCase(((TagNode) node)                        .getAttribute("id"))) {                    String content = "url:"+url+"\r\n"+"data:"+((TagNode) node).getAttribute("data-value")+"\r\n";                    FileOut.instance().write(content);                }            }        }        CommonData.urlInfoed.add(url);//添加到已经提取过的集合中        CommonData.urlInfo.remove(url);//从原集合中移去    }

  

  看一下代码:主要是采用htmlparser,一个html文档解析器来解析下载的内容,得到网页中的<a>标签,去掉邮件,javascript,空锚点等,将提取出来的链接加入到集合中,并将包含有item.taobao的链接加入到要提取网页信息的集合中。

  从要提取网页信息的集合中取得url,提取所需要的id为J_itemViewed的div的value值。并将其持久化到本地磁盘。我这里采用的是每一分钟写一个文件夹,每一秒写一下文件。得到的文件内容如下所示:具体看附件)

  url:http://item.taobao.com/item.htm?id=7520167497

  data:{"itemId":"7520167497","xid":"","pic":"i4/T1ZM86XoFpXXXfrvg2_043733.jpg","price":"279900","itemIdStr":"","title":"Konka/康佳 LC32IS68N 32寸液晶电视 高清网络电视机 USB/HDMI"}

第二步:建立索引
我们已经得到所需要的内容了,并将其保存在本地的硬盘上。接下来就是要对这些内容做索引了,说到索引,就不得不提lucene了,下面我们使用开源框架lucene来对刚刚提取出来的文件进行索引各搜索。
先介绍一下lucene。
简单的说呢,lucene就是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。
我们先来看一下lucene索引的过程

 如上图所示,lucene建立索引的步骤大概可以分为以下几步:

  将内容信息存入在Field中,将多个Filed组合成Document,然后经过分词,由IndexWriter写入到目录中。

  简单介绍一下这几个类,因为看lucene in action自己翻译过来的,可能会理解的不太准确,见谅。

  1.IndexWriter

  indexWriter 是创建索引的核心组件。它可以创建一个索引,或者打开一个已经存在的索引,也可以添加,删除,更新索引中的文档。indexWriter拥有索引的写的权限,却没有读与查询的权限。IndexWriter需要在某处来存储这些索引,而这个存储的地方就是Directory。

  2.Directory

  Directory类代表Lucene索引存储的地方。它是一个抽象类,其子类可以存储合适的索引。在我们的indexer例子中,我们使用FSDirectory.open来得到合适的具体实现FSDirectory来存储目录下的文件,然后将其传给IndexWriter的构造方法。

Lucene包含了若干个有趣的Directory实现。IndexWriter要想创建索引,则必须通过AnalyZer来解析内容。

3.Analyzer

在文本被索引之前,它必须通过analyzer分析。在IndexWriter的构造方法中,声明了Analyzer,analyzer是负责利用将要被索引的文字来抽取标识符并且消除剩余的。如果内容被索引的部分不是最简单的部分,需要先将里面的内容的简单字符抽取出来,再索引。

Analyzer是一个抽象类,但是Lucene自带一些它的实现.

Analyzer是Lucene非常重要的类,它常用于简单的过滤文本。开发者要想反lucene集成到产品中,需要正确的选择analyzer。

要想分析就需要文档,即Document,document中包含单独的field用来创建索引。

4.Document

Document类是fields的集合。其可以是web网页,email或者单纯的文本。

5.Field

每一个索引中的document都包含有一个或者多个命名的字Filed。每一个Field都有一个名字与对应的值。

  看一下建立索引的代码:(具体的过程看源代码中的Index.class)

建立索引

public Index() {        Analyzer analyzer = new IKAnalyzer();// 中文分词分析        File isfirstfile = new File(CommonData.isfirst);//是否为第一次启动,如果是第一次启动的话,索引采用添加而不是更新,如果不是话的,索引为合并更新        BufferedReader filereader;// 找到上次读到的文件        try {            filereader = new BufferedReader(new InputStreamReader(new FileInputStream(isfirstfile)));int startcount = Integer.parseInt(filereader.readLine());            Directory directory = FSDirectory.open(new File(CommonData.index));// 由是不是第一次启动来确定是true还是false,来确定索引是添加还是合并更新            writer = new IndexWriter(directory, analyzer,                    startcount > 0 ? false : true,                    IndexWriter.MaxFieldLength.UNLIMITED);            BufferedWriter writer = new BufferedWriter(new FileWriter(                    isfirstfile));            writer.write(startcount + 1 + "");//启动项加1            writer.flush();        } catch (Exception e) {            e.printStackTrace();        }    }

  

上图为建立完索引后生成的文件

   第三步:搜索

    建立好索引之后,我们就可以检索所需要的信息了,还是利用lucene,代码挺简单的:

搜索

/** * 信息搜索 * @author xiaoruoen **/public class Search {private Logger logger = Logger.getLogger(Search.class);public void search(String name){try {//打开索引存放的目录            Directory dic = FSDirectory.open(new File(CommonData.index));//创建索引搜索            IndexSearcher search = new IndexSearcher(dic);//创建中文分词            Analyzer analyzer = new IKAnalyzer();//开始搜索            QueryParser parser = new QueryParser(Version.LUCENE_32,"title",analyzer);            Query query = parser.parse(name);            TopDocs hits = search.search(query,100);//显示搜索结果            logger.info("共找到"+hits.totalHits+"条记录");            logger.info("=====================================================");for(ScoreDoc scoreDoc:hits.scoreDocs){                Document doc = search.doc(scoreDoc.doc);                logger.info("名称:"+doc.get("title"));                logger.info("价格:"+doc.get("price"));                logger.info("图片:"+doc.get("pic"));                logger.info("网址:"+doc.get("url"));            }        } catch (IOException e) {            e.printStackTrace();        } catch (ParseException e) {            e.printStackTrace();        }    }}

   我们搜索一下“女装”,看一下出来的结果:

  

INFO (2011-09-14 10:39:15,593) - 共找到63条记录INFO (2011-09-14 10:39:15,593) - =====================================================INFO (2011-09-14 10:39:15,625) - 名称:CIMARRON 堆堆领 女装 长袖 舒适显瘦 针织衫INFO (2011-09-14 10:39:15,625) - 价格:3200INFO (2011-09-14 10:39:15,625) - 图片:i4/T1sxtSXddGXXachFs3_045631.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=12267518050INFO (2011-09-14 10:39:15,625) - 名称:2011秋冬装新品 修身风衣双排扣 韩版女装风衣 女装中长款外套INFO (2011-09-14 10:39:15,625) - 价格:19000INFO (2011-09-14 10:39:15,625) - 图片:i3/T1sPxWXdhNXXXSNBQ3_050815.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=12523597229INFO (2011-09-14 10:39:15,625) - 名称:秋装新品2011时尚瑞丽新款女装日韩服饰9482 修身仿皮短外套INFO (2011-09-14 10:39:15,625) - 价格:6300INFO (2011-09-14 10:39:15,625) - 图片:i8/T1QSCkXcdoXXX5S5s4_053319.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=12488194244INFO (2011-09-14 10:39:15,625) - 名称:2011韩莉雅 秋装新款女装 时尚休闲长袖短外套HLY1131INFO (2011-09-14 10:39:15,625) - 价格:18800INFO (2011-09-14 10:39:15,625) - 图片:i7/T1rOSlXoBqXXaFtvc1_041102.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=13018744492INFO (2011-09-14 10:39:15,625) - 名称:2011秋装新款专柜正品女装时尚休闲翻领绣花 短外套 军绿色/咖啡INFO (2011-09-14 10:39:15,625) - 价格:26200INFO (2011-09-14 10:39:15,625) - 图片:i1/T1liqlXj8oXXXTRSAT_013057.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=12730707166INFO (2011-09-14 10:39:15,625) - 名称:水墨佳人女装2011秋装秋款新款新品加厚长袖瑞丽短外套INFO (2011-09-14 10:39:15,625) - 价格:26200INFO (2011-09-14 10:39:15,625) - 图片:i1/T1sTV_XadqXXcClX36_060517.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=12915156606INFO (2011-09-14 10:39:15,625) - 名称:ViVi杂志款 Asuka  Yuri秀粗麻女装秋冬短外套INFO (2011-09-14 10:39:15,625) - 价格:17900INFO (2011-09-14 10:39:15,625) - 图片:i2/T1e2hOXddzXXbw55cZ_032608.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=7761546704INFO (2011-09-14 10:39:15,625) - 名称:2011秋季新款女装羊毛呢妈妈装 宽松版中老年短外套翻领绿色INFO (2011-09-14 10:39:15,625) - 价格:22900INFO (2011-09-14 10:39:15,625) - 图片:i3/T1ZB40XopXXXckB9E3_051216.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=9290407443INFO (2011-09-14 10:39:15,625) - 名称:2011韩版秋冬女装新品 洋气 调皮可爱中长款双排扣风衣 外套INFO (2011-09-14 10:39:15,625) - 价格:8800INFO (2011-09-14 10:39:15,625) - 图片:i7/T1KpuaXm4sXXXmcEQZ_033735.jpgINFO (2011-09-14 10:39:15,625) - 网址:http://item.taobao.com/item.htm?id=13145728284INFO (2011-09-14 10:39:15,625) - 名称:新款韩版女装风衣外套 修身长袖女士风衣 翻领带拉链风衣外套INFO (2011-09-14 10:39:15,640) - 价格:15900INFO (2011-09-14 10:39:15,640) - 图片:i4/T1SPqkXmJjXXcjLEk8_072117.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=12812355549INFO (2011-09-14 10:39:15,640) - 名称:2011秋装新款女装军绿色工装休闲外套春秋韩版修身女士风衣INFO (2011-09-14 10:39:15,640) - 价格:19800INFO (2011-09-14 10:39:15,640) - 图片:i1/T17smlXb0FXXc.0vwZ_031951.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=13031860478INFO (2011-09-14 10:39:15,640) - 名称:2011春秋装新品 韩版女装风衣外套 长款修身风衣 女士外套大衣INFO (2011-09-14 10:39:15,640) - 价格:15900INFO (2011-09-14 10:39:15,640) - 图片:i4/T1WOp.Xh8HXXasdJIZ_032024.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=13031892105INFO (2011-09-14 10:39:15,640) - 名称:专柜正品女装2011新款春装韩版时尚假俩件带帽风衣INFO (2011-09-14 10:39:15,640) - 价格:26200INFO (2011-09-14 10:39:15,640) - 图片:i7/T1zjNYXgtkXXbyJ9g7_065620.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=9100442835INFO (2011-09-14 10:39:15,640) - 名称:特价 风衣 女 2011新款秋装韩版大码女装时尚热卖修身风衣INFO (2011-09-14 10:39:15,640) - 价格:13600INFO (2011-09-14 10:39:15,640) - 图片:i4/T1y6ajXktkXXXw6vM8_070426.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=12525276117INFO (2011-09-14 10:39:15,640) - 名称:枫之玲 2011秋装新款 修身 时尚 长袖女装风衣INFO (2011-09-14 10:39:15,640) - 价格:20000INFO (2011-09-14 10:39:15,640) - 图片:i8/T10d05XodEXXX8fYo4_052120.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=13145948058INFO (2011-09-14 10:39:15,640) - 名称:以诺 2011秋装 新款 女士 女装 长款 修身 风衣INFO (2011-09-14 10:39:15,640) - 价格:17800INFO (2011-09-14 10:39:15,640) - 图片:i1/T1Tmx7XjhEXXbLx7w._113135.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=12637146864INFO (2011-09-14 10:39:15,640) - 名称:2011夏装新品欧美女装单件套圆领短袖蝙蝠袖 全棉针织衫INFO (2011-09-14 10:39:15,640) - 价格:3990INFO (2011-09-14 10:39:15,640) - 图片:i2/T1feX4XjpkXXaI_Fjb_122628.jpgINFO (2011-09-14 10:39:15,640) - 网址:http://item.taobao.com/item.htm?id=9674621798

  这里只是简单的打印出来,有兴趣的可以弄个web,将结果显示出来。

好了,已经初步构建好一个最简单的购物搜索擎了,由于个人水平原因,可能代码写的有点……,但不管怎么说,是一个开始,接下来会慢慢完善的。

源代码下载

程序运行所需文件与搜集到的内容

  

转载于:https://www.cnblogs.com/xiaoruoen/archive/2011/09/14/2163882.html

构建自己的购物搜索引擎一:写一个简单的相关推荐

  1. 如何写一个简单的node.js C 扩展

    node 是由 c 编写的,核心的 node 模块也都是由 c 代码来实现,所以同样 node 也开放了让使用者编写 c 扩展来实现一些操作的窗口. 如果大家对于 require 函数的描述还有印象的 ...

  2. 怎样写一个简单的操作系统?(原文标题:How to write a simple operating system) 分类: 翻译 2011-01-26 01:10 3175人阅读 评论(3) 收藏

    怎样写一个简单的操作系统?(原文标题:How to write a simple operating system) 分类: 翻译2011-01-26 01:10 3175人阅读 评论(3) 收藏 举 ...

  3. 12.写一个简单的发布器与订阅器(C++)

    写一个简单的发布器与订阅器(C++)(rawmeat:http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber(c%2B%2B)) 注意 ...

  4. 用Qt写一个简单的音乐播放器(四):歌曲浏览、上一曲、下一曲

    一.前言 在用Qt写一个简单的音乐播放器(一):使用QMediaPlayer播放音乐中,我们已经知道如何去使用QMediaPlayer播放音乐. 在用Qt写一个简单的音乐播放器(二):增加界面(开始和 ...

  5. 用java做一个简单记事本_用记事本写一个简单的java程序

    用记事本写一个简单的java程序 第一步: 安装好jdk,并设置好环境变量. 桌面-计算机(右键)-属性-高级系统设置-环境变量-path-在变量值后加上:和jdk安装路径加上(路径即为C:\Prog ...

  6. 如何搭建python框架_从零开始:写一个简单的Python框架

    原标题:从零开始:写一个简单的Python框架 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 你为什么想搭建一个Web框架?我想有下面几个原因: 有一个 ...

  7. ipad php mysql_如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1

    原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app 作为一个i ...

  8. python123程序设计题说句心里话_用c++写一个简单的计算器程序

    // 050305.cpp : 定义控制台应用程序的入口点. // // 050304.cpp : 定义控制台应用程序的入口点. // //四则运算 #include "stdafx.h&q ...

  9. 用java写一个简单的区块链(下)

    用java写一个简单的区块链(下) 2018年03月29日 21:44:35 java派大星 阅读数:725 标签: 区块链java 更多 个人分类: 区块链 版权声明:本文为博主原创文章,转载请标明 ...

最新文章

  1. python调用大漠插件_Burp Extender Apis 插件开发 (一)
  2. zookeeper系列(二)实战master选举
  3. 剪刀,石头,布,小游戏脚本
  4. python优雅编程_Python优雅地可视化数据
  5. sqlmap第一次打靶成功
  6. Linux之父炮轰 “全球最大同性恋网站” GitHub:Merge制造了毫无用处的垃圾信息!...
  7. CAD如何完成10以上带圈序号的输入?
  8. 蓝桥杯官网 试题 PREV-113 历届真题 估计人数【第十届】【决赛】【研究生组】【C++】【Java】【Python】三种解法
  9. 音阶频率对照表_各个音阶的对应频率
  10. NVIDIA Game Ready 显卡驱动517.48发布!为《守望先锋2》做好游戏准备
  11. arcgis海岸带_需科学划定海岸带基准地理边界
  12. PowerDesigner16.5下载
  13. pathon和c语言的区别
  14. 双非一本考研国防科技大学计算机,【21择校】这些高校不歧视双非,保护一志愿!...
  15. unity3d学习笔记(一)方向键移动物体
  16. 使用setoolkit克隆钓鱼网站时修改网页错误的解决方法
  17. Python 解释器
  18. 单招计算机专业考几科,高职单招一般都考什么科目
  19. 数字IC前端面试问题总结
  20. Symbian环境搭建,高版本ActivePerl兼容性解决方案(5.10.0)

热门文章

  1. 计算机网络在企业管理中的应用研究,计算机网络技术在企业档案管理中的应用研究(原稿)...
  2. [转]基于Mathematica的机器人仿真环境(机械臂篇)
  3. 怎样在线转换视频文件格式
  4. MPS模块式柔性自动化生产线实训系统QY-JDYT11
  5. time_t、struct tm,ctime各数据类型、函数详解,转换以及跟时间字符串的转换
  6. js获取图片上道路的坐标
  7. 关于58同城和赶集网被曝光后的反思
  8. 【蓝桥软件学院】泛型类、泛型方法及泛型应用
  9. arduino物联网实验:光敏电阻实现光控小夜灯
  10. kvm虚拟机静态迁移