阿里云的一道面试题:写一个爬取文档树和通过输入关键字检索爬取的内容的demo

目录

前言

一、面试内容

1、电话面试和项目实践题目

2、动手题目:文档爬取与搜索

3、研究题目

 

①查询输入关键词,给出最佳匹配解决思路

二、启动solr

1、配置步骤

2、solr的界面说明和使用

三、开始爬取

代码示例:

四、通过关键词检索

代码示例:

五、前台页面

前台代码示例:

六、运行效果图

七、总结:(附代码下载)


前言

前几天收到外派阿里的一个面试,作为自信、高傲的我,虽然是外派,但是对于阿里还是有一丝崇拜之心的,就像高考想进清华北大,工作想进腾讯阿里是一样的,当然除了校招985/211能进阿里之外,其他想通过社招进阿里的,那是难上加难,起码在某个领域是专家级别的。所以难得有机会,就试试阿里的面试呗。

一、面试内容

1、电话面试和项目实践题目

首先是电话面试:这个一般都没什么问题吧,好好复习多看书,少吃零食多睡觉...这样肯定是能够回答出来的

其次是出一道动手写demo题目,如下

文档链接 : https://help.aliyun.com/document_detail/48851.html

爬取左侧文档树中所有的文档列表

查询页面输入关键词或描述性语言,给出最佳匹配的3个文档(匹配度从高到低排序)。

提供:

1. 代码

2. 匹配思路

加分项:对于描述性语言如何给出推荐的文档。例如用户输入:我的日志采集不到了 

大部分人一听说要写demo就慌了,别怕,我这不是分享经验和代码示例给你们吗,所以好好看完这篇文章,过了应该是没问题的,我反正是过了。

2、动手题目:文档爬取与搜索

3、研究题目

首先,进链接看一下,咱们瞅一瞅到底是什么东东,原来是阿里云的帮助文档,看来这个简单的demo其实就是一个根据用户输入的关键词搜索到对应解决方法的一个小项目。

第一小步,爬取内容这一块应该不是很难,不管是用java还是Python都可以实现难点是第的,只不过Python可能简单一点,java的话写的代码会多一点,当然小编目前还是想先学精java这一块,所以演示的是java代码完成的,至于Python的话,先学好学精一门语言,再去拓展另外的语言,这样才能更好的辅助你。

难点在于第二小步,“查询页面输入关键词或描述性语言,给出最佳匹配的3个文档(匹配度从高到低排序)”

咱们先不爬取,因为爬取的话肯定是得封装好想要的格式的,在咱们还没有想到查询关键词这功能的时候,先保留。

 

①查询输入关键词,给出最佳匹配解决思路

这里你当然可以自己写算法,写匹配,但是这样的话匹配的肯定不是很准确的,而且要在一天之内写出来,几乎是不可能的,所以,咱们看看前辈们有没有对于这类有更好的解决办法呢,踩在巨人的肩膀上会事半功倍。

其实有跟多方法可以实现类似的功能,

譬如通过分词器搜索:Jieba分词、Ansj分词.......具体其他分词效果可以戳这里:了解11大开源中文分词器

或者类似搜索引擎服务器的开源框架:Elasticsearch、Lucene......具体其他搜索引擎服务可以戳这里:了解13大开源搜索引擎

小编这里演示的是用solr搜索引擎实现这个爬取和检索的demo项目


二、启动solr

solr的下载地址:http://archive.apache.org/dist/lucene/solr/   最好下载低版本的,高版本的需要更高的jdk版本,我jdk是1.7的,下载的solr的版本是4.7.0的,或者在文末下载我做的demo的时候,我也会将用到的东西都放在里面。

1、配置步骤

① 下载下来后,解压

② cmd进入这个目录里:xxxxx/solr-4.7.0/example

③ 执行命令:java -jar start.jar

④ 访问是否启动成功,在浏览器输入http://localhost:8983/solr 就可以访问,表示启动成功

2、solr的界面说明和使用

具体的solr的这里面的其他功能我就不具体介绍了,大家可以参考网上资料,进一步加深对solr的理解和使用


三、开始爬取

首先在项目中引入solr的maven包

<dependency>
      <groupId>org.apache.solr</groupId>
      <artifactId>solr-solrj</artifactId>
      <version>4.7.0</version>
</dependency>

爬取很简单,就是模拟浏览器去访问内容就可以了,我们可以看到要爬取的网站左边那一块所有的文字内容都是在

<span class="menu-item-text"><span>  里面的,

这就简单了,所以我们就可以将爬取的数据,经过正则匹配,就可以拿到想要的所有的文字标题信息了

代码示例:

 /**
     * 爬取数据
     * @return
     */
    @ResponseBody
    @RequestMapping("/getDocs")
    public String getDocs() {
        Map<String, Object> mapReturn = new HashMap<>(); //返回结果
        try {
        //爬取前先在solr上建林索引属性
        alibabaService.addDefaultField();

        //开始爬取指定url的数据
        String htmlResult = GetAliApi.sendGet("https://help.aliyun.com/document_detail/48851.html", "");
        //获取到 <ul id="J_MenuListContainer"> 树文档的内容
        String[] mainMenuListContainer = htmlResult.split("<ul id=\"J_MenuListContainer\">")[1].split("<div class=\"all-products\" id=\"J_AllProducts\">");
        //log.debug(mainMenuListContainer[0]);
        //log.debug("------------------------------");

        //进行正则获取数据
            String searchReg = "<span class=\"menu-item-text\">(.*?)</span>";
            Pattern pattern = Pattern.compile(searchReg); 	// 讲编译的正则表达式对象赋给pattern
            Matcher matcher = pattern.matcher(mainMenuListContainer[0]);

            int i = 0;
            String pre = "A";
            while (matcher.find()) {
                i++;
                String title = matcher.group(1);
                log.debug(title);
                //将数据放到solr里,添加索引
                Alidocs alidocs = new Alidocs();
                alidocs.setId(pre+i);
                alidocs.setTitle(title);
                alibabaService.addIndex(alidocs);
            }

            mapReturn.put("returnCode","00");
            mapReturn.put("content","爬取成功");
        }catch (Exception e){
            e.printStackTrace();
            mapReturn.put("returnCode","-1");
            mapReturn.put("content","爬取失败,请重试");
        }
        String mapStr = JSONObject.toJSONString(mapReturn);
        return mapStr;

    }

addDefaultField()方法和addIndex()方法:

 // 添加默认索引属性
    public void addDefaultField() throws SolrServerException, IOException {
        // 声明要连接solr服务器的地址
        String url = "http://localhost:8983/solr";
        SolrServer solr = new HttpSolrServer(url);
        SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", "默认情况下必须添加的字段,用来区分文档的唯一标识");
        doc.addField("title", "默认的名称属性字段");
        solr.add(doc);

        solr.commit();
    }


 // 添加索引
    public void addIndex(Alidocs alidocs) throws SolrServerException, IOException {
        // 声明要连接solr服务器的地址
        String url = "http://localhost:8983/solr";
        SolrServer solr = new HttpSolrServer(url);
        solr.addBean(alidocs);
        solr.commit();
    }

sendGet()方法:

 public static String sendGet(String url, String param) {
        String result = "";
        String urlName = url + "?" + param;
        try {

            URL realURL = new URL(urlName);
            URLConnection conn = realURL.openConnection();
            //伪造ip访问
            String ip = randIP();
            System.out.println("目前伪造的ip:"+ip);
            conn.setRequestProperty("X-Forwarded-For", ip);
            conn.setRequestProperty("HTTP_X_FORWARDED_FOR", ip);
            conn.setRequestProperty("HTTP_CLIENT_IP", ip);
            conn.setRequestProperty("REMOTE_ADDR", ip);
            conn.setRequestProperty("Host", "help.aliyun.com/");
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
            conn.setRequestProperty("Referer","https://help.aliyun.com/"); //伪造访问来源
            conn.setRequestProperty("Origin", "https://help.aliyun.com/"); //伪造访问域名
            conn.connect();
            Map<String, List<String>> map = conn.getHeaderFields();
            for (String s : map.keySet()) {
                System.out.println(s + "-->" + map.get(s));
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += "\n" + line;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

这样,基本上就爬取的功能就完成了,我们可以看到爬取的是想要的信息


四、通过关键词检索

检索就更简单了,因为用的是solr搜索引擎的服务,所以只要根据solr的api,对数据进行传入就可以检索了,他自动会进行分词筛选,根据匹配度将数据进行返回。

代码示例:

 /**
     * 通过关键词获取数据
     * @param title
     * @return
     */
    @ResponseBody
    @RequestMapping("/findDocs")
    public String findDocs(String title)  {
        Map<String, Object> mapReturn = new HashMap<>(); //返回结果
        try {
            String result = alibabaService.findIndex(title);
            mapReturn.put("returnCode","00");
            mapReturn.put("content",result);
        }catch (Exception e){
            e.printStackTrace();
            mapReturn.put("returnCode","-1");
            mapReturn.put("content","查询异常");
        }
        String mapStr = JSONObject.toJSONString(mapReturn);
        return mapStr;
    }

findIndex()方法:

 // 查找索引
    public String findIndex(String titleInput) throws SolrServerException {
        // 声明要连接solr服务器的地址
        String url = "http://localhost:8983/solr";
        SolrServer solr = new HttpSolrServer(url);

        // 查询条件
        SolrQuery solrParams = new SolrQuery();
        solrParams.setStart(0);
        solrParams.setRows(10);
        solrParams.setQuery("title:"+titleInput);
        // 开启高亮
        solrParams.setHighlight(true);
        solrParams.setHighlightSimplePre("<font color='red'>");
        solrParams.setHighlightSimplePost("</font>");

        // 设置高亮的字段
        solrParams.setParam("hl.fl", "title");
        // SolrParams是SolrQuery的子类
        QueryResponse queryResponse = solr.query(solrParams);

        // (一)获取查询的结果集合
        SolrDocumentList solrDocumentList = queryResponse.getResults();


        List contentList = new LinkedList();
        for (SolrDocument solrDocument : solrDocumentList) {
           Map<String,Object> map = new HashMap<>();
           map.put("id",solrDocument.get("id"));
           map.put("title",solrDocument.get("title"));
           contentList.add(map);

        }
        return contentList.toString();
    }

五、前台页面

最后就是前台页面,做的不是很好,因为比较赶时间,只给了一天时间,白天又要上班,只能晚上花几个小时研究后台代码,前台的就先不管了,你们如果有时间的话可以美化美化

前台代码示例:

<%--
  Created by IntelliJ IDEA.
  User: yjl
  Date: 2019-03-13
  Time: 20:03
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>阿里测试题</title>
    <script src="/public/js/jquery-2.1.4.min.js"></script>
    <script src="/public/js/selfMsgBox.js"></script>
    <script src="/public/js/jquery.easyui.min.js" ></script>
    <script src="/js/public.js" ></script>
    <link type="text/css" rel="stylesheet" href="pages/AliDocsTest/css/weui.css" />
    <style>

    </style>
</head>
<body>
<div class="weui-btn-area">
    <div class="weui-cell__hd">1、先爬取文档数据</div>
    <a  class="weui-btn weui-btn_mini weui-btn_primary" id="getDocs">开始爬取</a>
</div>
<div class="weui-cells weui-cells_form">
    <div class="weui-cell">
        <div class="weui-cell__hd"><label class="weui-label">搜索关键词</label></div>
        <div class="weui-cell__bd">
            <input class="weui-input keytitle"  type="text"  placeholder="请输入关键词"/>
        </div>
    </div>
</div>
<div class="weui-btn-area">
    <a  class="weui-btn weui-btn_mini weui-btn_primary" id="findDocs">查询</a>
</div>


<script>

    $('#getDocs').click(function () {
        ajaxLoading('爬取中,请稍后...');
        $.ajax({
            url: "/ali/getDocs",
            data: {},
            type: 'post',
            dataType: 'json',
            success: function (data) {
                ajaxLoadEnd();

                $.MsgBox.Alert("提示",data.content,"确定");

            },
            error: function () {
                $.MsgBox.Alert("异常","爬取发生异常,请联系管理员!","确定");
            }
        })
    })


    $('#findDocs').click(function () {
        var keytitle = $('.keytitle').val();
        if(keytitle==""){
            $.MsgBox.Alert("提示","淘气!请输入内容","确定");
            return
        }
        ajaxLoading('查询中...');
        $.ajax({
            url: "/ali/findDocs",
            data: {"title":keytitle},
            type: 'post',
            dataType: 'json',
            success: function (data) {
                ajaxLoadEnd();
                if (data.returnCode=="00"){
                    $.MsgBox.Alert("提示",data.content,"确定");
                }else {
                    $.MsgBox.Alert("提示",data.content,"确定");
                }

            },
            error: function () {
                $.MsgBox.Alert("异常","查询发生异常,请联系管理员!","确定");
            }
        })
    })

    function ajaxLoading(text){
        $("<div class=\"datagrid-mask\"></div>").css({display:"block",width:"100%",height:$(window).height()}).appendTo("body");
        $("<div class=\"datagrid-mask-msg\"></div>").html(text).appendTo("body").css({display:"block",left:($(document.body).outerWidth(true) - 190) / 2,top:($(window).height() - 45) / 2});
    }
    function ajaxLoadEnd(){
        $(".datagrid-mask").remove();
        $(".datagrid-mask-msg").remove();
    }

</script>
</body>
</html>

六、运行效果图

这样基本上就ok了,也是简单的完成了,跟我预期的还是有点差距的,但是呢,为了赶时间,还是赶紧的发过去了,我是晚上22:21左右发过去的,本来以为面试官得明天才能给出结果,但是呢阿里这么牛X的公司,这么牛不是没有道理的,面试官当场就给我回复了,说我的通过了,有这么敬业的程序员,这样的公司能不牛吗

 

 


七、总结:(附代码下载)

1.先得启动solr
解压,在 xxxxx/solr-4.7.0/example 目录 cmd
执行命令:java -jar start.jar

2、启动项目 aliTestProject
然后先点击爬取,等待一会儿,等页面出现【爬取成功】字样,即可进行查询


3、查询效果图
 

整个项目代码下载链接:https://download.csdn.net/download/qq_27471405/11019809


参考文章:

https://blog.csdn.net/u013087513/article/details/76034373

https://my.oschina.net/apdplat/blog/412921

https://blog.csdn.net/business122/article/details/78064092

https://blog.csdn.net/qing419925094/article/details/42142117

感谢原作者的分享,让技术人能够更快的解决问题
 

发布了82 篇原创文章 · 获赞 109 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/qq_27471405/article/details/88548417