String15

Modified to parallel processing to speed up

const Discord = require("discord.js");
exports.run = async (client, msg, args, prefix) => {
if (args[0]) { // 명령어 뒤에 입력값이 있을 경우 (ex. !<명령어> <채팅>)
// 각 사이트별 제품 검색
const puppeteer = require('puppeteer'); //include Puppeteer Library
puppeteer.launch({headless:true}).then(async browser => {
// 브라우저 열기
// 검색어 한 문장으로 합치기
const search = args.join(' ');
// console.log(search);
// 검색 대기 시간이 길어질 수 있으므로 사용자에게 진행상황을 알려줄 필요가 있다
msg.reply("검색중 ...");
//include Puppeteer Library
const puppeteer = require('puppeteer');
const withBrowser = async (fn) => {
const browser = await puppeteer.launch({headless:true});
try {
return await fn(browser);
} finally {
await browser.close();
}
}
const withPage = (browser) => async (fn) => {
const page = await browser.newPage();
console.log('Open Browser');
// 1. 베스트펜
try {
console.log("bestpen crawling");
await page .goto('http://www.bestpen.kr');
return await fn(page);
} finally {
await page.close();
}
}
// 검색창으로 이동 & args[0] 검색
await page.waitForSelector('#header > div.headerBtm > div > p.searchOpen > i');
await page .click('#header > div.headerBtm > div > p.searchOpen > i');
await page .type('#keyword', args[0]);
await page .keyboard.press( "Enter" );
// await page.screenshot({ path : "screenshot.png" });
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
const urls = ['http://www.bestpen.kr/shop/shopbrand.html?&search=', 'http://www.pencafe.co.kr/shop/shopbrand.html?&search=', 'https://blueblack.co.kr/product/search.html?&keyword=', 'http://japan9.co.kr/shop/shopbrand.html?&search='];
const results = await withBrowser(async (browser) => {
return Promise.all(urls.map(async (url) => {
return withPage(browser)(async (page) => {
// 각 사이트별 제품 검색
var result = '';
site : switch(urls.indexOf(url)) {
case 0:
// 베스트펜
try {
var bestpen_url = url + search + "&sort=price"
await page.goto(bestpen_url);
// console.log("bestpen crawling");
// 검색 결과 가져오기 (최대 4개)
var bestpen = '';
for (var i = 1; i <= 4; i++) {
try {
var title = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a');
var bestpen_title = await page.evaluate( title => title.textContent, title );
// console.log("베스트펜 검색 결과 제품명 : ", bestpen_title);
var link = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dt > a');
var bestpen_link = await page.evaluate( link => link.href, link );
// console.log("베스트펜 검색 링크 : ", bestpen_link);
// 페이지 로딩 대기
await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child(4) > dd > ul > li.prd-name > a');
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
bestpen_link = bestpen_link.slice(0, bestpen_link.indexOf('&search'));
// 검색 결과 가져오기 (최대 4개)
searchLoop : for (var i = 1; i <= 4; i++) {
try {
var title = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a', element => {
return element.textContent;
});
// console.log("베스트펜 검색 결과 제품명 : ", title);
try {
var price = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-price > p:nth-child(2) > span.price');
var bestpen_data = await page.evaluate( price => price.textContent, price );
// console.log("베스트펜 검색 결과 가격 : ", bestpen_data);
} catch {
// 품절이라서 금액정보가 없을 경우 "SOLD OUT"으로 표시
bestpen_data = "SOLD OUT";
}
bestpen += `[${bestpen_title}](${bestpen_link}) - ${bestpen_data}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
bestpen += "검색결과 없음" + '\n';
break;
} else {
// 제품 개수가 4개 이하인 경우
break;
}
}
}
var link = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dt > a', element => {
return element.href;
});
// console.log("베스트펜 검색 링크 : ", link);
// 2. 펜카페
try {
console.log("pencafe crawling");
await page .goto('http://www.pencafe.co.kr');
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
link = link.slice(0, link.indexOf('&search'));
try {
var price = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-price > p:nth-child(2) > span.price', element => {
return element.textContent;
});
// console.log("베스트펜 검색 결과 가격 : ", price);
} catch {
// 품절이라서 금액정보가 없을 경우 "SOLD OUT"으로 표시
price = "SOLD OUT";
}
// args[0] 검색
await page.waitForSelector('#header > div.hd_mib > div.hd_sch.f_left > form > fieldset > input');
await page .type('#header > div.hd_mib > div.hd_sch.f_left > form > fieldset > input', args[0]);
await page .keyboard.press( "Enter" );
// await page.screenshot({ path : "screenshot.png" });
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
result += `[${title}](${link}) - ${price}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
result += "검색결과 없음" + '\n';
break searchLoop;
} else {
// 제품 개수가 4개 이하인 경우
break searchLoop;
}
}
}
break site;
case 1:
// 펜카페
try {
var pencafe_url = url + search + "&sort=price"
await page.goto(pencafe_url);
// console.log("pencafe crawling");
// 검색 결과 가져오기 (최대 4개)
var pencafe = '';
for (var i = 1; i <= 4; i++) {
try {
var title = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a');
var pencafe_title = await page.evaluate( title => title.textContent, title );
// console.log("펜카페 검색 결과 제품명 : ", pencafe_title);
// 페이지 로딩 대기
await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child(4) > dd > ul > li.prd-name > a');
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 펜카페 구조상 제품명 앞에 할인률(ex. 17%)이 붙는 경우가 많음 -> 제거
if (pencafe_title.indexOf('%') != -1) { pencafe_title = pencafe_title.slice(pencafe_title.indexOf('%')+1); }
// 펜카페 구조상 제품명 뒤에 사족(ex. (색상선택/금장~~~))이 붙는 경우가 많음 -> 제거
if (pencafe_title.lastIndexOf(')') == pencafe_title.length-1) { pencafe_title = pencafe_title.slice(0, pencafe_title.lastIndexOf('(')); }
var link = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a');
var pencafe_link = await page.evaluate( link => link.href, link );
// console.log("펜카페 검색 링크 : ", pencafe_link);
// 검색 결과 가져오기 (최대 4개)
searchLoop : for (var i = 1; i <= 4; i++) {
try {
var title = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a', element => {
return element.textContent;
});
// console.log("펜카페 검색 결과 제품명 : ", title);
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
pencafe_link = pencafe_link.slice(0, pencafe_link.indexOf('&search'));
// 펜카페 구조상 제품명 앞에 할인률(ex. 17%)이 붙는 경우가 많음 -> 제거
if (title.indexOf('%') != -1) { title = title.slice(title.indexOf('%')+1); }
// 펜카페 구조상 제품명 뒤에 사족(ex. (색상선택/금장~~~))이 붙는 경우가 많음 -> 제거
if (title.lastIndexOf(')') == title.length-1) { title = title.slice(0, title.lastIndexOf('(')); }
try {
var price = await page.waitForSelector('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-price > span');
var pencafe_data = await page.evaluate( price => price.textContent, price );
// console.log("펜카페 검색 결과 가격 : ", pencafe_data);
} catch {
// 품절이라서 금액정보가 없을 경우 "SOLD OUT"으로 표시
pencafe_data = "SOLD OUT";
}
var link = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-name > a', element => {
return element.href;
});
// console.log("펜카페 검색 링크 : ", link);
pencafe += `[${pencafe_title}](${pencafe_link}) - ${pencafe_data}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
pencafe += "검색결과 없음" + '\n';
break;
} else {
// 제품 개수가 4개 이하인 경우
break;
}
}
}
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
link = link.slice(0, link.indexOf('&search'));
try {
var price = await page.$eval('#searchWrap > div > div.item-wrap > div:nth-child(2) > dl:nth-child('+i+') > dd > ul > li.prd-price > span', element => {
return element.textContent;
});
// console.log("펜카페 검색 결과 가격 : ", price);
} catch {
// 품절이라서 금액정보가 없을 경우 "SOLD OUT"으로 표시
price = "SOLD OUT";
}
// 3. 블루블랙
try {
console.log("blueblack crawling");
await page .goto('https://blueblack.co.kr');
result += `[${title}](${link}) - ${price}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
result += "검색결과 없음" + '\n';
break searchLoop;
} else {
// 제품 개수가 4개 이하인 경우
break searchLoop;
}
}
}
break site;
case 2:
// 블루블랙
try {
var blueblack_url = url + search + "&order_by=priceasc"
await page.goto(blueblack_url);
// console.log("blublack crawling");
// args[0] 검색
await page.waitForSelector('#keyword');
await page .type('#keyword', args[0]);
await page .keyboard.press( "Enter" );
// await page.screenshot({ path : "screenshot.png" });
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 페이지 로딩 대기
await page.waitForSelector('#contents > div:nth-child(4) > ul > li:nth-child(4) > div.description > p.name > a > span:nth-child(2)');
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 검색 결과 가져오기 (최대 4개)
var blueblack = '';
for (var i = 1; i <= 4; i++) {
try {
var title = await page.waitForSelector('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > p.name > a > span:nth-child(2)');
var blueblack_title = await page.evaluate( title => title.textContent, title );
// console.log("블루블랙 검색 결과 제품명 : ", blueblack_title);
var link = await page.waitForSelector('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > p.name > a');
var blueblack_link = await page.evaluate( link => link.href, link );
// console.log("블루블랙 검색 링크 : ", blueblack_link);
// 검색 결과 가져오기 (최대 4개)
searchLoop : for (var i = 1; i <= 4; i++) {
try {
var title = await page.$eval('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > p.name > a > span:nth-child(2)', element => {
return element.textContent;
});
// console.log("블루블랙 검색 결과 제품명 : ", title);
var link = await page.$eval('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > p.name > a', element => {
return element.href;
});
// console.log("블루블랙 검색 링크 : ", link);
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
pencafe_link = pencafe_link.slice(0, pencafe_link.indexOf('&cate_no'));
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
link = link.slice(0, link.indexOf('&cate_no'));
try {
// 품절 아이콘이 있는지 확인
await page.$eval('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > div.status > div > img', element => {
return element.getAttribute("src");
});
try {
// 품절 아이콘이 있는지 확인
await page.$eval('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > div.status > div > img', element => {
return element.getAttribute("src");
});
blueblack_data = 'SOLD OUT';
} catch {
// 품절 아이콘이 없을 경우
var price = await page.waitForSelector('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > ul > li:nth-child(3) > span:nth-child(2)');
var blueblack_data = await page.evaluate( price => price.textContent, price );
// console.log("블루블랙 검색 결과 가격 : ", blueblack_data);
}
var price = 'SOLD OUT';
} catch {
try {
// 품절 아이콘이 없을 경우
var price = await page.$eval('#contents > div:nth-child(4) > ul > li:nth-child('+i+') > div.description > ul > li:nth-child(3) > span:nth-child(2)', element => {
return element.textContent;
});
} catch {
var price = '0원';
}
}
// console.log("블루블랙 검색 결과 가격 : ", price);
blueblack += `[${blueblack_title}](${blueblack_link}) - ${blueblack_data}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
blueblack += "검색결과 없음" + '\n';
break;
} else {
// 제품 개수가 4개 이하인 경우
break;
}
}
}
result += `[${title}](${link}) - ${price}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
result += "검색결과 없음" + '\n';
break searchLoop;
} else {
// 제품 개수가 4개 이하인 경우
break searchLoop;
}
}
}
break site;
case 3:
// 재팬나인
try {
await page.goto(url);
// console.log("japannine crawling");
// 4. 재팬나인
try {
console.log("japannine crawling");
await page .goto('http://www.japan9.co.kr');
var japannine_url = url + search + "&sort=price"
await page.goto(japannine_url);
// console.log("japannine crawling");
// args[0] 검색
await page.waitForSelector('body > center > table:nth-child(7) > tbody > tr > td > table:nth-child(1) > tbody > tr > td:nth-child(2) > table > tbody > tr > td:nth-child(2) > input');
await page .type('body > center > table:nth-child(7) > tbody > tr > td > table:nth-child(1) > tbody > tr > td:nth-child(2) > table > tbody > tr > td:nth-child(2) > input', args[0]);
await page .keyboard.press( "Enter" );
// await page.screenshot({ path : "screenshot.png" });
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 페이지 로딩 대기
await page.waitForSelector('#mk_search_production > tbody > tr:nth-child(17) > td:nth-child(3) > a');
} catch { ; } // 사이트 링크에 이상이 생겼거나 검색에 문제가 생겼을 경우 프로그램이 종료되는 것을 방지
// 검색 결과 가져오기 (최대 4개)
var japannine = '';
for (var i = 1; i <= 4; i++) {
try {
var title = await page.waitForSelector('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(3) > a');
var japannine_title = await page.evaluate( title => title.textContent, title );
// console.log("재팬나인 검색 결과 제품명 : ", japannine_title);
// 검색 결과 가져오기 (최대 4개)
searchLoop : for (var i = 1; i <= 4; i++) {
try {
var title = await page.$eval('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(3) > a', element => {
return element.textContent;
});
// console.log("재팬나인 검색 결과 제품명 : ", title);
// 재팬나인 구조상 제품명 앞에 항상 '/n'이 붙는다 -> 제거
japannine_title = japannine_title.slice(1);
// 재팬나인 구조상 제품명 앞과 뒤에 [쿠폰적용 || 주문예약상품]이 붙는 경우가 많음 -> 제거
if (japannine_title.indexOf('[') == 0) { japannine_title = japannine_title.slice(japannine_title.indexOf(']')+1); }
if (japannine_title.lastIndexOf(']') == japannine_title.length-1) { japannine_title = japannine_title.slice(0, japannine_title.lastIndexOf('[')); }
if (japannine_title.indexOf('{') == 0) { japannine_title = japannine_title.slice(japannine_title.indexOf('}')+1); }
if (japannine_title.lastIndexOf('}') == japannine_title.length-1) { japannine_title = japannine_title.slice(0, japannine_title.lastIndexOf('{')); }
// 재팬나인 구조상 제품명 앞에 항상 '/n'이 붙는다 -> 제거
title = title.slice(1);
// 재팬나인 구조상 제품명 앞과 뒤에 [쿠폰적용 || 주문예약상품]이 붙는 경우가 많음 -> 제거
if (title.indexOf('[') == 0) { title = title.slice(title.indexOf(']')+1); }
if (title.lastIndexOf(']') == title.length-1) { title = title.slice(0, title.lastIndexOf('[')); }
if (title.indexOf('{') == 0) { title = title.slice(title.indexOf('}')+1); }
if (title.lastIndexOf('}') == title.length-1) { title = title.slice(0, title.lastIndexOf('{')); }
var link = await page.waitForSelector('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(3) > a');
var japannine_link = await page.evaluate( link => link.href, link );
// console.log("재팬나인 검색 링크 : ", japannine_link);
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
japannine_link = japannine_link.slice(0, japannine_link.indexOf('&search'));
var link = await page.$eval('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(3) > a', element => {
return element.href;
});
// console.log("재팬나인 검색 링크 : ", link);
// 링크 구조상 &search 뒷부분은 제품 링크를 띄우는데 영향을 미치지 않음
// -> 글자수 제한(1024)도 있으므로 제거
link = link.slice(0, link.indexOf('&search'));
try {
// 품절 아이콘이 있는지 확인
var img_src = await page.$eval('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(6) > img', element => {
return element.getAttribute("src");
});
try {
// 품절 아이콘이 있는지 확인
var img_src = await page.$eval('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td:nth-child(6) > img', element => {
return element.getAttribute("src");
});
if (img_src.indexOf('no_amount0') != -1) { japannine_data = 'SOLD OUT'; }
} catch {
// 품절 아이콘이 없을 경우
var price = await page.waitForSelector('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td.brandprice > span');
var japannine_data = await page.evaluate( price => price.textContent, price );
// console.log("재팬나인 검색 결과 가격 : ", japannine_data);
if (img_src.indexOf('no_amount0') != -1) { var price = 'SOLD OUT'; }
} catch {
// 품절 아이콘이 없을 경우
var price = await page.$eval('#mk_search_production > tbody > tr:nth-child('+(5+((i-1)*4))+') > td.brandprice > span', element => {
return element.textContent;
});
// 재팬나인 구조상 금액 뒤에 (옵션에 따라 변동)이 붙는 경우가 있다 -> 제거
if (japannine_data.indexOf('(') != -1) { japannine_data = japannine_data.slice(0, japannine_data.indexOf('원')+1); }
// 재팬나인 구조상 금액 뒤에 (옵션에 따라 변동)이 붙는 경우가 있다 -> 제거
if (price.indexOf('(') != -1) { price = price.slice(0, price.indexOf('원')+1); }
}
// console.log("재팬나인 검색 결과 가격 : ", price);
result += `[${title}](${link}) - ${price}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
result += "검색결과 없음" + '\n';
break searchLoop;
} else {
// 제품 개수가 4개 이하인 경우
break searchLoop;
}
}
}
break site;
}
japannine += `[${japannine_title}](${japannine_link}) - ${japannine_data}` + '\n';
} catch {
if (i == 1) {
// 제품 정보가 아예 없을 경우 "검색결과 없음"으로 표시
japannine += "검색결과 없음" + '\n';
break;
} else {
// 제품 개수가 4개 이하인 경우
break;
}
}
}
// 브라우저 닫기
await browser.close();
console.log('Browser Closed');
// 검색 결과 챗봇에 출력
let Commands = new Discord.MessageEmbed()
.setTitle(`${args[0]}에 대한 검색 결과`)
.setColor("E5D49A")
// 베스트펜 검색 결과 (ex. 제품명(링크) - 금액)
.addField('베스트펜', `${bestpen.slice(0, 1023)}`)
// 펜카페 검색 결과
.addField('펜카페', `${pencafe.slice(0, 1023)}`)
// 블루블랙 검색 결과
.addField('블루블랙', `${blueblack.slice(0, 1023)}`)
// 재팬나인 검색 결과
.addField('재팬나인', `${japannine.slice(0, 1023)}`)
msg.reply({ embeds: [Commands] });
return result;
});
}))
});
// 검색 대기 시간이 길어질 수 있으므로 사용자에게 진행상황을 알려줄 필요가 있다
msg.reply("검색중 ...");
// 검색 결과 챗봇에 출력
let Commands = new Discord.MessageEmbed()
.setTitle(`${search}에 대한 검색 결과`)
.setColor("E5D49A")
// 베스트펜 검색 결과 (ex. 제품명(링크) - 금액)
.addField('베스트펜', `${results[0].slice(0, 1023)}`)
// 펜카페 검색 결과
.addField('펜카페', `${results[1].slice(0, 1023)}`)
// 블루블랙 검색 결과
.addField('블루블랙', `${results[2].slice(0, 1023)}`)
// 재팬나인 검색 결과
.addField('재팬나인', `${results[3].slice(0, 1023)}`)
msg.reply({ embeds: [Commands] });
} else {
msg.reply("검색어가 없습니다. 검색어를 추가해서 다시 입력해주세요.");
}
......