Java应用开源框架实现简易web搜索引擎

引言

应用 Java 的开源库,编写一个搜索引擎,这个引擎能爬取一个网站的内容。并根据网页内容进行深度爬取,获取所有相关的网页地址和内容,用户可以通过关键词,搜索所有相关的网址。

具体功能

(1) 用户可以指定爬取一个url对应的网页的内容。
(2) 对网页内容进行解析,并获取其中所有的url链接地址。
(3) 用户可以设定爬取深度,代表着从初始url对应的页面开始,可以爬取其中所有的url对应的网页内的url,以此类推。深度越大,能爬取到的网站越多。
(4) 对爬取到的url内容进行保存、建立索引。建立索引的内容是url地址本身,和url对应的网页标题。
(5) 用户可以通过关键词对网址进行搜索,找出有该关键词的url地址。
(6) 建立索引和搜索索引的过程能智能识别中文关键词,能对关键词进行分词操作。
(7) 用户可以指定保存索引的地址、初始url、爬取深度、进行搜索的关键词和最大匹配项。

开源框架

  1. Lucene
  2. Jsoup

源码

爬虫部分:Spider.java

package webCrawler.Spider;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import webCrawler.Index.BuildIndex;

/**
 * @author lannooo
 */

public class Spider {
  ArrayList<String> URLs;
  private String startURL;
  private int digLevel;

  /**
   * @param startURL 爬虫的起始URL
   * @param digLevel 爬取深度
   */
  public Spider(String startURL, int digLevel){
    this.startURL = startURL;
    this.digLevel = digLevel;
    this.URLs = new ArrayList<>();
  }

  /**
   * @param level 当前爬取的深度剩余
   * @param arrayList 需要进行下一轮爬去的URL集
   * @return 从一格url集爬取到的新的URL集
   * @throws IOException
   */
  public ArrayList<String> getLevelURLs(int level, ArrayList<String> arrayList)
      throws IOException{
    ArrayList<String> total = null;
    if(level>0){
      total = new ArrayList<>();
      for(String url: arrayList){
        /*对于每个arrayList中的URL,首先解析其网页内容,并获得里面所有URL项*/
        for(String each: getBareLinks(url)){
          total.add(each);
        }
      }
      /*用HashSet这个容器将total里面重复项删除*/
      HashSet<String> hashSet = new HashSet<>(total);
      total = new ArrayList<>(hashSet);
    }
    return total;
  }

  /**
   * 从startURL开始,爬取所有相关URLs
   * @throws IOException
   */
  public void getAll() throws IOException{
    ArrayList<String> newURLs;
    ArrayList<String> currentURLs = new ArrayList<>();
    /*把startURL加入currentURLs这个列表中,从这个url开始爬*/
    currentURLs.add(startURL);
    for(int i=digLevel; i>0; i--){
      /*
       * 对于每一层,都要获取一次由这个url引申出去的url集
       * 然后把当前集的已经爬去过的url加入到总的URL集中
       * 最后newURLs作为新的需要进行深度爬取的集进入下一轮循环
       */
      System.out.println("Dig into level: " + (digLevel-i+1));
      newURLs = getLevelURLs(i, currentURLs);
      for(String each: currentURLs){
        URLs.add(each);
      }
      currentURLs = newURLs;
    }
    for(String each:currentURLs){
      URLs.add(each);
    }
    HashSet<String> hashSet = new HashSet<>(URLs);
    URLs = new ArrayList<>(hashSet);
  }

  /**
   * @param path 保存索引的路径
   * @throws IOException
   */
  public void storeURLsAndInfo(String path) throws IOException{
    BuildIndex build = new BuildIndex(path);
    /* 把URLs中的所有url进行实际网页标题的爬取*/
    for(String each:URLs){
      String text = getLinkText(each);
      if(text!=null){
        build.addField("url", each);
        build.addField("text", text);
        /*将这一个entry加入索引中*/
        build.pushIndex();
      }
    }
    build.close();
  }

  /**
   * @param url 需要获取网页标题的url
   * @return 标题内容
   * @throws IOException
   */
  public String getLinkText(String url) throws IOException{
    Document document = null;
    try {
      /*用Jsoup进行连接,设置超时时间为3秒*/
      document = Jsoup.connect(url).timeout(3000).get();
    } catch (Exception e) {
      System.out.println("[TIMEOUT]Get title of url:"+url);
      return null;
    }
    String title = document.title();
    return title;
  }

  /**
   * @param url 进行内容解析的url
   * @return 返回该url的网页内容内的所有urls列表
   * @throws IOException
   */
  public ArrayList<String> getBareLinks(String url) throws IOException{
    ArrayList<String> linksList = new ArrayList<>();
    Document document;

    try {
      document = Jsoup.connect(url).timeout(2000).get();
    } catch (Exception e) {
      return linksList;
    }
    /*获取<body>标签理的所有带href属性的<a>标签*/
    Elements links = document.select("body").select("a[href]");

    for(Element link: links){
      /*从每一个解析得到的<a>标签中提取url,并去除锚点*/
      String href = link.attr("abs:href").replaceAll("#", "");
      /*只添加含有zju.edu.cn字符的url,去除末尾的'/'*/
      if(href.contains("zju.edu.cn")){
        if (href.endsWith("/")){
          href = href.substring(0, href.length()-1);
        }
        linksList.add(href);
      }
    }
    HashSet<String> hashSet = new HashSet<>(linksList);
    ArrayList<String> arrayList = new ArrayList<>(hashSet);

    return arrayList;
  }

  public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    System.out.println("Enter url:");
    String url = in.nextLine().trim();
    while(!url.startsWith("http://")){
      System.out.println("http:// is needed!");
      System.out.println("Enter url:");
      url = in.nextLine().trim();
    }
    System.out.println("Enter depth to dig more urls[<=3 recommended]:");
    int depth = in.nextInt();
    Spider spider = new Spider(url, depth);
    System.out.println("Enter path you want to save[default=d:/index-spider]:");
    String path = in.nextLine().trim();
    if(path.length()==0){
      path = "d:/index-spider";
    }
    try {
      System.out.println("Start fetching...");
      spider.getAll();
      System.out.println("Urls got success!");
      spider.storeURLsAndInfo(path);
      System.out.println("Stored success!");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

建立索引:BuildIndex.java

package webCrawler.Index;

import java.io.*;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

/**
 * @author lannooo
 *
 */
public class BuildIndex {
  private File file;
  private Directory directory;
  private IndexWriter indexWriter;
  private IndexWriterConfig config;
  private Analyzer analyzer;
  private Document document;

  /**
   * @param path 建立索引的路径
   */
  public BuildIndex(String path) {
    try {
      file = new File(path);
      directory = FSDirectory.open(file);
      document = new Document();
      analyzer = new IKAnalyzer();    /*中文分词工具类*/
      config = new IndexWriterConfig(Version.LUCENE_4_10_0, analyzer);
      indexWriter = new IndexWriter(directory, config);      

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * @param fieldName 加入到document中的新的一项的名称
   * @param fieldText 新的一项的内容
   */
  public void addField(String fieldName, String fieldText){
    try{
      Field field = new TextField(fieldName, fieldText, Field.Store.YES);
      document.add(field);
    }catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 将document加入到索引中
   */
  public void pushIndex(){
    try {
      indexWriter.addDocument(document);
      document = new Document();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 加入完整的一个document并保存到索引中
   * @param url 加入的url地址
   * @param text url对应的文本
   */
  public void addOneIndex(String url, String text){
    this.addField("url", url);
    this.addField("text", text);
    this.pushIndex();
  }

  /**
   * 关闭索引写入
   */
  public void close(){
    try {
      indexWriter.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

搜索索引

package webCrawler.Index;

import java.io.File;
import java.util.Scanner;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

/**
 * @author lannooo
 *
 */
public class SearchIndex {
  private IndexSearcher indexSearcher;
  private Analyzer analyzer;
  private QueryParser parser;
  private Query query;
  private TopDocs hits;
  private DirectoryReader reader;

  /**
   * @param path 进行索引搜索的路径
   */
  public SearchIndex(String path){
    try {
      reader = DirectoryReader.open(FSDirectory.open(new File(path)));
      indexSearcher = new IndexSearcher(reader);
      analyzer = new IKAnalyzer();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * @param fieldName 搜索的域名称
   * @param text 搜索的内容
   * @param matchNumber 最大匹配项数
   * @return 搜索到的最大匹配数
   */
  public int search(String fieldName, String text, int matchNumber){
    try {
      parser = new QueryParser(fieldName, analyzer);
      query = parser.parse(text);
      hits = indexSearcher.search(query, matchNumber);

      return hits.totalHits;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return -1;
  }
  /**
   * 打印所有的匹配项
   */
  public void printHits(){
    try{
      System.out.println("Total hits number:"+hits.totalHits);
      for(ScoreDoc doc: hits.scoreDocs){
        Document document = indexSearcher.doc(doc.doc);
        System.out.println(document.get("url"));
        System.out.println(document.get("text"));
      }
      reader.close();
    }catch (Exception e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    /*输入关键词*/
    Scanner in = new Scanner(System.in);
    System.out.println("Enter path of the index:");
    String path = in.nextLine().trim();
    while(path.length()==0){
      System.out.println("Enter path of the index:");
      path = in.nextLine().trim();
    }

    System.out.println("Enter max hit number:");
    int max = in.nextInt();
    while(max<0){
      System.out.println("Enter max hit number:");
      max = in.nextInt();
    }
    in.nextLine();
    System.out.print("Search>>> ");
    String text = in.nextLine().trim();
    /*循环读入用户的关键词,如果是q则退出,长度为0也退出*/
    while(!text.equals("q")){
      if(text.length()>0){
        SearchIndex search = new SearchIndex(path);
        int hits = search.search("text", text, max);
        if(hits!=-1){
          search.printHits();
        }
      }
      System.out.print("Search>>> ");
      text = in.nextLine().trim();
    }
  }
}

UI界面(这里为了方便只是命令行的形式,可以根据需求写一个GUI界面)

package webCrawler.UI;

import java.util.Scanner;

import webCrawler.Index.SearchIndex;

/**
 * @author lannooo
 *
 */
public class UI {
  public static void main(String[] args) {
    /*输入关键词*/
    Scanner in = new Scanner(System.in);
    System.out.print("Search>>> ");
    String text = in.nextLine().trim();
    /*对于用户的关键词,如果是q则退出,长度为0也退出*/
    while(!text.equals("q") && text.length()>0){
      SearchIndex search = new SearchIndex("d:/index-spider2");
      int hits = search.search("text", text, 20);
      if(hits!=-1){
        search.printHits();
      }
      System.out.print("Search>>> ");
      text = in.nextLine().trim();
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现lucene搜索功能的方法(推荐)

    直接上代码: package com.sand.mpa.sousuo; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverMa

  • 详解Java的Hibernate框架中的搜索工具的运用

    hibernate提供了全文索引功能,非常棒,这里简要介绍下它的用法, 1. 在pom.xml引入包依赖 <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>${hibernate-search.version}</version> </dependency> &

  • Java使用强大的Elastisearch搜索引擎实例代码

    Elastisearch是一个很强大,易用的搜索引擎 在系统上运行Elastisearch只需以下几步 1.下载Elastisearch 复制代码 代码如下: wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.0.zip 2.解压 unzip elasticsearch-5.4.0.zip 3.运行 elasticsearch-5.4.0/bin/elasticsearch 这时有可能会直接被K

  • java实现简单的搜索引擎

    记得java老师曾经说过百度的一个面试题目,大概意思是"有1W条无序的记录,如何从其中快速的查找到自己想要的记录".这个就相当于一个简单的搜索引擎.最近在整理这一年的工作中,自己竟然已经把这个实现了,今天对其进一步的抽象,和大家分享下. 先写具体的实现代码,具体的实现思路和逻辑写在代码之后. 搜索时用于排序的Bean /** *@Description: */ package cn.lulei.search.engine.model; public class SortBean { p

  • java asp分析各种搜索引擎的关键字,自动识别url 中关键字的编码

    所以必须要通过编码后的关键字,例如"解析关键字编码"在google里面输入搜索,得到编码后的"%E8%A7%A3%E6%9E%90%E5%85%B3%E9%94%AE%E5%AD%97%E7%BC%96%E7%A0%81" 1.从以上地址中解析出关键字部分. 2.通过编码后的关键字获取编码时的编码名称(如:gbk,utf-8等等) 3.用URLdecode(keywords,encodeCode)来解码得到对应的关键字. 以下是java代码的实现: 复制代码 代码如

  • 使用Java的Lucene搜索工具对检索结果进行分组和分页

    使用GroupingSearch对搜索结果进行分组 Package org.apache.lucene.search.grouping Description 这个模块可以对Lucene的搜索结果进行分组,指定的单值域被聚集到一起.比如,根据"author"域进行分组,"author"域值相同的的文档分成一个组. 进行分组的时候需要输入一些必要的信息: 1.groupField:根据这个域进行分组.比如,如果你使用"author"域进行分组,那么

  • Java应用开源框架实现简易web搜索引擎

    引言 应用 Java 的开源库,编写一个搜索引擎,这个引擎能爬取一个网站的内容.并根据网页内容进行深度爬取,获取所有相关的网页地址和内容,用户可以通过关键词,搜索所有相关的网址. 具体功能 (1) 用户可以指定爬取一个url对应的网页的内容. (2) 对网页内容进行解析,并获取其中所有的url链接地址. (3) 用户可以设定爬取深度,代表着从初始url对应的页面开始,可以爬取其中所有的url对应的网页内的url,以此类推.深度越大,能爬取到的网站越多. (4) 对爬取到的url内容进行保存.建立

  • Java WebService开源框架CXF详解

    CXF简介 CXF是一个开源的WebService框架.Apache CXF = Celtix + XFire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF 了,以下简称为 CXF.CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding .DataBinding.Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WS

  • Java WebService开源框架CXF详解

    目录 CXF简介 支持多种标准 CXF入门案例 服务端的实现 客户端的实现 CXF+Spring整合发布SOAP模式的服务 服务端的实现 客户端的实现 CXF发布REST模式的服务 CXF+Spring整合发布REST模式的服务 综合案例:手机归属地查询 CXF简介 CXF是一个开源的WebService框架.Apache CXF = Celtix + XFire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF 了,以下简称为 CXF.CXF 继承了 Celtix

  • 在Java的Struts框架下进行web编程的入门教程

    当点击一个超链接或提交一个HTML表单在Struts2 的 Web应用程序,输入所收集被发送到一个Java类称为操作控制器.当动作执行后,结果选择了一个资源来呈现响应.资源通常是一个JSP,但它也可以是一个PDF文件,Excel电子表格,或一个Java applet 窗口. 假设已经建立开发环境.现在让我们继续为第一个 "Hello World" 的 struts2 项目构建.这个项目的目的是建立一个Web应用程序,它收集用户的姓名,并显示"Hello World"

  • 基于Java SSM框架实现简易的评教系统

    目录 介绍 效果图 部分核心代码 介绍 项目编号:BS-GX-014 数据库:mysql 开发工具:IDEA / ECLIPSE 开发技术:SSM 框架 本系统基于SSM框架实现.主要包含三个角色,管理员,老师,学生.管理员主要管理学生,老师,课程.学生可以进行选课,选完课后可以对任课老师评价.老师可以查看自己的评价信息. 效果图 部分展示功能如下: 管理员角色: 学生角色: 老师角色: 部分核心代码 package one.controller; import java.util.List;

  • Java图片处理开源框架Thumbnailator

    图片处理是当今软件开发中非常重要的一环,然而处理图片的开源框架却并不多.现金网上流传的Java处理图片的代码,虽然可对图片进行简单处理,但效果并不理想.虽然也有些其他解决方案,但都摆脱不了繁琐,使用起来十分不方便. 为了解决这个问题,我也是在网上找了好久,看了很多资料,功夫不负有心人,最终找到了一个处理图片十分棒的开源框架.特此拿出来与大家分享. Thumbnailator 是一个优秀的图片处理的Google开源Java类库.处理效果远比Java API的好.从API提供现有的图像文件和图像对象

  • java Spring 5 新特性函数式Web框架详细介绍

    java Spring 5 新特性函数式Web框架 举例 我们先从示例应用程序的一些摘录开始.下面是暴露Person对象的响应信息库.很类似于传统的,非响应信息库,只不过它返回Flux<Person>而传统的返回List<Person>,以及返回Mono<Person>的地方返回Person.Mono<Void>用作完成标识:指出何时保存被完成. public interface PersonRepository { Mono<Person> g

  • Python web开发之用Tornado框架制作简易表白墙网站

    目录 前言 Tornado框架简单介绍 Tornado框架优势 Tornado框架缺点 Tornado框架使用场景 框架的组成 开始先试下Tornado 先导入本次要用的模块 视图 设置路由 设置前端socket,调用 运行程序,先试试水 设置主页,调用前端文件模板 设置 表白墙视图 留言 前言 今天我们要用Python做Web开发,做一个简单的[表白墙]网站.众所周知表白墙的功能普遍更多的是发布找人,失物招领,还是一个大家可以跟自己喜欢的人公开表白的平台 Tornado框架简单介绍 在Pyth

  • 详解Java的Struts2框架的结构及其数据转移方式

    Struts2的结构 1.为什么要使用框架? (1)框架自动完成了很多琐屑的任务 对于Struts2来说,它帮助我们方便地完成了数据类型转换.数据验证.国际化等等 Web开发中常见的任务.还有Spring中大量使用的Template模式,都是在让我们的开发 过程更加自动化.智能化.使用框架就是避免重新发明轮子,重新复制这些模板代码. 框架让我们将精力更多地放在更高级别的问题上,而不是常见工作流和基础任务上. (2)使用框架就是优雅地继承了框架背后的架构 框架背后的架构通常定义了一系列的工作流程,

  • Java shiro安全框架使用介绍

    目录 1.shiro安全框架 1.1 什么是权限管理 1.2 什么是身份认证 1.3 什么是授权 1.4 认证授权框架有哪些 2.使用shiro完成认证工作 2.1 shiro中认证的关键对象 2.2 认证流程 2.3 项目代码 2.3.1 依赖 2.3.2 创建ini文件 2.3.3 测试代码 2.4 认证的原理 3.授权 3.1 修改ini文件 3.2 修改代码 1.shiro安全框架 Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证.授权.加密和session会话

随机推荐