如何用nodejs + async抓取一个网站的图片~

news/2024/4/29 14:05:09/文章来源:https://blog.csdn.net/weixin_34117522/article/details/89411781

准备工具

  1. 既然是用nodejs来抓取, 安装nodejs就是必须的

  2. 我们需要用async来控制流程, 用cheerio来解析页面, 用mkdirp来创建目录, 用request来抓取页面

  3. async的具体教程参考 https://github.com/caolan/async

步骤

创建packed.json, 内容如下

{"name": "spider","version": "0.0.1","description": "spider","main": "app.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"repository": {},"keywords": ["spider"],"author": "LinCenYing","license": "MIT","dependencies": {"async": "^2.0.0-rc.5","cheerio": "^0.18.0","colors": "^1.1.2","mkdirp": "^0.5.0","request": "^2.51.0","url": "^0.10.2"}
}

执行 npm install 命令安装相关依赖

创建app.js文件, 代码如下

var colors = require('colors');
colors.setTheme({silly: 'rainbow',input: 'grey',verbose: 'cyan',prompt: 'red',info: 'green',data: 'blue',help: 'cyan',warn: 'yellow',debug: 'magenta',error: 'red'
});
var node = {async: require('async'),cheerio: require('cheerio'),fs: require('fs'),mkdirp: require('mkdirp'),path: require('path'),request: require('request'),url: require('url')
};
var Spider = {/*** 配置选项*/options: {// 网站地址uri: 'http://blog.naver.com/PostList.nhn?from=postList&blogId=tomiaaa&currentPage=',// 保存到此文件夹saveTo: './tomiaaa',// 从第几页开始下载startPage: 1,// 到第一页结束endPage: 388,// 图片并行下载上限downLimit: 2},posts: [],/*** 开始下载(程序入口函数)*/start() {var async = node.async;async.waterfall([this.getPages.bind(this),this.downAllImages.bind(this)], (err, result) => {if (err) {console.log('error: %s'.error, err.message);} else {console.log('success: 下载完毕'.info);}});},/*** 爬取所有页面*/getPages(callback) {var async = node.async;var i = this.options.startPage || 1;async.doWhilst((callback) => {var uri = this.options.uri + '' + i;async.waterfall([this.downPage.bind(this, uri, i),this.parsePage.bind(this)], callback);i++;}, (page) => this.options.endPage > page, callback);},/*** 下载单个页面*/downPage(uri, curpage, callback) {console.log('开始下载页面:%s', uri);var options = {url: uri,headers: {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.54 Safari/537.36','Cookie': 'lang_set=zh;'}};node.request(options, (err, res, body) => {if (!err) console.log('下载页面成功:%s'.info, uri);var page = {page: curpage,uri: uri,html: body};callback(err, page);});},/*** 解析单个页面并获取数据*/parsePage(page, callback) {console.log('开始分析页面数据:%s', page.uri);var $ = node.cheerio.load(page.html);var $posts = $('._photoImage');var self = this;var src = [];$posts.each(function() {var href = $(this).attr('src').split("?")[0];src.push(href)});self.posts.push({loc: src,title: "page" + page.page});console.log('分析页面数据成功,共%d张图片'.info, $posts.length);callback(null, page.page);},/*** 下载全部图片*/downAllImages(page, callback) {var async = node.async;console.log('开始全力下载所有图片,共%d篇', this.posts.length);async.eachSeries(this.posts, this.downPostImages.bind(this), callback);},/*** 下载单个页面的图片* @param  {Object} post*/downPostImages(post, callback) {var async = node.async;async.waterfall([this.mkdir.bind(this, post),this.downImages.bind(this),], callback);},/*** 创建目录*/mkdir(post, callback) {var path = node.path;post.dir = path.join(this.options.saveTo, post.title);console.log('准备创建目录:%s', post.dir);if (node.fs.existsSync(post.dir)) {callback(null, post);console.log('目录:%s 已经存在'.error, post.dir);return;}node.mkdirp(post.dir, function(err) {callback(err, post);console.log('目录:%s 创建成功'.info, post.dir);});},/*** 下载post图片列表中的图片*/downImages(post, callback) {console.log('发现%d张图片,准备开始下载...', post.loc.length);node.async.eachLimit(post.loc, this.options.downLimit, this.downImage.bind(this, post), callback);},/*** 下载单个图片*/downImage(post, imgsrc, callback) {var url = node.url.parse(imgsrc);var fileName = node.path.basename(url.pathname);var toPath = node.path.join(post.dir, fileName);console.log('开始下载图片:%s,保存到:%s', fileName, post.dir);node.request(encodeURI(imgsrc)).pipe(node.fs.createWriteStream(toPath)).on('close', () => {console.log('图片下载成功:%s'.info, imgsrc);callback();}).on('error', callback);}
};
Spider.start();

做一些对应的修改, 然后执行 node app.js

详细说明

var colors = require('colors');
colors.setTheme({silly: 'rainbow',input: 'grey',verbose: 'cyan',prompt: 'red',info: 'green',data: 'blue',help: 'cyan',warn: 'yellow',debug: 'magenta',error: 'red'
});

上面的代码, 只是给console.log加个颜色, 好看点, 没其他用处

var node = {async: require('async'),cheerio: require('cheerio'),fs: require('fs'),mkdirp: require('mkdirp'),path: require('path'),request: require('request'),url: require('url')
};

加载相关依赖

抓取网站图片的思路很简单, 先大概了解下目标网站的结构, 一般来说结构都是, 1个图片的目录页面, 1个图片的详细页, 当然也有一些网站, 没有目录页, 只有详细页, 这样就更简单了...

下面我们就以http://blog.naver.com/PostList.nhn?from=postList&blogId=tomiaaa 这个博客做例子

这个博客就是没有目录页, 所以抓起来也简单, 点击下分页, 就可以知道分页参数是什么, 这个博客的分页参数是currentPage

简单了解下, 网站结构, 就可以开始写代码了...

var Spider = {}

先定义个对象

options: {// 网站地址uri: 'http://blog.naver.com/PostList.nhn?from=postList&blogId=tomiaaa&currentPage=',// 保存到此文件夹saveTo: './tomiaaa',// 从第几页开始下载startPage: 1,// 到第一页结束endPage: 388,// 图片并行下载上限downLimit: 2
}
posts: [],

把一些配置文件写一下, posts这个数组, 用来存后面抓到的数据

start() {var async = node.async;async.waterfall([this.getPages.bind(this),this.downAllImages.bind(this)], (err, result) => {if (err) {console.log('error: %s'.error, err.message);} else {console.log('success: 下载完毕'.info);}});
},

我们用async.waterfall来控制流程, 这里主要执行2个函数, this.getPages.bind(this)用来抓取页面, 获取图片的地址, this.downAllImages.bind(this)用来把图片下载到本地

getPages(callback) {var async = node.async;var i = this.options.startPage || 1;async.doWhilst((callback) => {var uri = this.options.uri + '' + i;async.waterfall([this.downPage.bind(this, uri, i),this.parsePage.bind(this)], callback);i++;}, (page) => this.options.endPage > page, callback);
},

我们用doWhilst来循环抓取所有页面, (page) => this.options.endPage > page的作用是, 当page大于我们设置的最大页数时, 停止抓取

每抓一个页面, 我们分成2个步骤, 1是this.downPage.bind(this, uri, i)抓取html文档, 2是this.parsePage.bind(this)将html文档解析, 并提取出文档中的图片地址

downPage(uri, curpage, callback) {console.log('开始下载页面:%s', uri);var options = {url: uri,headers: {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.54 Safari/537.36','Cookie': 'lang_set=zh;'}};node.request(options, (err, res, body) => {if (!err) console.log('下载页面成功:%s'.info, uri);var page = {page: curpage,uri: uri,html: body};callback(err, page);});
}

这个函数就是用request来抓取html文档, 其中options.url是抓取页面的链接, 这个会从上面的循环中传过来, options.headers主要定义请求头, 可以设置浏览器ua, cookies等等, 如果抓取需要登录的页面, 那么这里的cookies就必须要设置了, 将抓取到的html文档,当前页数,地址等信息通过callback返回去

parsePage(page, callback) {console.log('开始分析页面数据:%s', page.uri);var $ = node.cheerio.load(page.html);var $posts = $('._photoImage');var self = this;var src = [];$posts.each(function() {var href = $(this).attr('src').split("?")[0];src.push(href)});self.posts.push({loc: src,title: "page" + page.page});console.log('分析页面数据成功,共%d张图片'.info, $posts.length);callback(null, page.page);
},

这个函数的作用就是通过cheerio来解析页面, 执行var $ = node.cheerio.load(page.html);, 后面操作的方法和jq是一样一样的~~~将抓取到的图片地址存成数组, 写到之前定义好的posts数组中

这里需要将page.page传回去, 因为上面的doWhile需要这个值来判断是否到最后页

到这里getPages函数就结束了, 通过parsePage发回去的page.page, 不断循环,判断 直到将所有页面抓取完毕

页面都抓取完了, 图片地址也都有, 那么剩下的就是用downAllImages把图片保存到本地了

downAllImages(page, callback) {var async = node.async;console.log('开始全力下载所有图片,共%d篇', this.posts.length);async.eachSeries(this.posts, this.downPostImages.bind(this), callback);
},

这回我们用async.eachSeries来循环我们之前存好的posts数组, 这个函数的格式和$.each很像, 简单说, 就是将posts里的每个对象按顺序放到this.downPostImages.bind(this)里执行一次

downPostImages(post, callback) {var async = node.async;async.waterfall([this.mkdir.bind(this, post),this.downImages.bind(this),], callback);
},

还是用async.waterfall来控制流程, 先创建子目录, 然后下载图片

mkdir(post, callback) {var path = node.path;post.dir = path.join(this.options.saveTo, post.title);console.log('准备创建目录:%s', post.dir);if (node.fs.existsSync(post.dir)) {callback(null, post);console.log('目录:%s 已经存在'.error, post.dir);return;}node.mkdirp(post.dir, function(err) {callback(err, post);console.log('目录:%s 创建成功'.info, post.dir);});
},

通过我们设置的根目录和抓取到title创建子目录

downImages(post, callback) {console.log('发现%d张图片,准备开始下载...', post.loc.length);node.async.eachLimit(post.loc, this.options.downLimit, this.downImage.bind(this, post), callback);
},

这里用async.eachLimit来控制循环, 和async.eachSeries差不多, 不过eachLimit可以设置并发, 如果这个函数的第二个参数设置成1, 那么就和async.eachSeries完全一样了, 将post.loc里的图片, 按顺序放到this.downImage.bind(this, post)里执行

downImage(post, imgsrc, callback) {var url = node.url.parse(imgsrc);var fileName = node.path.basename(url.pathname);var toPath = node.path.join(post.dir, fileName);console.log('开始下载图片:%s,保存到:%s', fileName, post.dir);node.request(encodeURI(imgsrc)).pipe(node.fs.createWriteStream(toPath)).on('close', () => {console.log('图片下载成功:%s'.info, imgsrc);callback();}).on('error', callback);
}

通过url,path等插件, 读取图片名称, 通过request抓取图片, 用fs将图片保存到本地

clipboard.png

clipboard.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_821014.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中文编程,用python编写小说网站爬虫

中文编程,用python编写小说网站爬虫 - 乘风龙王的文章 - 知乎 https://zhuanlan.zhihu.com/p/51309019 我比较喜欢看小说,在网络上看小说一般有2种选择,正版或盗版。正版要钱,盗版要么只能在线阅读,要么下载下来一堆广…

第一章:web网站服务(一)

防伪码:拼一个春夏秋冬,赢一个无悔人生第一章:web网站服务(一) 实验报告1、卸载原来已经存在的httpd注:编译安装针对专业服务器,,避免端口冲突、程序冲突,--nodeps表忽略…

为什么您可能不应该将您的企业网站运出家门

Hosting a website out of your home is generally a bad idea. You certainly can, and if you’re just having fun learning to build a site, you won’t run into many issues. But if you’re serious about starting a business, it’s best to look for online hosting…

chrome浏览器 快捷键_在Google Chrome浏览器中搜索网站的关键字快捷键的完整指南...

chrome浏览器 快捷键Want to make your browsing more efficient in Chrome? Here’s how you can use keyword shortcuts to quickly find info on any site you want. 是否想在Chrome中提高浏览效率? 您可以通过以下方法使用关键字快捷方式在所需的任何网站上快…

百度 添加浏览器url_现在在现代浏览器中是否真的需要在网站URL中添加“ www”?...

百度 添加浏览器urlWhen we visit a website using our favorite browser, we usually just type in the basic part of the URL and ‘arrive’ at the desired location without any problems. But have we reached a point where we can start omitting ‘www’ from the UR…

chrome浏览器拦截广告_如何禁用Chrome的新广告拦截器(在某些网站或所有网站上)...

chrome浏览器拦截广告Google Chrome now has a built-in ad blocker, designed to get rid of the ads that are intrusive or otherwise annoying, but allow ads from sites that follow specific guidelines. If you’re not into the idea of letting your browser control…

asp租用和saas租用_最佳下载,租用和购买有声读物的网站

asp租用和saas租用We recently published a list of websites where you can download free eBooks, or purchase, borrow, or rent eBooks. However, if you would rather listen to your favorite books, here are some websites offering audiobooks you can download, rent…

如何下载在线玩的游戏_在线下载游戏和玩游戏的最佳网站

如何下载在线玩的游戏This week we have a list of fun websites for you. Playing games can help relieve stress and provide a break from work. The following are some websites we found offering free online games, freeware games for download, or games you can pu…

谷歌搜索设置只在某网站搜索_Google助理可以在某一天为您打电话

谷歌搜索设置只在某网站搜索Last week Google demoed Duplex, an AI intended to make phone calls on users’ behalf. Depending on who you ask it’s either mind blowing or dystopic. 上周Google演示了Duplex,这是一种AI,旨在代表用户拨打电话。 根…

如何在Google Chrome浏览器中举报网络钓鱼和恶意网站

Google now makes it much easier to report “suspicious websites” in Chrome. Websites you might want to report include phishing websites, sites hosting malware, and similar bad things. Google will use these reports to block websites for everyone. Google现在…

如何在iPhone和iPad上的Safari中阻止网站

If you’re an iPhone or iPad owner (especially one whose children use your device), you might occasionally want to block access to a specific website. Whether that website is one of adult nature or just something, you don’t want your children to be able t…

编程爱好者网站_读书爱好者的最佳免费网站

编程爱好者网站For many people, reading is less of a hobby and more of a passion. Here are some of the best websites for the book lover in all of us. 对于许多人来说,阅读不再是一种爱好,而是一种激情。 这是我们所有人中对于书迷的最佳网站。…

python爬虫捕鱼网站_古法捕鱼,千年绝技

文:魔力 图:来自网络先民在进入农耕文化之前,打渔狩猎才是他们的谋生手段。经过了千百年,先辈们用经验和智慧创造许多捕鱼的方法。原来先辈创造的捕鱼方法,人们现在已经不再使用。我们现在看到的都是炸鱼、毒鱼、电鱼……

草根站长心酸路:你的网站后来怎么样了?

作为一个站长,下面这些你一定都懂:1、没有资金,连空间都用免费的2、没有技术,连域名都不知道怎样解析3、没有人缘,碰到问题没有人能够帮助4、受人鄙视,有时请教一些所谓的高手,等了48小时后都得…

针对单个网站的渗透思路(精)

本人Web安全初学者,从老师那里获得了一套很完整的针对单一网站的渗透思路今天起的早,就自己试着总结一份,记下来。分享给大家。首先,当我们拿到一个网站的域名或者IP的时候。最先要做的是信息收集。下面着重介绍一下信息收集模块一…

关于为什么同一网站下返回的404页面不一样

今天在上班的时候偶然发现访问同一个网站,不同目录时返回的404页面不同。具体效果如下:情况1:情况2:因为是公司签了保密协议的东西,漏洞还未修复,所以我把IP盖住了,不过这俩确实是同意网站不同路…

ipad和iphone适配_如何在iPhone和iPad上通过搜索打开应用程序,网站和快捷方式

ipad和iphone适配Khamosh PathakKhamosh Pathak If you’re not using the Search feature on your iPhone or iPad, you’re missing out. Instead of using the home screen to launch apps or Safari to open web pages, try using the universal search feature for a much…

sharepoint 2010 网站集定期备份

SharePoint 2010 是为企业用户提供基于微软SharePoint平台的工作流扩展。用户无需编写代码就可以快速、便捷地设计任务表单和业务流程,从而帮助企业用户进一步拓展SharePoint平台的内容管理,内外部协同和企业业务流程管理能力,并可将SharePoi…

响应式网站与自适应网站比较

Adaptive website(自适应网站) 应对在浏览器的宽度变化不调整网页元素的位置,缩放网页元素,以适应在可用空间。 Responsive website (响应式网站) 应对在浏览器的宽度变化通过调整网页元…

网站重定向次数过多怎么解决_面试官:CPU飙高及Full GC次数过多怎么排查?

处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题。当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警。本文主要针对系统运行缓慢这一问题,提供该问题的排…