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

相关推荐