node-爬虫

使用技术:
express + crawler + request
而crawler是集成了request+cheerio,由于某些方面crawler没有使用request那么方便,只是在爬虫上可以比request简单的实现爬虫,所以比较强大简单,而在node里比较流行,而node实现爬虫有些不够好,所以在这方面框架没有几个,不像python那么强大而且这方面的库很多,很专业,而node的使用后发现有些不太好,而且这方面都比较简单而粗略。

爬去电影天堂的代码

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
let express = require('express');
let router = express.Router();

let Crawler = require('crawler');
let fs = require('fs');
let path = require('path');
let request = require('request');
let mkdirs = require('../util/mkdirs');
let dataUrl = path.resolve(__dirname, '../../data');
let root = 'https://www.dytt8.net';

/* GET home page. */
router.get('/', function(req, res, next) {
let c = new Crawler({
// 在每个请求处理完毕后将调用此回调函数
callback : function (error, resData, done) {
if(error){
console.log(error);
res.render('index', { title: '电影天堂', content: error });
}else{
var $ = resData.$;
let $dataList = $(".bd3r .co_content8 ul table");
let paging = $(".bd3r .co_content8 .x");
let listData = [];

// 分页处理
let sel = paging.find('select');
let maxLen = sel.children().length;
let currPage = sel.find('[selected]').text();

$dataList.each((key, item)=>{
let $a = $(item).find('a');
let dac = $(item).find('font').text();
let content = $(":last-child",item).find('td').text();

dac.match(/(\d{4}-\d{2}-\d{2} [\d:]+)\D*(\d+)/);
listData.push({"name": $a.text(),
"href": root+$a.attr('href'),
"date": RegExp.$1,
"clickNum": RegExp.$2,
"content": content
});
});
createFs(currPage, listData);

let listUrl = [];
for(let i=2;i<=maxLen;i++){
listUrl.push(root+'/html/gndy/dyzz/list_23_'+i+'.html');
}
getPageNum(listUrl);
res.render('index', { title: '电影天堂', content: listData });
}
done();
}
});

// 将一个URL加入请求队列,并使用默认回调函数
c.queue(root+'/html/gndy/dyzz/list_23_1.html');
});

// 获取当前页的数据
function getPageNum(listUrl){
let c = new Crawler({
// 在每个请求处理完毕后将调用此回调函数
callback : function (error, resData, done) {
if(error){
console.log(error);
}else{
var $ = resData.$;
let $dataList = $(".bd3r .co_content8 ul table");
let paging = $(".bd3r .co_content8 .x");
let listData = [];

// 分页处理
let sel = paging.find('select');
let maxLen = sel.children().length;
let currPage = sel.find('[selected]').text();

$dataList.each((key, item)=>{
let $a = $(item).find('a');
let dac = $(item).find('font').text();
let content = $(":last-child",item).find('td').text();

dac.match(/(\d{4}-\d{2}-\d{2} [\d:]+)\D*(\d+)/);
listData.push({"name": $a.text(),
"href": root+$a.attr('href'),
"date": RegExp.$1,
"clickNum": RegExp.$2,
"content": content
});
});

createFs(currPage, listData);
}
done();
}
});

// 将一个URL加入请求队列,并使用默认回调函数
c.queue(listUrl);
}

// 遍历当前页的条数
async function createFs(page, content){
let url = path.join(dataUrl,'./'+page);
let file = path.join(url, './mkdir.json');
// 如果不存在创建目录,支持多层创建,异步
await mkdirs(url);
await (()=>{
let p = new Promise((resolve,reject)=>{
fs.writeFile(file, JSON.stringify(content), (err)=>{
if(err){
resolve(false);
console.log(err);
}else{
resolve(true);
}
});
});
return p;
})();
for(let item of content){
detailContent(item, url);
}
}

// 获取具体信息
async function detailContent(item, rootUrl){
let dirUrl = path.join(rootUrl,'./'+item.name.replace(/[\\\/:*?"<>|]/g,"-"));
let file = path.join(dirUrl,'./detail.json');
await mkdirs(dirUrl);

let c = new Crawler({
callback(error, resData, done){
if(error){
console.log(error);
}else{
let data = {};
let $ = resData.$;
let $content = $('.bd3r .co_area2');
let contentStr = $content.find('#Zoom p').eq(0).text();
let imgList = [];
data.title = $content.find('.title_all font').text();
data.content = contentStr.substr(0,contentStr.indexOf('【下载地址】'));
$content.find('#Zoom img').each(function(key, it){
imgList.push($(it).attr('src'));
});
data.img = imgList;
data.magnet = $content.find('[href^="magnet"]').attr('href');
data.thunder = $content.find('[vkorgxpv]').attr('vkorgxpv');
fs.writeFile(file, JSON.stringify(data), err=>{
if(err){
console.log(err);
}
});
// getImg(JSON.stringify(imgList), dirUrl);
}
done();
}
});
c.queue(item.href);
}

// 图片下载处理
// 由于某些问题无法正确获取到图片,并且会报错而无法爬取下去
function getImg(imgList, dirUrl){
// var c = new Crawler({
// encoding:null,
// jQuery:false,// set false to suppress warning message.
// callback:function(err, res, done){
// if(err){
// console.error(err.stack);
// }else{
// console.log(res.options.filename);
// console.log(res.body);
// fs.createWriteStream(res.options.filename).write(res.body);
// }
// done();
// }
// });
imgList = JSON.parse(imgList);
imgList = imgList.map((item, key)=>{
return {uri: item, filename: path.join(dirUrl,"./"+key+".jpg")};
});
// c.queue(imgList);
for(let ii = 0; ii<imgList.length; ii++){
if(!fs.existsSync(imgList[ii].filename)){
setTimeout(()=>{
let options = {
url: imgList[ii].uri,
headers: {
'content-length': 1001976
}
};

request(options).pipe(fs.createWriteStream(imgList[ii].filename)).on('close',function(){
console.log("关闭..."+imgList[ii].filename);
});
},1000);
}
}
}

module.exports = router;

在文字爬去方面一点问题都没有,但在图片方面都各种问题出现,而图片量大时不要使用pipe,会出现0k的图片,既空图片,而且要使用异步的形式下载图片,如使用bagpipe或async方法下载图片。

// 图片下载方式

1
2
3
4
5
6
7
8
9
10
11
request({
url: imgUrl,// 请求链接
encoding:null
}, (err, response, body) => {
fs.writeFileSync(
imgPath, // 保存本地地址
body, // 保存内容
{
encoding:'binary'
}
)

github代码