这里的断点续传不是文件下载时的断点续传,而是指在爬行页面时有时会遇到各种网络中断而从中断前的页面及其数据继续爬行的过程,这个过程和断点续传原理上相似故以此命名。我的具体做法是:在下载出现故障或是图片已经全部获得时,将存储目录,当前爬行页面和已经获取的图片地址以json形式存储到数据文件中,而用户选择断点续传模式时提取数据文件中的这三条信息,继而从上次中断之处重新运行。

数据文件示例:

  1. {"url":"http://m.03122.com/gaoqing/9353/27.html","pictures":["http://img.cdjqjy.com/pic/gqimg/94/b36ab1a61b3c19d84386ed69ffcdad16.jpg","http://img.cdjqjy.com/pic/gqimg/94/fd4b2a40df592105725d7374f101d86c.jpg","http://img.cdjqjy.com/pic/gqimg/94/cd742a03afa54603d39b2a0d67190cfb.jpg","http://img.cdjqjy.com/pic/gqimg/94/69493144d80120ce1631d98b77ab667a.jpg"],"folder":"pictures(2017-11-18 15_5_31)"}

Node.js 代码如下:

  1. //======================================================
  2. // mm131图片批量下载爬虫1.01
  3. // 1.00 具备功能
  4. // 1.01 增加断点续传
  5. // 2017年11月15日
  6. //======================================================
  7.  
  8. // 内置http模块
  9. var http=require("http");
  10.  
  11. // 内置文件处理模块,用于创建目录和图片文件
  12. var fs=require('fs');
  13.  
  14. // 用于转码。非Utf8的网页如gb2132会有乱码问题,需要iconv将其转码
  15. var iconv = require('iconv-lite');
  16.  
  17. // cheerio模块,提供了类似jQuery的功能,用于从HTML code中查找图片地址和下一页
  18. var cheerio = require("cheerio");
  19.  
  20. // 请求参数JSON。http和https都有使用
  21. var options;
  22.  
  23. // request请求
  24. var req;
  25.  
  26. // 图片数组,找到的图片地址会放到这里
  27. var pictures=[];
  28.  
  29. // 存放图片的目录
  30. var folder="";
  31.  
  32. //--------------------------------------
  33. // 爬取网页,找图片地址,再爬
  34. // pageUrl sample:http://www.mm131.com/xinggan/2852.html
  35. // pageUrl sample:http://www.mm131.com/xinggan/2853.html
  36. // pageUrl sample:http://www.mm131.com/xinggan/2976.html
  37. //--------------------------------------
  38. function crawl(pageUrl){
  39. console.log("Current page="+pageUrl);
  40.  
  41. // 得到hostname和path
  42. var currUrl=pageUrl.replace("http://","");
  43. var pos=currUrl.indexOf("/");
  44. var hostname=currUrl.slice(0,pos);
  45. var path=currUrl.slice(pos);
  46. //console.log("hostname="+hostname);
  47. //console.log("path="+path);
  48.  
  49. // 初始化options
  50. options={
  51. hostname:hostname,
  52. port:80,
  53. path:path,// 子路径
  54. method:'GET',
  55. };
  56.  
  57. req=http.request(options,function(resp){
  58. var html = [];
  59.  
  60. resp.on("data", function(data) {
  61. html.push(data);
  62. })
  63. resp.on("end", function() {
  64. var buffer = Buffer.concat(html);
  65. var body = iconv.decode(buffer,'gb2312'); // 特地增加的,为了让汉字不乱码
  66. //console.log(body);
  67.  
  68. var $ = cheerio.load(body);
  69. var picCount=0;
  70.  
  71. // 找图片放入数组
  72. $(".content-pic a img").each(function(index,element){
  73. var picUrl=$(element).attr("src");
  74. //console.log(picUrl);
  75.  
  76. if(picUrl.indexOf('.jpg')!=-1){
  77. pictures.push(picUrl);
  78. picCount++;
  79. }
  80. })
  81. console.log("找到图片"+picCount+"张.");
  82.  
  83. var nextPageUrl=null;
  84. // 找下一页
  85. $(".content-page a").each(function(index,element){
  86. var text=$(element).text();
  87. if(text.indexOf('下一页')!=-1){
  88. nextPageUrl=$(element).attr("href");
  89. nextPageUrl="http://www.mm131.com/xinggan/"+nextPageUrl;// 把省略部分加上
  90. console.log("找到下一页.");
  91. }
  92. })
  93.  
  94. if(nextPageUrl==null){
  95. console.log(pageUrl+"已经是最后一页了.\n");
  96. saveFile(pageUrl,pictures);// 保存
  97. download(pictures);
  98. }else{
  99. //console.log("下一页是"+nextPageUrl);
  100. crawl(nextPageUrl);
  101. }
  102.  
  103. }).on("error", function() {
  104. saveFile(pageUrl,pictures);// 保存
  105. console.log("crawl函数失败,请进入断点续传模式继续进行");
  106. })
  107. });
  108.  
  109. // 超时处理
  110. req.setTimeout(7500,function(){
  111. req.abort();
  112. });
  113.  
  114. // 出错处理
  115. req.on('error',function(err){
  116. console.log('请求发生错误'+err);
  117. saveFile(pageUrl,pictures);// 保存
  118. console.log("crawl函数失败,请进入断点续传模式继续进行");
  119. });
  120.  
  121. // 请求结束
  122. req.end();
  123. }
  124.  
  125. //--------------------------------------
  126. // 下载图片
  127. //--------------------------------------
  128. function download(pictures){
  129.  
  130. var total=0;
  131. total=pictures.length;
  132. console.log("总计有"+total+"张图片将被下载.");
  133. appendToLogfile(folder,"总计有"+total+"张图片将被下载.\n");
  134. for(var i=0;i<pictures.length;i++){
  135. var picUrl=pictures[i];
  136. downloadPic(picUrl,folder);
  137. }
  138. }
  139.  
  140. //--------------------------------------
  141. // 写log文件
  142. //--------------------------------------
  143. function appendToLogfile(folder,text){
  144. fs.appendFile('./'+folder+'/log.txt', text, function (err) {
  145. if(err){
  146. console.log("不能书写log文件");
  147. console.log(err);
  148. }
  149. });
  150. }
  151.  
  152. //--------------------------------------
  153. // 取得当前时间
  154. //--------------------------------------
  155. function getNowFormatDate() {
  156. var date = new Date();
  157. var seperator1 = "-";
  158. var seperator2 = "_";
  159. var month = date.getMonth() + 1;
  160. var strDate = date.getDate();
  161. if (month >= 1 && month <= 9) {
  162. month = "0" + month;
  163. }
  164. if (strDate >= 0 && strDate <= 9) {
  165. strDate = "0" + strDate;
  166. }
  167. var currentdate =date.getFullYear() + seperator1 + month + seperator1 + strDate
  168. + " " + date.getHours() + seperator2 + date.getMinutes()
  169. + seperator2 + date.getSeconds();
  170. return currentdate;
  171. }
  172.  
  173. //--------------------------------------
  174. // 下载单张图片
  175. // picUrl sample:http://img2.mm131.com:55888/pic/2852/1.jpg
  176. //--------------------------------------
  177. function downloadPic(picUrl,folder){
  178. console.log("图片:"+picUrl+"下载开始");
  179.  
  180. // 得到hostname,path和port
  181. var currUrl=picUrl.replace("http://","");
  182. var pos=currUrl.indexOf("/");
  183. var hostname=currUrl.slice(0,pos);
  184. var path=currUrl.slice(pos);
  185.  
  186. // 有端口加端口,没有端口默认80
  187. var port=80;
  188. if(hostname.indexOf(":")!=-1){
  189. var arr=hostname.split(":");
  190. hostname=arr[0];
  191. port=arr[1];
  192. }
  193.  
  194. //console.log("hostname="+hostname);
  195. //console.log("path="+path);
  196. //console.log("port="+port);
  197.  
  198. var picName=currUrl.slice(currUrl.lastIndexOf("/"));
  199.  
  200. // 初始化options
  201. options={
  202. hostname:hostname,
  203. port:port,
  204. path:path,// 子路径
  205. method:'GET',
  206. headers:{
  207. 'Referer':'http://www.mm131.com',
  208. }
  209. };
  210.  
  211. req=http.request(options,function(resp){
  212. var imgData = "";
  213. resp.setEncoding("binary");
  214.  
  215. resp.on('data',function(chunk){
  216. imgData+=chunk;
  217. });
  218.  
  219. resp.on('end',function(){
  220.  
  221. // 创建文件
  222. var fileName="./"+folder+picName;
  223. fs.writeFile(fileName, imgData, "binary", function(err){
  224. if(err){
  225. console.log("[downloadPic]文件 "+fileName+" 下载失败.");
  226. console.log(err);
  227. appendToLogfile(folder,"文件 "+picUrl+" 下载失败.\n");
  228. }else{
  229. appendToLogfile(folder,"文件 "+picUrl+" 下载成功.\n");
  230. console.log("文件"+fileName+"下载成功");
  231. }
  232. });
  233. });
  234. });
  235.  
  236. // 超时处理
  237. req.setTimeout(7500,function(){
  238. req.abort();
  239. });
  240.  
  241. // 出错处理
  242. req.on('error',function(err){
  243. if(err){
  244. console.log('[downloadPic]文件 '+picUrl+" 下载失败,"+'因为'+err);
  245. appendToLogfile(folder,"文件"+picUrl+"下载失败.\n");
  246. }
  247. });
  248.  
  249. // 请求结束
  250. req.end();
  251. }
  252.  
  253. //--------------------------------------
  254. // 程序入口
  255. //--------------------------------------
  256. function getInput(){
  257. process.stdin.resume();
  258. process.stdout.write("\033[33m 新建模式输入第一页URL,断点续传模式输入0,请输入: \033[39m");// 草黄色
  259. process.stdin.setEncoding('utf8');
  260.  
  261. process.stdin.on('data',function(text){
  262. var input=text.trim();
  263. process.stdin.end();// 退出输入状态
  264.  
  265. if(text.trim()=='0'){
  266. process.stdout.write("\033[36m 进入断点续传模式. \033[39m"); // 蓝绿色
  267.  
  268. // Read File
  269. fs.readFile('./save.dat','utf8',function(err,data){
  270. if(err){
  271. console.log('读取文件save.dat失败,因为'+err);
  272. }else{
  273. //console.log(data);
  274. var obj=JSON.parse(data);
  275.  
  276. pictures=obj.pictures;
  277. console.log('提取图片'+pictures.length+'张');
  278.  
  279. folder=obj.folder;
  280.  
  281. // 创建目录
  282. fs.mkdir('./'+folder,function(err){
  283. if(err){
  284. console.log("目录"+folder+"已经存在");
  285. }
  286. });
  287.  
  288. crawl(obj.url);
  289. }
  290. });
  291.  
  292. // Resume crawl
  293. }else{
  294. process.stdout.write("\033[35m 进入新建模式. \033[039m"); //紫色
  295.  
  296. folder='pictures('+getNowFormatDate()+")";
  297. // 创建目录
  298. fs.mkdir('./'+folder,function(err){
  299. if(err){
  300. console.log("目录"+folder+"已经存在");
  301. }
  302. });
  303.  
  304. crawl(input);
  305. }
  306. });
  307. }
  308.  
  309. //--------------------------------------
  310. // 将爬行中信息存入数据文件
  311. //--------------------------------------
  312. function saveFile(url,pictures){
  313. var obj=new Object;
  314. obj.url=url;
  315. obj.pictures=pictures;
  316. obj.folder=folder;
  317. var text=JSON.stringify(obj);
  318.  
  319. fs.writeFile('./save.dat',text,function(err){
  320. if(err){
  321. console.log('写入文件save.dat失败,因为'+err);
  322. }
  323. });
  324. }
  325.  
  326. // 调用getInput函数,程序开始
  327. getInput();

2017年11月16日

Node.js mm131图片批量下载爬虫1.01 增加断点续传功能的更多相关文章

  1. Node.js mm131图片批量下载爬虫1.00 iconv协助转码

    //====================================================== // mm131图片批量下载爬虫1.00 // 2017年11月15日 //===== ...

  2. Node.js nvshens图片批量下载爬虫1.01

    //====================================================== // nvshens图片批量下载爬虫1.01 // 用最近的断点续传框架改写原有1.0 ...

  3. Node.js meitulu图片批量下载爬虫1.01版

    在 http://www.cnblogs.com/xiandedanteng/p/7614051.html 一文我曾经书写过一个图片下载爬虫,但原有程序不是为下载图片而设计故有些绕,于是稍微改写了一下 ...

  4. Node.js abaike图片批量下载爬虫1.02

    //====================================================== // abaike图片批量下载爬虫1.02 // 用最近的断点续传框架改写原有1.01 ...

  5. Node.js meitulu图片批量下载爬虫1.051

    原有1.05版程序没有断点续传模式,现在在最近程序基础上改写一版1.051. //====================================================== // m ...

  6. Node.js mzitu图片批量下载爬虫1.00

    又攻下一座山头. //====================================================== // mzitu图片批量下载爬虫1.00 // 2017年11月19 ...

  7. Node.js 4493图片批量下载爬虫1.00

    这个爬虫依然需要iconv转码,想不到如今非utf8的网页还这么多.另外此网页找下一页的方式比较异常,又再次借助了正则表达式. 代码如下: //============================ ...

  8. Node.js monly图片批量下载爬虫1.00

    此爬虫又用到了iconv转码,代码如下: //====================================================== // mmonly图片批量下载爬虫1.00 ...

  9. Node.js m03122图片批量下载爬虫1.00

    //====================================================== // m03122图片批量下载爬虫1.00 // 2017年11月18日 //==== ...

随机推荐

  1. windows系统中,创建临时环境变量

    以servlet-api.jar 包为例 set classpath=%classpath%;C:\apache-tomcat-6.0.37\lib\servlet-api.jar

  2. DelegatingFilterProxy干了什么?

    org.springframework.web.filter.DelegatingFilterProxy 一般情况,创建一个Filter是交给自己来实现的.基于servlet规范,在web.xml中配 ...

  3. Nginx日志统一格式

    统一格式如下:nginx.conf 纯文本: log_format main '$remote_addr - $remote_user [$time_local] "$request&quo ...

  4. What does a (+) sign mean in an Oracle SQL WHERE clause?

    This is an Oracle-specific notation for an outer join. It means that it will include all rows from t ...

  5. canvas元素内容生成图片

    转自https://segmentfault.com/a/1190000003853394 想要将canvas元素当前显示的内容生成为图像文件,我们首先要获取canvas中的数据,在HTML5 < ...

  6. [Arc074E] RGB Sequence

    [Arc074E] RGB Sequence Description 今天也在愉快地玩Minecraft!现在MM有一块1?N的空地,每个格子按照顺序标记为1到N.MM想要在这块空地上铺上红石块.绿宝 ...

  7. 【DFS】【图论】NOIP2014寻找道路

    [NOIP2014]寻找道路 题目描述 Description 在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1.路径上的所有点的出边所 ...

  8. 【随机化】Petrozavodsk Summer Training Camp 2016 Day 5: Petr Mitrichev Contest 14, Saturday, August 27, 2016 Problem I. Vier

    给你一个1~n的排列,让你找出4个下标a b c d,满足 (a+b)%n=(c+d)%n (w(a)+w(b))%n=(w(c)+w(d))%n,并且是非平凡解. 发现对于每个数i,找出两个数和为其 ...

  9. 20162304 实验二《Java面向对象程序设计》实验报告

    20162304 实验二<Java面向对象程序设计>实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 ...

  10. openfire安装完毕后无法登录控制台(忘记密码)的解决方法

    openfire登录管理控制提示: Login failed:make sure your username and password are correct and that you’re an a ...