js 爬虫抓取网页数据( Control的异步和回调知识的逻辑 )

优采云 发布时间: 2021-10-09 00:00

  js 爬虫抓取网页数据(

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就方便多了。

  我想在这里再提一件事。最初保证所有插入都完成。这个操作可以在SQL层实现,也就是事务,但是node-mysql在我使用的时候还是不能很好的支持事务,所以只好手动使用代码来保证。

  解析 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

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线