首页 > 网站 > WEB开发 > 正文

一次使用NodeJS实现网页爬虫记

2024-04-27 14:07:10
字体:
来源:转载
供稿:网友

一次使用NodeJS实现网页爬虫记

前言

几个月之前,有同事找我要php CI框架写的OA系统。他跟我说,他需要学习PHP CI框架,我建议他学习大牛写的国产优秀框架QeePHP。

我上QeePHP官网,发现官方网站打不开了,GOOGLE了一番,发现QeePHP框架已经没人维护了。API文档资料都没有了,那可怎么办?

毕竟QeePHP学习成本挺高的。GOOGLE时,我发现已经有人把文档整理好,放在自己的个人网站上了。我在想:万一放文档的个人站点也挂了,

怎么办?还是保存到自己的电脑上比较保险。于是就想着用NodeJS写个爬虫抓取需要的文档到本地。后来抓取完成之后,干脆写了一个通用版本的,

可以抓取任意网站的内容。

爬虫原理抓取初始URL的页面内容,提取URL列表,放入URL队列中,从URL队列中取一个URL地址,抓取这个URL地址的内容,提取URL列表,放入URL队列中

。。。。。。。。。。。。

NodeJS实现源码

  1 /**  2  * @desc 网页爬虫 抓取某个站点  3  *  4  * @todolist  5  * URL队列很大时处理  6  * 302跳转  7  * 处理COOKIE  8  * iconv-lite解决乱码  9  * 大文件偶尔异常退出 10  * 11  * @author WadeYu 12  * @date 2015-05-28 13  * @copyright by WadeYu 14  * @version 0.0.1 15  */ 16   17 /** 18  * @desc 依赖的模块 19  */ 20 var fs = require("fs"); 21 var http = require("http"); 22 var https = require("https"); 23 var urlUtil = require("url"); 24 var pathUtil = require("path"); 25  26 /** 27  * @desc URL功能类 28  */ 29 var Url = function(){}; 30  31 /** 32  * @desc 修正被访问地址分析出来的URL 返回合法完整的URL地址 33  * 34  * @param string url 访问地址 35  * @param string url2 被访问地址分析出来的URL 36  * 37  * @return string || boolean 38  */ 39 Url.PRototype.fix = function(url,url2){ 40     if(!url || !url2){ 41         return false; 42     } 43     var oUrl = urlUtil.parse(url); 44     if(!oUrl["protocol"] || !oUrl["host"] || !oUrl["pathname"]){//无效的访问地址 45         return false; 46     } 47     if(url2.substring(0,2) === "//"){ 48         url2 = oUrl["protocol"]+url2; 49     } 50     var oUrl2 = urlUtil.parse(url2); 51     if(oUrl2["host"]){ 52         if(oUrl2["hash"]){ 53             delete oUrl2["hash"]; 54         } 55         return urlUtil.format(oUrl2); 56     } 57     var pathname = oUrl["pathname"]; 58     if(pathname.indexOf('/') > -1){ 59         pathname = pathname.substring(0,pathname.lastIndexOf('/')); 60     } 61     if(url2.charAt(0) === '/'){ 62         pathname = ''; 63     } 64     url2 = pathUtil.normalize(url2); //修正 ./ 和 ../ 65     url2 = url2.replace(////g,'/'); 66     while(url2.indexOf("../") > -1){ //修正以../开头的路径 67         pathname = pathUtil.dirname(pathname); 68         url2 = url2.substring(3); 69     } 70     if(url2.indexOf('#') > -1){ 71         url2 = url2.substring(0,url2.lastIndexOf('#')); 72     } else if(url2.indexOf('?') > -1){ 73         url2 = url2.substring(0,url2.lastIndexOf('?')); 74     } 75     var oTmp = { 76         "protocol": oUrl["protocol"], 77         "host": oUrl["host"], 78         "pathname": pathname + '/' + url2, 79     }; 80     return urlUtil.format(oTmp); 81 }; 82  83 /** 84  * @desc 判断是否是合法的URL地址一部分 85  * 86  * @param string urlPart 87  * 88  * @return boolean 89  */ 90 Url.prototype.isValidPart = function(urlPart){ 91     if(!urlPart){ 92         return false; 93     } 94     if(urlPart.indexOf("javascript") > -1){ 95         return false; 96     } 97     if(urlPart.indexOf("mailto") > -1){ 98         return false; 99     }100     if(urlPart.charAt(0) === '#'){101         return false;102     }103     if(urlPart === '/'){104         return false;105     }106     if(urlPart.substring(0,4) === "data"){//base64编码图片107         return false;108     }109     return true;110 };111 112 /**113  * @desc 获取URL地址 路径部分 不包含域名以及QUERYSTRING114  *115  * @param string url116  *117  * @return string118  */119 Url.prototype.getUrlPath = function(url){120     if(!url){121         return '';122     }123     var oUrl = urlUtil.parse(url);124     if(oUrl["pathname"] && (///$/).test(oUrl["pathname"])){125         oUrl["pathname"] += "index.html";126     }127     if(oUrl["pathname"]){128         return oUrl["pathname"].replace(/^//+/,'');129     }130     return '';131 };132  133 134 /**135  * @desc 文件内容操作类136  */137 var File = function(obj){138     var obj = obj || {};139     this.saveDir = obj["saveDir"] ? obj["saveDir"] : ''; //文件保存目录140 };141 142 /**143  * @desc 内容存文件144  *145  * @param string filename 文件名146  * @param mixed content 内容147  * @param string charset 内容编码148  * @param Function cb 异步回调函数149  * @param boolean bAppend 150  *151  * @return boolean152  */153 File.prototype.save = function(filename,content,charset,cb,bAppend){154     if(!content || !filename){155         return false;156     }157     var filename = this.fixFileName(filename);158     if(typeof cb !== "function"){159         var cb = function(err){160             if(err){161                 console.log("内容保存失败 FILE:"+filename);162             }163         };164     }165     var sSaveDir = pathUtil.dirname(filename);166     var self = this;167     var cbFs = function(){168         var buffer = new Buffer(content,charset ? charset : "utf8");169         fs.open(filename, bAppend ? 'a' : 'w', 0666, function(err,fd){170             if (err){171                 cb(err);172                 return ;173             }174             var cb2 = function(err){175                 cb(err);176                 fs.close(fd);177             };178             fs.write(fd,buffer,0,buffer.length,0,cb2);179         });180     };181     fs.exists(sSaveDir,function(exists){182         if(!exists){183             self.mkdir(sSaveDir,"0666",function(){184                 cbFs();185             });186         } else {187             cbFs();188         }189     });190 };191 192 /**193  * @desc 修正保存文件路径194  *195  * @param string filename 文件名196  *197  * @return string 返回完整的保存路径 包含文件名198  */199 File.prototype.fixFileName = function(filename){200     if(pathUtil.isAbsolute(filename)){201         return filename;202     }203     if(this.saveDir){204         this.saveDir = this.saveDir.replace(/[///]$/,pathUtil.sep);205     }206     return this.saveDir + pathUtil.sep + filename;207 };208 209 /**210  * @递归创建目录211  *212  * @param string 目录路径213  * @param mode 权限设置214  * @param function 回调函数215  * @param string 父目录路径216  *217  * @return void218  */219 File.prototype.mkdir = function(sPath,mode,fn,prefix){220     sPath = sPath.replace(///+/g,'/');221     var aPath = sPath.split('/');222     var prefix = prefix || '';223     var sPath = prefix + aPath.shift();224     var self = this;225     var cb = function(){226         fs.mkdir(sPath,mode,function(err){227             if ( (!err) || ( ([47,-4075]).indexOf(err["errno"]) > -1 ) ){ //创建成功或者目录已存在228                 if (aPath.length > 0){229                     self.mkdir( aPath.join('/'),mode,fn, sPath.replace(///$/,'')+'/' );230                 } else {231                     fn();232                 }233             } else {234                 console.log(err);235                 console.log('创建目录:'+sPath+'失败');236             }237         });238     };239     fs.exists(sPath,function(exists){240         if(!exists){241             cb();242         } else if(aPath.length > 0){243             self.mkdir(aPath.join('/'),mode,fn, sPath.replace(///$/,'')+'/' );244         } else{245             fn();246         }247     });248 };249 250 /**251  * @递归删除目录 待完善 异步不好整252  *253  * @param string 目录路径254  * @param function 回调函数255  *256  * @return void257  */258 File.prototype.rmdir = function(path,fn){259     var self = this;260     fs.readdir(path,function(err,files){261         if(err){262             if(err.errno == -4052){ //不是目录263                 fs.unlink(path,function(err){264                     if(!err){265                         fn(path);266                     }267                 });268             }269         } else if(files.length === 0){270             fs.rmdir(path,function(err){271                 if(!err){272                     fn(path);273                 }274             });275         }else {276             for(var i = 0;
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表