-
Notifications
You must be signed in to change notification settings - Fork 1
/
lib.js
556 lines (515 loc) · 18.4 KB
/
lib.js
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
const https = require("https");
const parser = require("node-html-parser");
const navigator = require("navigator");
const userAgent = navigator.userAgent;
const defaulturl = "https://skb.skku.edu/haksaeng/status.do";
// async 구현은 <https://usefulangle.com/post/170/nodejs-synchronous-http-request>를 참고 하였습니다.
// 마지막 페이지의 url을 반환하는 함수.
function getPromise_LastUrl() {
return new Promise((resolve, reject) => {
https.get(
"https://skb.skku.edu/haksaeng/status.do?mode=list&&articleLimit=10&article.offse",
{
headers: {
"User-Agent": userAgent,
},
},
(res) => {
let data = [];
res.on("data", (fragments) => {
data.push(fragments);
});
res.on("end", () => {
let lastHtml = parser
.parse(data)
.querySelectorAll(".page-last");
ret = defaulturl + lastHtml[0]._attrs.href;
//console.log(ret);
resolve(ret);
});
res.on("error", (error) => {
reject(error);
});
}
);
});
}
// 마지막 페이지의 url을 반환하는 async 함수.
async function makeSynchronousRequest_LastUrl(request) {
try {
let http_promise = getPromise_LastUrl();
let last_url = await http_promise;
// holds response from server that is passed when Promise is resolved
return last_url;
} catch (error) {
// Promise rejected
console.log(error);
}
}
// target person의 정보가 담긴 웹페이지의 url을 반환하는 함수
function getPromise_FindPersonUrl(url, targetNum) {
return new Promise((resolve, reject) => {
https.get(
url,
{
headers: {
"User-Agent": userAgent,
},
},
(res) => {
let data = [];
res.on("data", (fragments) => {
data.push(fragments);
});
res.on("end", () => {
let root = parser.parse(data);
let ret = "";
// 각 확진자의 웹페이지로 들어가는 url은 "dt.board-list-content-title"에 존재.
root.querySelectorAll(
"dt.board-list-content-title"
).forEach((d) => {
let splitedStr = d.innerText
.trim()
.replace(/(\r\n\t|\n|\r\t)/gm, "")
.split("~");
// 한 페이지에 1명의 정보만 담긴 케이스
if (splitedStr.length == 1) {
if (d.innerText.indexOf(String(targetNum)) != -1) {
let splitedUrl = d.childNodes[1].rawAttrs
.split('"')[1]
.replace(/amp/g, "article")
.replace(/;article/g, "");
resolve(defaulturl + splitedUrl);
}
} else {
// 한 페이지에 2명 이상의 정보가 담긴 케이스
let num1 = parseInt(
splitedStr[0].substring(
splitedStr[0].indexOf(" ") + 1,
splitedStr[0].length
)
);
let num2 = parseInt(
splitedStr[1].substring(
0,
splitedStr[1].indexOf("번")
)
);
//console.log(num1 + " " + num2);
for (let num = num1; num <= num2; num++) {
if (num == targetNum) {
let splitedUrl = d.childNodes[1].rawAttrs
.split('"')[1]
.replace(/amp/g, "article")
.replace(/;article/g, "");
resolve(defaulturl + splitedUrl);
}
}
}
});
// 만약 해당 확진자가 존재하지 않을 경우.
resolve(-1);
});
res.on("error", (error) => {
reject(error);
});
}
);
});
}
// target person의 url을 반환하는 async 함수
async function makeSynchronousRequest_FindPersonUrl(url, num, request) {
try {
let http_promise = getPromise_FindPersonUrl(url, num);
let personUrl = await http_promise;
// holds response from server that is passed when Promise is resolved
return personUrl;
} catch (error) {
// Promise rejected
console.log(error);
}
}
// 해당 확진자의 웹페이지에 존재하는 확진환자 정보의 수와 #번째 인지 추출하는 함수.
function getPromise_FindTargetSequence(url, targetNum) {
return new Promise((resolve, reject) => {
https.get(
url,
{
headers: {
"User-Agent": userAgent,
},
},
(res) => {
var targetSequence = {
totalNum_In_OnePage: 0,
Num_In_Page: 0,
};
let data = [];
res.on("data", (fragments) => {
data.push(fragments);
});
res.on("end", () => {
let root = parser.parse(data);
let ret = "";
root.querySelectorAll(
"dt.board-list-content-title"
).forEach((d) => {
let splitedStr = d.innerText
.trim()
.replace(/(\r\n\t|\n|\r\t)/gm, "")
.split("~");
if (splitedStr.length === 1) {
if (d.innerText.indexOf(String(targetNum)) !== -1) {
targetSequence.Num_In_Page = 1;
targetSequence.totalNum_In_OnePage = 1;
resolve(targetSequence);
}
} else {
let num1 = parseInt(
splitedStr[0].substring(
splitedStr[0].indexOf(" ") + 1,
splitedStr[0].length
)
);
let num2 = parseInt(
splitedStr[1].substring(
0,
splitedStr[1].indexOf("번")
)
);
//console.log(num1 + " " + num2);
for (let num = num1; num <= num2; num++) {
if (num === targetNum) {
/* target 사람이 포함된 Post에 게시된 총 확진자의 수 */
targetSequence.totalNum_In_OnePage =
num2 - num1 + 1;
targetSequence.Num_In_Page = num - num1 + 1;
//console.log(targetSequence)
resolve(targetSequence);
}
}
}
});
resolve(-1);
});
res.on("error", (error) => {
reject(error);
});
}
);
});
}
// 해당 확진자의 웹페이지에 존재하는 확진환자 정보의 수와 #번째 인지 추출하는 async 함수.
async function makeSynchronousRequest_FindTargetSequence(url, num) {
try {
let http_promise = getPromise_FindTargetSequence(url, num);
let targetSequence = await http_promise;
// holds response from server that is passed when Promise is resolved
return targetSequence;
} catch (error) {
// Promise rejected
console.log(error);
}
}
// 특정사람의 path를 반환하는 함수
function getPromise_FindPath(url) {
return new Promise((resolve, reject) => {
https.get(
url,
{
headers: {
"User-Agent": userAgent,
},
},
(res) => {
let data = [];
res.on("data", (fragments) => {
data.push(fragments);
});
res.on("end", () => {
let ret = [];
// 확진자의 동선에 대한 정보는 ".fr-view"에 존재한다.
let innerHtml = parser
.parse(data)
.querySelector(".fr-view");
parser
.parse(innerHtml)
.querySelectorAll("p")
.forEach((d) => {
//console.log(d.innerText);
if (d.innerText !== "") ret.push(d.innerText);
});
resolve(ret);
});
res.on("error", (error) => {
reject(error);
});
}
);
});
}
// 특정사람의 path를 반환하는 async 함수
async function makeSynchronousRequest_FindPath(url, request) {
try {
let http_promise = getPromise_FindPath(url);
let path = await http_promise;
// holds response from server that is passed when Promise is resolved
return path;
} catch (error) {
// Promise rejected
console.log(error);
}
}
// 위에 작성된 함수들을 통합적으로 사용해 사람의 path를 구하는 함수
async function getPromise_FindPerson(num) {
return new Promise(async (resolve, reject) => {
// target person의 정보를 object로 저장.
var person = {
url: "",
confirmed_Num: 0,
totalNum_In_OnePage: 0,
Num_In_Page: 0,
campus: "",
datas: [],
splitedData: "",
path: [],
};
person.confirmed_Num = num;
// wait to http request to finish
const basicUrl =
"https://skb.skku.edu/haksaeng/status.do?mode=list&&articleLimit=10&article.offset";
let i = 0;
let curUrl = basicUrl + "=" + i;
let last_url = await makeSynchronousRequest_LastUrl();
// console.log(curUrl);
let targeturl = await makeSynchronousRequest_FindPersonUrl(curUrl, num);
if (targeturl !== -1) {
let path = await makeSynchronousRequest_FindPath(targeturl);
person.url = targeturl;
person.datas = path;
let targetSequence =
await makeSynchronousRequest_FindTargetSequence(curUrl, num);
//console.log(targetSequence);
person.Num_In_Page = targetSequence.Num_In_Page;
person.totalNum_In_OnePage = targetSequence.totalNum_In_OnePage;
// console.log(path);
var tmp = personInfo(
person.confirmed_Num,
person.totalNum_In_OnePage,
person.Num_In_Page,
person.datas
);
person.campus = tmp.campus;
person.splitedData = tmp.splitedData;
person.path = tmp.path;
resolve(person);
}
while (curUrl !== last_url) {
i += 10;
curUrl = basicUrl + "=" + i;
targeturl = await makeSynchronousRequest_FindPersonUrl(curUrl, num);
if (targeturl !== -1) {
let path = await makeSynchronousRequest_FindPath(targeturl);
person.url = targeturl;
person.datas = path;
let targetSequence =
await makeSynchronousRequest_FindTargetSequence(
curUrl,
num
);
//console.log(targetSequence);
person.Num_In_Page = targetSequence.Num_In_Page;
person.totalNum_In_OnePage = targetSequence.totalNum_In_OnePage;
//console.log(path);
var tmp = personInfo(
person.confirmed_Num,
person.totalNum_In_OnePage,
person.Num_In_Page,
person.datas
);
person.campus = tmp.campus;
person.splitedData = tmp.splitedData;
person.path = tmp.path;
resolve(person);
}
}
// 만약 target person이 없을 경우.
if (targeturl === -1) reject();
});
}
async function makeSynchronousRequest_FindPerson(num, request) {
try {
let http_promise = getPromise_FindPerson(num);
let path = await http_promise;
// holds response from server that is passed when Promise is resolved
return path;
} catch (error) {
// Promise rejected
console.log(error);
}
}
let placeNames = [
[
"600주년기념관",
"법학관",
"교수회관",
"호암관",
"중앙학술정보관",
"학생회관",
"국제관",
"양현관",
"퇴계인문관",
"다산경제관",
"경영관",
"수선관",
"수선관(별관)",
"킹고(K)하우스",
"인터네셔널(I)하우스",
"금잔디광장",
"대운동장",
],
[
"학생회관",
"복지회관",
"수성관",
"유틸리티센터",
"환경플랜트",
"건축관리실",
"공학실습동A",
"제1공학관 21",
"제1공학관 22",
"제1공학관 23",
"공학실습동B(24)",
"제2공학관 25",
"제2공학관 26",
"제2공학관 27",
"공학실습동C(28)",
"건축환경실험실",
"제1과학관 31",
"제2과학관 32",
"화학관",
"반도체관",
"삼성학술정보관",
"운용재",
"기초학문관 51",
"약학관",
"생명공학관 61",
"생명공학관 62",
"생명공학실습동",
"대강당",
"의학관",
"체육관",
"제1종합연구동",
"제2종합연구동",
"제약기술관",
"산학협력센터",
"N센터",
"학군단",
"기숙사인관",
"기숙사의관",
"기숙사예관",
"기숙사지관",
"게스트하우스",
"기숙사신관",
],
];
// 확진자의 정보를 추출하는 함수
function personInfo(num, total, order, crudeInfo) {
var person = {
campus: "",
splitedData: "",
path: [],
}; // 0-확진자 번호, 1-인사0/자과1, 2-날짜, 3-동선, 4-다녀간 장소 배열
let info = [num, 0, 0, 0, []];
let n = crudeInfo.length;
for (let i = 0; i < n; i++) {
if (crudeInfo[i].indexOf("인사캠") !== -1) {
info[1] = "인사캠";
break;
} else if (crudeInfo[i].indexOf("자과캠") !== -1) {
info[1] = "자과캠";
break;
}
// 혼합인 경우
}
info[2] = crudeInfo[0].substring(0, 6); // 게시물마다 양식이 다름... .match로 고쳐보기
// 동선 없는 경우
for (let i = 0; i < n; i++) {
if (crudeInfo[i].indexOf("교내 동선은 없습니다.") !== -1) {
info[3] = "교내 동선은 없습니다.";
//console.log(info);
person.campus = info[1];
person.splitedData = info[3];
person.path = info[4];
return person;
}
}
// 동선 있는 경우
if (total === 1) {
let data = "";
for (let i = 0; i < n; i++) {
if (
crudeInfo[i].indexOf("교내 동선") !== -1 ||
crudeInfo[i].indexOf("확진학생의 적극적인 협조") !== -1
) {
i++;
while (crudeInfo[i].indexOf("의심증상 상담") === -1) {
data += crudeInfo[i];
i++;
}
break;
}
}
info[3] = data;
info[4] = getPlaces(data);
} else {
let order_alphabet = ["A", "B", "C", "D"];
let alphabet = order_alphabet[order - 1];
let i;
for (i = 0; i < n; i++) {
if (crudeInfo[i].indexOf(alphabet) !== -1) {
break;
}
}
let data = "";
i++;
for (i; i < n; i++) {
if (
crudeInfo[i].indexOf("※") !== -1 ||
crudeInfo[i].indexOf(order_alphabet[order]) !== -1
)
break;
data += crudeInfo[i];
}
info[3] = data;
info[4] = getPlaces(data);
}
//console.log(info);
person.campus = info[1];
person.splitedData = info[3];
person.path = info[4];
return person;
}
function getPlaces(dataString) {
let visited = [];
let n = placeNames[0].length;
let m = placeNames[1].length;
for (let i = 0; i < n; i++) {
if (
dataString.indexOf(placeNames[0][i]) !== -1 &&
!visited.includes(placeNames[0][i])
) {
visited.push(placeNames[0][i]);
}
}
for (let i = 0; i < m; i++) {
if (
dataString.indexOf(placeNames[1][i]) !== -1 &&
!visited.includes(placeNames[1][i])
) {
visited.push(placeNames[1][i]);
}
}
return visited;
}
module.exports = { makeSynchronousRequest_FindPerson };