最近一直在寫微信小程序,沒時間看nodejs,今天就把以前寫的圖片爬蟲拉出來曬一曬,省得完全忘記~寫的還不完善,還沒時間繼續,請開始嘲笑個人爛代碼~javascript
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>圖片爬蟲</title> <style> </style> </head> <body> <form method="post" autocomplete="off" action="/Interface/downloadImage"> <div><span class="title">網站 URL:</span><input name="url" type="text" value="https://www.cnblogs.com"> </div> <div><span class="title">存儲路徑:</span><input type="text" name="savePath" value="./images" /></div> <div> <span class="title">分 頁:</span> <span>第</span> <input name="beginPage" placeholder="#p1" class="page" value="#p1" /> <span>到</span> <input placeholder="#p3" name="endPage" class="page"value="#p3" /> <span>頁</span> <span style="color: red;font-size: 12px;">(支持兩種格式分頁,例如連接上的#p20、#pn20或者?p=20、?pn=20,根據目標URL判斷是否屬於這兩種中的一種,不屬於或不分頁不填)</span> </div> <div><input type="submit" class="submit" value="開始爬取"></div> </form> </body> </html>
var http = require('http'); var fs = require('fs'); var url = require('url'); var path = require("path"); var querystring = require('querystring'); var downloadImg = require('./downloadimg'); // 建立服務器 http.createServer(function(request, response) { var body = ""; request.on('data', function(chunk) { body += chunk; }); request.on('end', function() { // 解析參數 body = querystring.parse(body); if(body.url) { // 輸出提交的數據 POST(body, response) } else { // 解析請求,包括文件名 var pathname = url.parse(request.url).pathname.substr(1); if(pathname == "") { pathname = "index.html" } // 從文件系統中讀取請求的文件內容 fs.readFile(pathname, function(err, data) { if(err) { console.log(err); response.writeHead(404, { 'Content-Type': 'text/html' }); } else { // HTTP 狀態碼: 200 : OK // Content Type: text/plain switch(path.extname(pathname)) { case ".html": response.writeHead(200, { 'Content-Type': 'text/html' }); break; case ".js": response.writeHead(200, { 'Content-Type': 'text/javascript' }); break; } // 響應文件內容 response.write(data.toString()); } // 發送響應數據 response.end(); }); } }); }).listen(8081); //處理post請求 function POST(body, response) { if(body.url) { // 設置響應頭部信息及編碼 response.writeHead(200, { 'Content-Type': 'text/html; charset=utf8' }); response.write("<p style='margin:2px 0;padding:3px 0;'>正在準備下載..</p>") setTimeout(function() { downloadImg.downloadimg( body, function(msg, err) { if(err) { response.write("<p style='color:#2196f3;margin:2px 0;border-bottom:1px solid #CCC;padding:3px 0;font-size:12px;'>" + err + '</p>') } else { response.write("<p style='color:green;margin:2px 0;border-bottom:1px solid #CCC;padding:3px 0;font-size:12px;'>" + msg + '</p>') } }, function(successMsg, errMsg) { if(successMsg) response.end("<p style='background:green;color:#FFF;position:fixed;top:0;left:0;width:100%;padding:10px;margin:0'>" + successMsg + "</p>") if(errMsg) response.end("<p style='background:red;color:#FFF;position:fixed;top:0;left:0;width:100%;padding:10px;margin:0'>" + errMsg + "</p>") } ) }, 2000) } }
//依賴模塊 var fs = require('fs'); var request = require("request"); var cheerio = require("cheerio"); var mkdirp = require('mkdirp'); var http = require('http'); var urlm = require('url'); var querystring = require("querystring"); //建立目標網址http請求模塊 var downloadimg = function(body, imgcallback, endcallback) { //解析post參數 var url = body.url; var dir = body.savePath || './images'; //獲取保存目錄 var page = { begin: body.beginPage, end: body.endPage }; //建立目錄 mkdirp(dir, function(err) { if(err) { console.log(err); } }); //第一次發送請求 getRequest(url, dir, imgcallback, endcallback, page); } //請求目標地址 var getRequest = function(url, dir, imgcallback, endcallback, page) { //獲取地址真實 var pageUrl = getPageUrl(page, url) //打印當前請求地址 if(page.currentIndex) { imgcallback(0, "正在分析第" + page.currentIndex + "頁," + pageUrl) } else { imgcallback(0,"正在分析" + pageUrl) } //開始請求 request(url, function(error, response, body) { if(!error && response.statusCode == 200) { images = imageArrangement(body, url, function(errmsg) { //網站分析失敗回調,傳回錯誤信息 endcallback(0, errmsg) }, function(imageSrcArr) { console.log(imageSrcArr) //網站body分析成功,返回圖片完整地址數組:imageSrcArr //初始化完成數量 var complateImageLength = 0; for(var i = 0; i < imageSrcArr.length; i++) { var item = imageSrcArr[i]; //獲取圖片名 var fileName = item.split('/').pop(); //建立下載 download(item, dir, fileName, function(imagsrc, err) { complateImageLength++; //一張下載完成 imgcallback(imagsrc + "下載成功", err) //所有下載完成 if(complateImageLength == imageSrcArr.length) { //分頁 if(page.currentIndex < page.EndIndex) { page.currentIndex++; //獲取分頁地址 //var pageUrl = getPageUrl(page,urldata) getRequest(url, dir, imgcallback, endcallback, page); } else { endcallback('下載完成,保存在' + dir) } } }); } }) } }); } //網站body分析,圖片整理,返回圖片地址數組 var imageArrangement = function(body, url, errorcallback, successcallback) { //errorcallback:網站爬取失敗回調 var $ = cheerio.load(body); //找到img所在標籤目錄 var images = $('img'); //排除非正常圖片 images.each(function(index, item) { var img = $(this) var src = img.attr('src') + ""; //base64暫時不下載 if(src == 'undefined' || src.length == 0 || src.indexOf('base64') > -1) { images.splice(index, 1) return; } }); var imageSrcArr = []; images.each(function(index, item) { var img = $(this) var src = item.attribs.src.split('?')[0]; //下載文件路徑含有search時會形成下載失敗 //獲取文件根路徑 var rootPathname = urlm.parse(src).pathname.substr(0, 1) == '/' ? urlm.parse(src).pathname : '/' + urlm.parse(src).pathname; //防止圖片地址不完整重新拼接 if(!/(http|https)/.test(src)) { src = src.substr(0, 2) == '//' ? 'http:' + src : 'http://' + urlm.parse(url).host + rootPathname; } else if(!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(src)) { //排除非圖片後綴 return; } imageSrcArr.push(src) }) //圖片數目爲0,返回錯誤結束響應 if(!imageSrcArr.length) { //網站不包含圖片標籤,結束下載 errorcallback('下載失敗:該網站不包含圖片或者網站禁止爬蟲') return; } successcallback(imageSrcArr) } //圖片下載方法,保存到dir var download = function(url, dir, filename, callback) { request.head(url, function(err, res, body) { if(err) console.log(err) //callback下載完成回調 //傳入流:fsa.pipe(fsb),fsa必須是存在的流,但fsb不存在會建立;pipe方法是可讀流導入可寫流 request(url).pipe(fs.createWriteStream(dir + "/" + filename)).on('close', function() { //下載完成傳回地址,錯誤信息 callback(url, err) }) }); }; //獲取分頁真實地址 function getPageUrl(page, url) { var urldata = urlm.parse(url); if(!urldata.protocol && !urldata.host) { //請求出地址不完整,結束 //return; } if( page.begin && page.end ) { page.BeginIndex = page.begin.replace(/[^0-9]/ig, ""); page.EndIndex = page.end.replace(/[^0-9]/ig, ""); var pageIag = page.begin.replace(/\d+/g, ''); page.currentIndex = page.currentIndex || page.BeginIndex; if(page.BeginIndex && page.EndIndex) { //hash分頁 if(/#/.test(page.begin) && /#/.test(page.end)) { url = urldata.protocol + '//' + urldata.host + urldata.path + pageIag + page.currentIndex //querystring分頁 } else if(/=/.test(page.begin) && /=/.test(page.end)) { var querystringData = querystring.parse(url); } } } return url; } //導出下載模塊 exports.downloadimg = downloadimg;