Created
May 16, 2019 16:49
-
-
Save hmmhmmhm/3f8768245c50b9d443590e675352f09a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const puppeteer = require('puppeteer'); | |
const logger = require('./logger'); | |
const Pool = require('./pool'); | |
const LRU = require("lru-cache"); | |
var browserErrorCount = 0; | |
var collectInterval = null; | |
var hitInterval = null; | |
async function pageAccelerate(page){ | |
await page.setRequestInterception(true); | |
page.on('request', request => { | |
if (request.resourceType() === 'image') | |
request.abort() | |
else | |
request.continue() | |
}) | |
}; | |
let chucheon = (async () => { | |
// 추천 예정인 게시글 목록 | |
let preChucheonList = {}; | |
// 추천이 진행 중인 게시글 목록 | |
let processingChucheonList = {}; | |
// 이미 추천한 게시글 목록 캐시 | |
let options = { | |
max: 500000, | |
maxAge: 1000 * 60 * 60 } | |
let votedCache = new LRU(options); | |
let ignoredCache = new LRU(options); | |
const browser = await puppeteer.launch({headless: true}) | |
const refreshPage = await browser.newPage() | |
// 로그인 합니다. | |
let initTry = true | |
try{ | |
await refreshPage.goto('https://user.ruliweb.com/member/login', {waitUntil: 'domcontentloaded'}) | |
// Hit ID & PW | |
await refreshPage.type('#user_id', '', {delay: 30}) | |
await refreshPage.type('#user_pw', '', {delay: 30}) | |
// 모든 메시지는 뜨는 즉시 accept 합니다. | |
refreshPage.on('dialog', async dialog => { | |
console.log(dialog.message()) | |
await dialog.accept() | |
}) | |
await refreshPage.click('#login_submit', {delay: 200}) | |
await refreshPage.waitForNavigation() | |
}catch(e){ | |
logger('최초 로딩에 실패하였습니다. 다시 재시작을 시도합니다..') | |
initTry = false | |
} | |
let isInitError = false | |
if(!initTry){ | |
logger(`시스템이 자동으로 재시작 됩니다.`) | |
if(collectInterval !== null) | |
clearInterval(collectInterval) | |
if(hitInterval !== null) | |
clearInterval(hitInterval) | |
await browser.close() | |
isInitError = true | |
console.error('reboot') | |
process.exit(1) | |
} | |
if(isInitError) return | |
logger('스마트 커뮤니티 마케팅 시스템 작동하였습니다.') | |
// 페이지 재사용 풀을 생성합니다. | |
//const pool = new Pool(browser) | |
// 페이지 내 최신 게시글 목록을 수집합니다. | |
async function collectBoardFresh(page){ | |
// collectedData | |
// [0] collectedDocs | |
// [1] failureDocs | |
let collected = await page.evaluate(() => { | |
function isBad(text){ | |
for(var bad of bads){ | |
var badCount = 0 | |
var badChars = bad.split('') | |
if(typeof ignores[bad] != 'undefined'){ | |
for(var ignoreWord of ignores[bad]){ | |
if(text.indexOf(ignoreWord) != -1) | |
return false | |
} | |
} | |
for(var badChar of badChars){ | |
if(text.indexOf(badChar) != -1) | |
badCount++ | |
} | |
if(badCount == badChars.length) | |
return true | |
} | |
return false | |
} | |
//document.querySelector('button[type=submit]').click() | |
var docItems = document.querySelectorAll('.board_list_table')[0].querySelectorAll('.deco') | |
var collectedDocs = [] | |
for(var docItem of docItems){ | |
var docDetail = docItem.parentElement.parentElement | |
if(docDetail.className.indexOf('table_body notice') == -1){ | |
if(!isBad(docItem.innerText)){ | |
collectedDocs.push([ | |
docItem.href, | |
docItem.innerText | |
]) | |
} | |
} | |
} | |
var failureDocs = [] | |
for(var docItem of docItems){ | |
var docDetail = docItem.parentElement.parentElement | |
var docParent = docDetail.parentElement | |
if(docDetail.className.indexOf('table_body notice') == -1){ | |
if(Number(docParent.getElementsByClassName('recomd')[0].innerText) == 0){ | |
if(!isBad(docItem.innerText)){ | |
failureDocs.push([ | |
docItem.href, | |
docItem.innerText | |
]) | |
} | |
} | |
} | |
} | |
return { | |
docs: collectedDocs, | |
failure: failureDocs | |
} | |
}) | |
// 추천내용을 수집합니다. | |
let addedNew = 0 | |
// 추천에 실패한 목록을 추천 대기 목록에 추가시킵니다. | |
// logger(`탐색 중 추가로 발견한 미추천 게시글 수: ${Object.keys(collected.failure).length}`) | |
for(let collectedDocIndex in collected.failure){ | |
let collectedDoc = collected.failure[collectedDocIndex] | |
// 추천하지 않기로 결정된 게시글이면 넘어갑니다. | |
if(ignoredCache.get(collectedDoc[0]) !== undefined) | |
continue | |
// 아직 추천을 진행하지 않은 게시글이라면 넘어갑니다. | |
if(votedCache.get(collectedDoc[0]) === undefined) | |
continue | |
// 현재 추천이 진행중인 게시글이라면 넘어갑니다. | |
if(typeof processingChucheonList[collectedDoc[0]] != 'undefined') | |
continue | |
// 추천이 한번이상 진행되었는데도 | |
// 추천이 적용되지 않은 게시글만 포함시킵니다. | |
if((typeof preChucheonList[collectedDoc[0]]) === 'undefined'){ | |
preChucheonList[collectedDoc[0]] = collectedDoc[1] | |
addedNew++ | |
} | |
} | |
// 게시글 목록을 역순으로 추천 대기 목록에 추가합니다. | |
for(let collectedDocIndex in collected.docs){ | |
let collectedDoc = collected.docs[collectedDocIndex] | |
//console.log(collectedDocIndex, collectedDoc[0]) | |
// 추천하지 않기로 결정된 게시글이면 넘어갑니다. | |
if(ignoredCache.get(collectedDoc[0]) !== undefined) | |
continue | |
if(votedCache.get(collectedDoc[0]) !== undefined) | |
continue | |
// 현재 추천이 진행중인 게시글이라면 넘어갑니다. | |
if(typeof processingChucheonList[collectedDoc[0]] != 'undefined') | |
continue | |
if((typeof preChucheonList[collectedDoc[0]]) === 'undefined'){ | |
preChucheonList[collectedDoc[0]] = collectedDoc[1] | |
addedNew++ | |
} | |
} | |
if(addedNew != 0)logger(`${addedNew} 건의 글이 새로 올라왔습니다.`) | |
} | |
async function collect(){ | |
await refreshPage.goto('https://bbs.ruliweb.com/community/board/300143', {waitUntil: 'domcontentloaded'}) | |
// 최신게시글 데이터를 모두 수집합니다. | |
await collectBoardFresh(refreshPage) | |
} | |
// await collect() // 임시용 await | |
collectInterval = setInterval(collect, 10000) | |
async function hit(){ | |
let isStartedReboot = false | |
try{ | |
let keys = Object.keys(preChucheonList) | |
if(keys.length == 0) return | |
// 해당 게시글을 추천예정 목록에서 삭제합니다. | |
let targetUrl = keys.shift() | |
let targetName = preChucheonList[targetUrl] | |
delete preChucheonList[targetUrl] | |
// 이후 추천 진행중 목록에 추가합니다. | |
processingChucheonList[targetUrl] = true | |
const hitPage = await browser.newPage() | |
let work = async (page)=>{ | |
// 추천할 게시글 페이지로 갑니다. | |
await page.goto(targetUrl, { | |
waitUntil: 'domcontentloaded' | |
}) | |
// 최신게시글 데이터를 모두 수집합니다. | |
await collectBoardFresh(page) | |
// 모든 내용을 승인합니다. | |
page.on('dialog', async dialog => { | |
await dialog.accept() | |
}) | |
// 게시글을 추천합니다. | |
let pageResult = await page.evaluate(() => { | |
function isBad(text){ | |
for(var bad of bads){ | |
var badCount = 0 | |
var badChars = bad.split('') | |
if(typeof ignores[bad] != 'undefined'){ | |
for(var ignoreWord of ignores[bad]){ | |
if(text.indexOf(ignoreWord) != -1) | |
return false | |
} | |
} | |
for(var badChar of badChars){ | |
if(text.indexOf(badChar) != -1) | |
badCount++ | |
} | |
if(badCount == badChars.length) | |
return true | |
} | |
return false | |
} | |
if(isBad(document.querySelector('.board_main_view').querySelector('.view_content').innerText)) | |
return false | |
document.querySelector('.board_main_view').querySelector('.like').click() | |
return true | |
}) | |
if(!pageResult) | |
ignoredCache.set(targetUrl, true) | |
} | |
//await pool.run(work) | |
await work(hitPage) | |
hitPage.close() | |
logger(`추천완료. ${Object.keys(preChucheonList).length} 개 남음. 제목: ${targetName}`) | |
// 추천한 목록에 추가합니다. | |
votedCache.set(targetUrl, true) | |
// 추천을 진행 중인 목록에서 삭제합니다. | |
delete processingChucheonList[targetUrl] | |
}catch(e){ | |
browserErrorCount++ | |
if(browserErrorCount < 5){ | |
logger(`작업 진행 도중 오류가 발생했습니다. 자동 재시작까지 남은 오류수 ${browserErrorCount}`) | |
}else{ | |
if(isStartedReboot) return | |
isStartedReboot = true | |
logger(`브라우저 5회 오류 발생으로 인해 시스템을 재시작합니다...`) | |
if(collectInterval !== null) | |
clearInterval(collectInterval) | |
if(hitInterval !== null) | |
clearInterval(hitInterval) | |
await browser.close() | |
console.error('reboot') | |
process.exit(1) | |
} | |
//console.log(e) | |
} | |
} | |
//await hit() | |
hitInterval = setInterval(hit, 2500) | |
/* | |
const dimensions = await page.evaluate(() => { | |
return { | |
width: document.documentElement.clientWidth, | |
height: document.documentElement.clientHeight, | |
deviceScaleFactor: window.devicePixelRatio | |
}; | |
}); | |
*/ | |
// await browser.close() | |
}); | |
chucheon() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment