zoukankan      html  css  js  c++  java
  • 2019爬虫项目总结——我在项目中踩的那些坑

    2019刚出校门,初到公司,最大的成就是完成了一个全球抓取数据的系统!简单介绍一下这个项目的实现思路以及在项目中踩过的坑,随时告诫自己以后尽量避免!

    历时一个半月还多几天,终于通过交付测试了!项目是从全球范围内,通过Google,Bing,雅虎来抓取数据,由于服务器不是特别的好,并且考虑到会有很多的脏数据,我们在实现的时候并没有将数据入库,想要将数据进行持久化的保存,可以使用导出的方式将数据以电子表格的方式导出来!

    难点一:

    项目经历了一次大的改版,将整个的抓取流程做了一次很大的改动。我们在抓取数据的时候,需要通过关键词搜索出对应的网页url链接,再访问链接深入到官网去抓取我们想要的数据,之后再将抓取到的数据动态的拼接到界面上对应的url链接的后面。因为数据并不能保存到数据库进行持久化存储,并且抓取到的url是一个集合,所以,如何在抓取到url之后还能深入到链接里面去抓取数据就成了一个难点问题!

    在大牛的帮助下,我们找到了一种实现思路:

    通过页面的开始按钮,给服务端发送一个ajax请求开始抓取数据,数据抓取完之后,将数据返回绑定到界面,给每一个url规定一个唯一的标识,紧接着调用第二个ajax循环遍历抓到的数据,发送请求进行深度抓取,将抓取到的数据返回之后根据标识拼接到对应的url后面,完成一次抓取。

    难点二:

    这样第一个难点问题就解决了,虽然这个问题解决了,但是如果我们想要中途停止怎么办?这毕竟是一个网站,在抓取数据的时候,我得时时刻刻的开着这个网站,当我感觉数据量差不多了,我不想再抓了,我想将现有的数据导出来,我要停止抓取,然后再导出数据,那么,我要怎么停止呢?这是碰到的第二个难点问题!

    为什么这个会是一个难点问题呢?因为我在实现循环遍历的时候一次性将所有的url都遍历完了,有多少条url就发送了多少条的ajax请求。因为ajax是异步的,所以,虽然界面不会因为发送很多的ajax而出现卡死状态,但是请求我已经发送完了,接下来的数据返回就不是我能干预的了,这就是问题点所在。我也尝试当点击停止的时候将返回的数据隐藏掉,表面上看起来是抓取的操作被关闭了,但是,当用户进行第二次搜索操作的时候,由于第一次的搜索并没有结束,所以第二次的搜索会变得很慢。这并不是一种很好的解决方案。

    后来通过浏览器的Network发现,既然每个请求都有一个name属性,那么我能不能通过name属性来获取到每一条请求呢?既然成功的状态码是200,那么我能不能通过浏览器告诉服务器,你不要给我返数据了,我不要了。答案当然是可以的了。

    <script src = "jquery.js"></script>
    <script>
    var xhr = $.ajax({type:'POST',
        url:'b.php',
        data:'',
        success:function(){
            alert('ok');
        }
    })
    alert(xhr);
    
    console.log(xhr);
    </script>
    <button id="song">abort</button>
    <script>
    $(function(){
        $("#song").click(function(){
            alert('click');
            xhr.abort();
        })
    })
    </script>

    我们在创建ajax请求的时候,将这个ajax赋值给一个变量,然后,我们将在需要的地方,使用这个变量,调用abort方法就可以将未响应(padding)的请求关闭掉了。

    当然这种写法只能是将一个ajax请求关闭,我们需要的是批量的关闭,有什么办法吗?

    有的,定义一个数组,将所有的ajax,push到里面,然后在点击停止的时候,遍历数组,关闭掉所有的请求!

    //定义一个ajax数组
    var ajaxArr = [];
    
    //深度访问的函数
    function requestPlus(id, url) {
        //获取界面上的ID和网址
        //1.获取所有的tr
        //Ajax进行深度搜索
        if (typeof (url) != undefined) {
            req++;
            $(".logo_div").show();
            //将同步请求变成异步请求,之后所有的ajax都会变成异步请求,设置这个的目的是因为后面有将异步设置为同步,防止点击同步之后再做此功能变成同步,影响速度
            $.ajaxSettings.async = true;
            currentAjax = $.ajax({
                cache: false,       //缓存
                dataType: 'json',
                type: 'Post',        //传参方式
                url: "/home/DepthQuery",  //传到哪里去,调用后台的程序将参数传递过去,通过路由的形式传递
                data: { 'Id': id, 'url': url },
                success: function (data) {
                    if (data == null) return;
                    var _data = data.Msg;
                    //            var html = "<table>";
                    var html = "";
                    if (_data.length > 0) {
                        for (var i = 0; i < _data.length; i++) {
                            //将取出来的Id与页面上的Id搜索匹配,直接将ID拼接上就可以了
                            var state = $("#hid").val();
                            if (state != "false") {
                                //                            html += "<td>" + _data[0].facebook + "</td>" + "<td>" + _data[0].twitter + "</td>";
                                //获取到facebook,和twitter
                                var facebook = _data[0].facebook;
                                var twitter = _data[0].twitter;
                                var Email = _data[0].Email;
                                var phone = _data[0].Tel
    
                                
                                $("#" + _data[0].Num + "").children(".facebook1").text(facebook);
                                $("#" + _data[0].Num + "").children(".mailbox1").text(Email);
                                if (Email) { //typeof(Email)!=undefined
                                    $("#email").append(Email + ",")
                                }
                                $("#" + _data[0].Num + "").children(".Recom_num1").text(twitter);
                                $("#" + _data[0].Num + "").children(".phone_num1").text(phone);
                            }
                        }
                    }
                    req--;//标识减一
                },
                error: function () {
                    req--;
                }
            });
            ajaxArr.push(currentAjax);
        }
    }
    
    //关闭请求
    function abortAjax() {
        $.each(ajaxArr, function (i, a) {
            a.abort();
        });
    }

    这样就实现了批量关闭请求的功能。到此,一个完整的抓取流程就完成了,但是,这样还是存在问题的。

    问题一:响应速度过慢

    这样虽然实现了功能,但是在实测当中,我们发现数据的返回速度特别慢,到最后时候一条数据能返回十五六分钟!这也是项目中最大的难点,怎么将速度提上去?对,没错,使用线程就可以了,这也是为什么会有一个很大的改版原因。

    先说这种实现方式速度慢的原因,也是我最想记录下来的:

    我们一开始怀疑是接口返回的慢,导致了整个速度变慢,但是我们通过压力测试,接口的返回速度并不慢,所以,问题到底在哪呢?Ajax发送请求完毕之后就跟它没关系了,对吗?Ajax也是异步请求的,那么数据返回结果到页面的速度也是互不影响的,那么问题在哪?

    我们应该知道,js是脚本语言,它是单线程的,单线程怎么能实现异步呢?按道理来讲,异步是基于多线程来实现的,那为什么js是单线程的,却可以实现异步呢?

    通过查阅资料,我了解到:Ajax实现异步是通过定时器加队列的方式实现的。就是说,Ajax的异步还是依托单线程来实现的(通过定时器实现异步),每发送一个异步请求,就将这个请求加到一个队列里面去,当有多个异步数据返回的时候,将返回的数据先放到队列里面,等待线程空闲的时候来取数据然后做后续的操作,这就是出现数据慢的原因,不是抓数据满,是绑定数据慢。接下来的改版方式很简单了,把请求扔到后台代码中用多线程做处理就可以了。下一篇再记录任务(Tesk)的相关总结。

    参考博客:https://www.cnblogs.com/hutuzhu/p/4301751.html

  • 相关阅读:
    Python_Note_08Day 9_HTML
    Python_Note_08Day 6_Bastion Host_Paramiko_SSH
    Python_Note_08Day 5_Module
    Python_Note_Day 11_Mysql02_Python MySQL API
    Python_Note_Day 11_Mysql
    Python_Data_Analysis_01
    485. Max Consecutive Ones
    498. Diagonal Traverse
    500. Keyboard Row
    481. Magical String
  • 原文地址:https://www.cnblogs.com/Xiaomoml/p/12227455.html
Copyright © 2011-2022 走看看