// ==UserScript==
// @namespace https://tampermonkey.myso.kr/
// @name 네이버 블로그 글 제목 키워드 추출 분석
// @description 네이버 블로그의 글 제목을 기반으로 모든 키워드를 추출하고 분석결과를 제공합니다.
// @copyright 2021, myso (https://tampermonkey.myso.kr)
// @license Apache-2.0
// @version 1.0.3
// @author Won Choi
// @match *://blog.naver.com/PostView*
// @match *://blog.naver.com/PostList*
// @connect naver.com
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/vendor/gm-app.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/vendor/gm-add-style.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/vendor/gm-add-script.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/vendor/gm-xmlhttp-request-async.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/donation.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/lib/naver-blog.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/lib/naver-search-nx.min.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/lib/naver-search-rx.js
// @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey/assets/lib/smart-editor-one.js
// @require https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuidv4.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.7.2/bluebird.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js
// @downloadURL none
// ==/UserScript==
// ==OpenUserJS==
// @author myso
// ==/OpenUserJS==
GM_App(async function main() {
GM_donation('#viewTypeSelector, #postListBody, #wrap_blog_rabbit, #writeTopArea, #editor_frame', 0);
GM_addStyle(`a._readKeywords .ico_spd { display: block; position: absolute; right: 13px; top: 13px; width: 20px; height: 20px; text-align: center; line-height: 20px; font-size:11px; font-weight: bold; }`);
GM_addStyle(`body._readKeywordsStatus::after { content: attr(data-keywords-status); position: fixed; z-index: 1000000; margin: auto; left:0;top:0;bottom:0;right:0; background:rgba(0,0,0,0.7); color:#fff; display: flex; justify-content: center; align-items: center; word-wrap: break-word; word-break: break-word; overflow-wrap: break-word; white-space: pre-wrap; }`);
async function popup(data) {
const tmpl = Handlebars.compile(`
키워드 추출 분석 결과
키워드 추출 분석 결과
{{keywords.length}}개의 키워드
{{#each keywords}}
-
{{@index}}. {{query}}
{{item.rank}}위
{{#each rel}}
-
{{query}}
검색하기
{{#if r_category}}
-
생산선호주제
{{r_category}}
{{/if}}
{{#if theme.main}}
-
메인소비주제
{{theme.main.name}}
{{/if}}
{{#each theme.sub}}
-
서브소비주제
{{name}}
{{/each}}
{{/each}}
{{/each}}
`);
const html = tmpl(data);
const blob = new Blob([html], {type : 'text/html'});
return window.open(URL.createObjectURL(blob), '_readKeywords', 'width=600, height=960');
}
async function starter(event) {
if(handler.running) return alert('이미 키워드 추출 분석이 진행 중입니다.');
document.body.classList.toggle('_readKeywordsStatus', handler.running = true);
try {
const uri = new URL(location.href), params = Object.fromEntries(uri.searchParams.entries()), blogId = params.blogId;
const se = SE_parse(wrapper), se_title = se.sections.find(o=>o.type == 'title');
if(blogId && se_title) {
const title = se_title.text.join(' ');
document.body.dataset.keywordsStatus = `제목을 분석 중입니다... 잠시 기다려 주세요...\n\n${title}`;
const words = await NX_termsParagraph(title), uniqs = _.uniq(words);
document.body.dataset.keywordsStatus = `제목을 분석 중입니다...\n${uniqs.length}개의 키워드가 발견되었습니다.`;
const cases = uniqs.reduce((function loops(index, cases, word, offset, uniqs){
if(index >= 2) return cases;
const sorts = Array.from(uniqs).sort((curr)=>curr===word?-1:1).slice(1);
const trans = Array.from(sorts).map((next)=>`${word} ${next}`);
const remap = Array.from(sorts).reduce(loops.bind(null, index+1), []).map((next)=>`${word} ${next}`);
cases.push(word, ...trans, ...remap);
return cases.sort().filter((v, i, a)=>a.indexOf(v) == i);
}).bind(null, 0), []);
document.body.dataset.keywordsStatus = `제목을 분석 중입니다... 잠시 기다려 주세요...\n\n${cases.length}개의 조합 키워드가 발견되었습니다.`;
const cases_terms = await NR_termsAll(...cases); console.log(cases_terms);
const cases_items = cases_terms; //.filter(o=>o.r_category || o.theme);
const cases_query = cases_items.map(o=>o.query);
document.body.dataset.keywordsStatus = `순위를 분석 중입니다... 잠시 기다려 주세요...\n\n${cases_query.length}개의 키워드 순위를 수집중입니다.`;
const items = await NX_itemsAll(...cases_query);
document.body.dataset.keywordsStatus = `순위를 분석 중입니다... 잠시 기다려 주세요...\n\n${items.length}개의 키워드 순위 수집이 완료되었습니다.`;
const trans = items.map((item)=>(item = item || {}, item.item=item.items && item.items.find(o=>o.blogId==blogId), item));
const finds = trans.filter(o=>o.item).map(o=>(o.rel=o.query.split(' ').map(k=>cases_terms.find(o=>o.query==k)).filter(v=>!!v), o)).sort((a,b)=>a.item.rank-b.item.rank);
console.log(finds);
popup({ keywords: finds });
}
}catch(e){
console.error(e);
alert('키워드 추출 과정에서 오류가 발생하였습니다.');
}
document.body.classList.toggle('_readKeywordsStatus', handler.running = false);
}
async function handler(event) {
if(handler.running && event && event.type == 'keydown' && event.keyCode == 27) stopper(event);
const wrappers = Array.from(document.querySelectorAll('[data-post-editor-version]'));
wrappers.map((wrapper) => {
const menu = wrapper.querySelector('.lyr_overflow_menu'); if(!menu) return;
const menu_append = (type, rate = 1) => {
const item = menu.querySelector(`a._readKeywords.${type}`) || document.createElement('a'); if(item.className) return;
item.classList.add('_readKeywords', type); item.href = '#'; menu.append(item); item.innerHTML = `키워드 추출 분석`;
item.onclick = async function(event) {
event.preventDefault();
await starter(event);
}
}
menu_append('normal', 1.0);
});
}
window.addEventListener('keyup', handler, false);
window.addEventListener('keydown', handler, false);
window.addEventListener('keypress', handler, false);
window.addEventListener('click', handler, false);
handler();
});