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
相关推荐
结束数据方法的参数,该如何定义?-- 集合为自定义实体类中的结合属性,有几个实体类,改变下标就行了。<input id="add" type="button" value="新增visitor&quo