• 基于http(s)协议的模板化爬虫设计


    声明:本文为原创,转载请注明出处

    本文总共三章,前面两章废话吐槽比较多,想看结果的话,直接看第三章(后续会更新,最近忙着毕设呢,毕设也是我自己做的,关于射频卡的,有时间我也放上来,哈哈)。

    • 一,系统总体结构

      首先吐槽一下,标题取得好水,原谅我是一枚耿直的工科男。系统框图如下所示,简单看一下,对整体有个把握,总体由4个部分组成

      上图中,装载URL队列的是先进先出的队列,整个爬虫系统的设计就是基于宽度优先遍历的原则设计的。所以,对于执行整个下载任务的引擎而言,它只是启动一个线程池,然后机械的从队列里面拿出链接,下载网页,然后再将网页中的链接放到队列里面,循环上面的操作。由于每一个站点都有其自身布局的特点,比如特定的编码,特定的URL格式,面向站点的模板需要提供这些特点,以方便爬取效率和自己想要得到的效果。对于一张网页,我们真正关心的,讲个实话,只是一部分数据,所以在模板获取数据之前,是先需要对数据进行过滤,排版等相关处理的,面向数据处理的模型就是为了解决这个问题而产生的。(我去感觉这段话,有种催眠的感觉,发现自己表达能力好差。)

    •   二,设计思想及核心流程图

       说点题外话,关于爬虫的的实现,网上很多,但是只要你不是我这样的大老粗,你仔细看就会发现,所有的实现基本都是机械抓取URL,最为致命的是抓取的URL还有可能是相对URL。那什么获取个性化数据就有点大海捞针的感觉了。哈哈,自恋一下,本爬虫就可以大海捞针(你也可以当笑话看,否则这玩意太枯燥了)。其实我感觉的话,爬虫的核心无非就两个:一是要能很好的处理URL,二是方便获取想要的数据。至于什么效率,鄙人认为,在这个计算机处理能力飞速提示的年代,不是那么重要。

      下面是整个引擎执行流程图:

    上图十分清楚的描述了整个引擎的执行流程图,现在一看好像很简单,但是当时写起来,那个问题,不想说了,不利于弘扬社会主义核心价值观,哈哈。至于到底是怎么用我会把代码放出来(留言qq索取也行,githup还没有建仓库呢),有了这个图大家看起来,也方便些。接下来,实在是不想废话了,直接看使用demo。

    • 三,实践

      正所谓实践是检验真理的唯一标准,这里以抓取阿里巴巴旗下蚂蚁金服的基金数据为例子,进行简单讲解一下。

      3.1 定义一个基于该网站的引擎类。此类需要导入我开发的包,然后继承自Spider。Spider有几个函数是用于子类重载的。主要的也就是下面的三个重载子函数.详情看注释

    public class AliFundtion extends Spider {
        private WebClient webClient=new WebClient();
        private Pattern HREF=Pattern.compile("href="(.*?)"");
        public AliFundtion(int maxSize,int threadSize){
            super(maxSize,threadSize);
            initWebOptions();
        }
        private void initWebOptions(){
            webClient.setAjaxController(new NicelyResynchronizingAjaxController());
            webClient.getOptions().setJavaScriptEnabled(true);
            webClient.getOptions().setCssEnabled(false);
            webClient.getOptions().setTimeout(35000);
            webClient.getOptions().setThrowExceptionOnScriptError(false);
        }
    
        /**
         * 通过重载这个函数,我们这里只抓取蚂蚁金服首页展示的基金数据,因为它首页包含了所有的基金,
         * 这个函数决定了URL队列里面到底有哪些链接,是引擎的抓取的来源
         * @param html 抓取到的网页
         * @param url  该网页对应的URL
         * @param que URL队列 
         */
        @Override
        protected void allHrefEnQueue(String html, String url, SpiderQueue que) {
            if(url.equals("http://www.fund123.cn/fund")){//断定是首页
                List<String> hrefs=RegexUtil.getAllMatcher(html,1,HREF);
                for(String href:hrefs){
                    String absoUrl=Browser.relative2TrUri(href,url);//转化为绝对路径
                    if(absoUrl.startsWith("http://www.fund123.cn/matiaria?fundCode")){//这正是我们需要的基金数据
                        que.enQueue(absoUrl);
                    }
                }
            }
    
        }
    
        /**
         * 这个函数决定了,通过URL获取网页的方式,对于需要执行js代码的网页而言,重载这个函数是必须得,比如这里就是基于htmlUnit重载了Spider的这个函数
         * @param url 即将抓取的URL
         * @param charset 所用的字符编码
         * @return  返回抓取到的数据
         * @throws IOException 
         */
        @Override
        synchronized protected String toGetHtmlPage(String url, String charset) throws IOException {
           HtmlPage page=webClient.getPage(url);
            return page.asXml();
        }
    
        /**
         * 注意这里必须调用父类的close()方法,来释放一些资源
         */
        @Override
        protected void close() {
            super.close();
            webClient.close();
        }
    }

       3.2 主程序调用步骤

    public class Main {
        public static void main(String[] args) {
    
            //step1: 创建一个你自己定义的爬虫引擎,第一个参数表示队列的最大容量,第二个参数表示开启线程池中线程数量
            AliFundtion aliFundtion=new AliFundtion(50,6);
            //step2:定义你自己的站点模板
            ICrawlTemplate temp=new AbstractTemplate() {
                @Override
                public void crawlValue(String html, String url) {
                    //todo 在这里写下你想做的事,比如将得到的数据持久化到数据库,或者保存到磁盘,其中html表示被数据模型修饰后的数据,url是该html对应的链接
                    System.out.println(html);
                }
                @Override
                public String getBaseSite() {
                    return "http://www.fund123.cn/fund";
                }
    
                @Override
                public String getCharset() {
                    return null;
                }
    
                @Override
                public boolean filterUrl(String url) {
                    return true;
                }
            };
            //step3:采用正则表达式进行数据建模
                //<_%></_%>这是一对表示这个一个正则表达式的标签
                    //index 属性 就是该正则表达式需要提取的组
            StringBuilder sb=new StringBuilder();
            sb.append("基金名称:").append("<_% index="1">").append("<span class="fundmatiaria-title-fundname">(.*?)</span>").append("</_%>");
            sb.append("基金代码:").append("<_% index="1">").append("<span class="fundmatiaria-title-fundcode">(.*?)</span>").append("</_%>");
            sb.append("基金净值:").append("<_% index="1">").append("<p class="fundmatiaria-fundinfo-value".*?>(.*?)</p>").append("</_%>");
            RegexModel model=new RegexModel(sb.toString());
            model.getOptions().setAbsoJs(true);//表示将所有js路径转化为绝对路径
            model.getOptions().setAbsoImage(true);//表示将所有image路径转化为绝对路径
            model.getOptions().setAbsoCss(true);//表示将所有css路径转化为绝对路径
            aliFundtion.setModel(model);
            //step4:下载模板
            aliFundtion.downLoadArtcle(temp);
        }
    }

     3.3 看一下结果:

    基金名称:南方原油(QDII-FOF)

    基金代码:(501018)

    基金净值:1.1502  +1.42%  -1.57%

    基金名称:广发道琼斯美国石油开发与生产指数(QDII-LOF)A

    基金代码:(162719)

    基金净值:1.1371 +1.65% +0.04%

    基金名称:银华心诚灵活配置混合

    基金代码:(005543)

    基金净值:0.9507 +1.79% +1.79%

  • 相关阅读:
    vue滑块拖拽校验
    vue和原生自动聚焦
    vue实现bar左右拖拽
    fastclick插件使用
    三大家族易忘点和案例
    移动端调试工具chrome+devtools
    restful 与 webapi 详解
    .NET Core 中依赖注入框架详解 Autofac
    .NET Core 对象( Transient、Scope、Singleton )生命周期详解 (对象创建以及释放)
    C# 通过DataSet 获取SQL 存储过程返回的多个结果集(tables)
  • 原文地址:https://www.cnblogs.com/enjoyall/p/9163296.html
Copyright © 2020-2023  润新知