0%

想说 node爱你不容易啊

之前写的图片下载器,使用 python 和 C#,今儿个使用 nodejs 重写,使用 cheerio 库,和 jq 比较像

代码是没问题,我出问题了…

  • http.request 没有同步版本,导致一开始就回调…
  • http.request 参数要么是 string,要么是 option,必须要分 host,path,但是这个下载器必须带 User-Agent 的 header
  • request 类库 option 可以使用 url|uri,但是回调函数,callback(err,res,body),body 是 utf8 编码,gbk 中文乱码,而 res 是 request 类库处理之后传回的,此时给 res 加上 on data,end 事件接收数据无效,使用 pipe 也无效
  • 获取页面的 srcs,判断是否有下一页,同步代码一个 while 搞定,node 使用 async.whilst 也可以,之前使用 if + 递归搞定,但是内存占用更多,不好的方法,使用 whilst 代替
  • 下载完回调,log 一下耗时,本想使用 async.each,但是这货不提供 index 呀,自己实现 index++,可能不能保证顺序呀,使用[].forEach,自己实现 done 函数,判断 finished 看是否全部完成,呢吗 async 内部也是这么写的好么!!!我就想实现个回调而已啊!!!!
  • 下载文件,res.pipe(fs-stream),此时要给 fs.WriteStream 加上 finish 事件,要不然,程序不管有没有接受完,就退出了

网络差的时候,程序莫名报错…打断点停不下来…异步么,啊啊啊!!!
python 的大概也就 90 多行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
var http = require("http");
var urlFn = require("url");
var pathFn = require("path");
var fs = require("fs");
var util = require("util");

var iconv = require("iconv-lite");
var cheerio = require("cheerio");
var async = require("async");

/*
* configs
*/
var page_encoding = "gbk"; // 'gbk'
var debuging = true; //use example url
var example_url = "----------------url------------------";
var image_dir = "iamge";

function get_title($) {
// 文件夹名称
return $("title").text();
}

function get_page_srcs($) {
// 获取当前page imgs src
var srcs = $(".img_box img").map(function() {
return $(this).attr("src");
});
return [].slice.call(srcs);
}

function has_next_page($) {
// 是否有next page

return (
$(".pages")
.eq(1)
.find("a")
.last()
.attr("href") !== "#"
);
}

function next_page_url($) {
// next page url

return $(".pages")
.eq(1)
.find("a")
.last()
.attr("href");
}
//--------------------------------------------------------------

var url = "";
if (debuging) {
url = example_url;
} else if (process.argv.length > 2) {
url = process.argv[2]; // node down a
} else {
return console.log(
"\n\
Usage : node down url\n\
url like : %s\n\
",
example_url
);
}

var $;
var title;
var start = new Date();
getHtml(url, function(err, html) {
$ = cheerio.load(html);
title = get_title($);
var dir = image_dir + "/" + title + "/";
!fs.existsSync(image_dir) && fs.mkdirSync(image_dir);
!fs.existsSync(dir) && fs.mkdirSync(dir);

getSrcs(url, function(srcs) {
var count = srcs.length;
console.log("共%s张", count);

srcs.forEach(function(src, index) {
var ext = pathFn.extname(src) || ".jpg";
var dest = dir + (index + 1) + ext;
download(src, dest, function() {
console.log("%s 完成下载 ...", index + 1);
//done();
});
});

var finished = 0;

function done() {
finished++;
if (finished == count) {
console.log("完成下载,耗时 : %s秒", (new Date() - start) / 1000);
}
}
});
});

function getHtml(url, callback) {
// callback(err,html)
var parsed = urlFn.parse(url);
callback = callback || function() {};

http
.request(
{
host: parsed.host,
path: parsed.path,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2101.0 Safari/537.36"
}
},
function(res) {
//response event handler

var buffer = new Buffer(0);
res.on("data", function(buf) {
buffer = Buffer.concat([buffer, buf]);
});
res.on("end", function() {
var html = "";
if (Buffer.isEncoding(page_encoding)) {
//utf8
html = buffer.toString(page_encoding);
} else {
html = iconv.decode(buffer, page_encoding);
}
// console.log(html);
callback(null, html);
});
}
)
.on("error", callback)
.end();
}

function getSrcs(url, callback) {
// callback(srcs)
var srcs = [];

getHtml(url, function(err, html) {
if (err) throw err;

//main page
var $ = cheerio.load(html);
var cur_url = url; //当前url
var cur_srcs = get_page_srcs($).map(function(src) {
// 当前页面的src
return urlFn.resolve(cur_url, src);
});
//add main page srcs
srcs = srcs.concat(cur_srcs);

//next pages
async.whilst(
function() {
return has_next_page($);
},
function(next) {
cur_url = urlFn.resolve(cur_url, next_page_url($)); // change to next page

//next page
getHtml(cur_url, function(err, html) {
if (err) throw err;

$ = cheerio.load(html);
var cur_srcs = get_page_srcs($).map(function(src) {
// 当前页面的src
return urlFn.resolve(cur_url, src);
});
srcs = srcs.concat(cur_srcs);
next();
});
},
function(err) {
if (err) throw err;

callback(srcs);
}
);
});
}

function download(src, dest, end) {
end = end || function() {};

var parsed = urlFn.parse(src);
http
.get(
{
host: parsed.host,
path: parsed.path,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2101.0 Safari/537.36"
}
},
function(res) {
// http://stackoverflow.com/questions/11944932/how-to-download-a-file-with-node-js
res.pipe(fs.createWriteStream(dest)).on("finish", function() {
this.close(end);
});
}
)
.on("error", function(err) {
throw err;
})
.end();
}