最近一直在写微信小程序,没时间看nodejs,今天就把之前写的图片爬虫拉出来晒一晒,免得彻底忘记~写的还不完善,还没时间继续,请开始嘲笑我的烂代码~
首页展示
开始爬取图片
下载图片
前端界面 index.html
<!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>
index.js(启动 node index.js)
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)
}
}
downloadimg.js
//依赖模块
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;