网页爬虫抓取百度图片( Control的异步和回调知识的逻辑 )
优采云 发布时间: 2022-01-18 12:10网页爬虫抓取百度图片(
Control的异步和回调知识的逻辑
)
Node.js写爬虫的基本思路及抓取百度图片的例子分享
更新时间:2016-03-12 17:32:27 作者:qiaolevip
本篇文章主要介绍Node.js编写爬虫的基本思路和抓取百度图片的例子分享。作者提到了GBK转码需要特别注意的转码问题。有需要的朋友可以参考以下
其实写爬虫的思路很简单:
但是当我真正写这个爬虫的时候,还是遇到了很多问题(和我基础不够扎实,也没有认真研究过node.js有很大关系)。主要原因是node.js的异步和回调知识没有完全掌握,导致在编写代码的过程中走了不少弯路。
模块化的
模块化对于 node.js 程序非常重要。不能像写PHP一样把所有代码都扔到一个文件里(当然这只是我个人的恶习),所以有必要一开始就分析一下这个爬虫需要实现的功能。,大致分为三个模块。
主程序,调用爬虫模块和持久化模块,实现完整的爬虫功能
爬虫模块根据传入的数据发送请求,解析HTML并提取有用数据,返回一个对象
持久性模块,接受一个对象并将其内容存储在数据库中
模块化也带来了一个困扰我一个下午的问题:模块之间的异步调用导致数据错误。其实我还是不太明白是什么问题,因为脚本语言的调试功能不方便,所以还没有深入研究过。
还有一点需要注意的是,在模块化的时候,尽量谨慎的使用全局对象来存储数据,因为可能你的模块的某个功能还没有结束,而全局变量已经被修改了。
控制流
这个东西很难翻译,直译就叫控制流(?)。众所周知,node.js的核心思想是异步,但是如果异步多了,就会有好几层嵌套,代码真的很难看。此时,您需要借助一些控制流模块重新排列逻辑。这里我们推荐 async.js(),它在开发社区中非常活跃且易于使用。
async提供了很多实用的方法,我主要在写爬虫的时候使用
这些控制流方式给爬虫的开发带来了极大的便利。考虑这样一个应用场景,需要向数据库中插入多条数据(属于同一个学生),并且需要在插入所有数据后返回结果,那么如何保证所有的插入操作都结束了? 它只能通过回调层来保证。使用 async.parallel 要方便得多。
这里还要提一件事,本来是为了保证所有的insert都完成,这个操作可以在SQL层实现,也就是transaction,但是node-mysql在我使用的时候还是不能很好的支持transaction,所以只能用代码手动保证。
解析 HTML
解析过程中也遇到了一些问题,记录在这里。
发送 HTTP 请求获取 HTML 代码最基本的方法是使用 node 自带的 http.request 函数。如果是抓取简单的内容,比如获取指定id元素的内容(常用于抓取商品价格),那么正则就足够完成任务了。但是对于复杂的页面,尤其是数据项很多的页面,使用 DOM 会更加方便和高效。
node.js 的最佳 DOM 实现是cheerio()。其实,cheerio 应该算是 jQuery 的一个子集,针对 DOM 操作进行了优化和精简,包括了 DOM 操作的大部分内容,去掉了其他不必要的内容。使用cheerio,您可以像使用普通的jQuery 选择器一样选择您需要的内容。
下载图片
在爬取数据时,我们可能还需要下载图片。其实下载图片的方式和普通网页没有太大区别,但是有一点让我很苦恼。
请注意下面代码中的起泡注释,这是我年轻时犯的错误......
var req = http.request(options, function(res){
//初始化数据!!!
var binImage = '';
res.setEncoding('binary');
res.on('data', function(chunk){
binImage += chunk;
});
res.on('end', function(){
if (!binImage) {
console.log('image data is null');
return null;
}
fs.writeFile(imageFolder + filename, binImage, 'binary', function(err){
if (err) {
console.log('image writing error:' + err.message);
return null;
}
else{
console.log('image ' + filename + ' saved');
return filename;
}
});
});
res.on('error', function(e){
console.log('image downloading response error:' + e.message);
return null;
});
});
req.end();
GBK转码
另一个值得解释的问题是node.js爬虫爬取GBK编码内容时的转码问题。其实这个问题很容易解决,只是新手可能会走弯路。这是所有的源代码:
var req = http.request(options, function(res) {
res.setEncoding('binary');
res.on('data', function (chunk) {
html += chunk;
});
res.on('end', function(){
//转换编码
html = iconv.decode(html, 'gbk');
});
});
req.end();
我这里使用的转码库是iconv-lite(),完美支持GBK、GB2312等双字节编码。
示例:爬虫批量下载百度图片
<p>
var fs = require('fs'),
path = require('path'),
util = require('util'), // 以上为Nodejs自带依赖包
request = require('request'); // 需要npm install的包
// main函数,使用 node main执行即可
patchPreImg();
// 批量处理图片
function patchPreImg() {
var tag1 = '摄影', tag2 = '国家地理',
url = 'http://image.baidu.com/data/imgs?pn=%s&rn=60&p=channel&from=1&col=%s&tag=%s&sort=1&tag3=',
url = util.format(url, 0, tag1, tag2),
url = encodeURI(url),
dir = 'D:/downloads/images/',
dir = path.join(dir, tag1, tag2),
dir = mkdirSync(dir);
request(url, function(error, response, html) {
var data = JSON.parse(html);
if (data && Array.isArray(data.imgs)) {
var imgs = data.imgs;
imgs.forEach(function(img) {
if (Object.getOwnPropertyNames(img).length > 0) {
var desc = img.desc || ((img.owner && img.owner.userName) + img.column);
desc += '(' + img.id + ')';
var downloadUrl = img.downloadUrl || img.objUrl;
downloadImg(downloadUrl, dir, desc);
}
});
}
});
}
// 循环创建目录
function mkdirSync(dir) {
var parts = dir.split(path.sep);
for (var i = 1; i