Greasy Fork

Greasy Fork is available in English.

네이버 블로그 통계 지표 다운로드 플러스

네이버 블로그 통계의 지표 다운로드 기능을 개선하여 줍니다.

当前为 2021-06-29 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @namespace    https://tampermonkey.myso.kr/
// @name         네이버 블로그 통계 지표 다운로드 플러스
// @description  네이버 블로그 통계의 지표 다운로드 기능을 개선하여 줍니다.
// @copyright    2021, myso (https://tampermonkey.myso.kr)
// @license      Apache-2.0
// @version      1.0.11
// @author       Won Choi
// @connect      naver.com
// @match        *://admin.blog.naver.com/*/stat/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/vendor/gm-app.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/vendor/gm-add-style.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/vendor/gm-add-script.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/vendor/gm-xmlhttp-request-async.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/donation.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/assets/lib/naver-blog.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone.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/xlsx/0.17.0/xlsx.full.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.11.0/toastify.min.js
// ==/UserScript==

// ==OpenUserJS==
// @author myso
// ==/OpenUserJS==
GM_App(async function main() {
    GM_donation('.l__container');
    GM_addStyle("@import url('https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.11.0/toastify.min.css')");
    moment.tz.setDefault("Asia/Seoul");
    async function download() {
        const edate = moment().day(-6).toDate();
        const range = _.range(15).map(o=>moment(edate).subtract(o, 'weeks').toISOString(true));
        const range_date = _.range(7 * 15).map(o=>moment(edate).subtract(o - 6, 'days').toISOString(true));
        const sdate = _.minBy(range, o=>moment(o).toDate());
        const xlsx = XLSX.utils.book_new();
        const xlsx_name = `블로그통계분석_${user.nickname}_${user.userId}_${moment(sdate).format('YYYY-MM-DD')}~${moment(edate).format('YYYY-MM-DD')}.xlsx`
        // 방문분석
        {
            Toastify({ text: `방문 통계 데이터 가져오는 중...`, }).showToast();
            const data_view = await NB_blogStat['방문분석'](user.userId, edate, 'WEEK');
            const xlsx_sheet_name = `방문`;
            const xlsx_sheet_data = [];
            {
                xlsx_sheet_data.push(['■ 방문통계 요약']);
                const cv_sum = _.sumBy(data_view['조회수']['cv'], 'total').toFixed(0);
                const cv_avg = _.meanBy(data_view['조회수']['cv'], 'total').toFixed(0);
                const uv_sum = _.sumBy(data_view['순방문자수']['uv'], 'total').toFixed(0);
                const uv_avg = _.meanBy(data_view['순방문자수']['uv'], 'total').toFixed(0);
                const rv_sum = _.sumBy(data_view['재방문율']['revisit'], 'total').toFixed(0);
                const rv_avg = _.meanBy(data_view['재방문율']['revisit'], 'total').toFixed(0);
                const rvp_avg = _.meanBy(data_view['재방문율']['revisit'], 'total_p').toFixed(2);
                const visit_sum = _.sumBy(data_view['방문횟수']['visit'], 'visit').toFixed(0);
                const visit_avg = _.meanBy(data_view['방문횟수']['visit'], 'visit').toFixed(0);
                const av_avg = _.meanBy(data_view['평균방문횟수']['averageVisit'], 'total').toFixed(2);
                const ad_avg = _.meanBy(data_view['평균사용시간']['averageDuration'], 'total').toFixed(2);
                xlsx_sheet_data.push(['', '조회수', '순방문자수', '재방문수', '재방문율 (%)', '방문횟수', '평균방문횟수', '평균사용시간 (초)']);
                xlsx_sheet_data.push(['평균', cv_avg, uv_avg, rv_avg, rvp_avg, visit_avg, av_avg, ad_avg]);
                xlsx_sheet_data.push(['합계', cv_sum, uv_sum, rv_sum, '-', visit_sum, '-', '-']);
            }
            xlsx_sheet_data.push([]);
            // 표
            {
                xlsx_sheet_data.push(['■ 방문통계 주차별 통계']);
                xlsx_sheet_data.push(['날짜', '조회수', '순방문자수', '재방문수', '재방문율 (%)', '방문횟수', '평균방문횟수', '평균사용시간 (초)']);
                xlsx_sheet_data.push(...Array.from(range).map((item) => {
                    const data = [];
                    const date = moment(item).format('YYYY-MM-DD');
                    data.push(date);
                    data.push(_.get(_.find(data_view['조회수']['cv'], { date }), 'total', 0).toFixed(0));
                    data.push(_.get(_.find(data_view['순방문자수']['uv'], { date }), 'total', 0).toFixed(0));
                    data.push(_.get(_.find(data_view['재방문율']['revisit'], { date }), 'total', 0).toFixed(0));
                    data.push(_.get(_.find(data_view['재방문율']['revisit'], { date }), 'total_p', 0).toFixed(2));
                    data.push(_.get(_.find(data_view['방문횟수']['visit'], { date }), 'visit', 0).toFixed(0));
                    data.push(_.get(_.find(data_view['평균방문횟수']['averageVisit'], { date }), 'total', 0).toFixed(2));
                    data.push(_.get(_.find(data_view['평균사용시간']['averageDuration'], { date }), 'total', 0).toFixed(2));
                    return data;
                }));
            }
            const xlsx_sheet =  XLSX.utils.aoa_to_sheet(xlsx_sheet_data);
            XLSX.utils.book_append_sheet(xlsx, xlsx_sheet, xlsx_sheet_name);
        }
        // 사용자분석
        {
            Toastify({ text: `이웃 통계 데이터 가져오는 중...`, }).showToast();
            const data_user = await NB_blogStat['사용자분석'](user.userId, edate, 'WEEK');
            const xlsx_sheet_name = `이웃`;
            const xlsx_sheet_data = [];
            {
                xlsx_sheet_data.push(['■ 이웃 증감 현황']);
                xlsx_sheet_data.push(['날짜', '서로이웃', '이웃추가', '이웃삭제']);
                xlsx_sheet_data.push(...Array.from(range).map((item) => {
                    const data = [];
                    const date = moment(item).format('YYYY-MM-DD');
                    data.push(date);
                    data.push(_.get(_.find(data_user['이웃증감수']['relationDelta'], { date }), 'friend', '-'));
                    data.push(_.get(_.find(data_user['이웃증감수']['relationDelta'], { date }), 'add', '-'));
                    data.push(_.get(_.find(data_user['이웃증감수']['relationDelta'], { date }), 'delete', '-'));
                    return data;
                }));
            }
            xlsx_sheet_data.push([]);
            {
                xlsx_sheet_data.push(['■ 이웃 방문 현황']);
                xlsx_sheet_data.push(['날짜', '조회수:전체', '순방문자수:전체', '조회수:서로이웃', '순방문자수:서로이웃', '조회수:이웃', '순방문자수:이웃', '조회수:기타', '순방문자수:기타']);
                xlsx_sheet_data.push(...Array.from(range).map((item) => {
                    const data = [];
                    const date = moment(item).format('YYYY-MM-DD');
                    data.push(date);
                    data.push(_.get(_.find(data_user['이웃방문현황']['조회수']['relationVisit'], { date }), 'total', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['순방문자수']['relationVisit'], { date }), 'total', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['조회수']['relationVisit'], { date }), 'friend', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['순방문자수']['relationVisit'], { date }), 'friend', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['조회수']['relationVisit'], { date }), 'follow', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['순방문자수']['relationVisit'], { date }), 'follow', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['조회수']['relationVisit'], { date }), 'etc', '-'));
                    data.push(_.get(_.find(data_user['이웃방문현황']['순방문자수']['relationVisit'], { date }), 'etc', '-'));
                    return data;
                }));
            }
            const xlsx_sheet =  XLSX.utils.aoa_to_sheet(xlsx_sheet_data);
            XLSX.utils.book_append_sheet(xlsx, xlsx_sheet, xlsx_sheet_name);
        }
        // 유입분석
        {
            function get_search_query(url) {
                try {
                    const uri = new URL(url);
                    if(uri.searchParams.has('topReferer')) { return get_search_query(uri.searchParams.get('topReferer')) };
                    if(uri.searchParams.has('proxyReferer')) { return get_search_query(uri.searchParams.get('proxyReferer')) };
                    return uri.searchParams.get('query') || uri.searchParams.get('q');
                } catch(e) {}
            }
            Toastify({ text: `유입 통계 데이터 가져오는 중...`, }).showToast();
            const xlsx_sheet_name = `유입`;
            const xlsx_sheet_data = [];
            {
                xlsx_sheet_data.push(['날짜', '도메인', '키워드', '경로', '유입수', '유입률 (%)']);
                const data_init = await Promise.map(range_date, async (date) => await NB_blogStat['사용자분석']['유입분석']['전체'](user.userId, date, 'DATE'));
                const data_view = await Promise.map(data_init, async (item) => {
                    const data = await Promise.map(item['refererTotal'], async (item) => {
                        const resp = await NB_blogStat['사용자분석']['유입분석']['전체']['상세'](user.userId, item.date, 'DATE', { searchEngine: item.referrerSearchEngine, refererDomain: item.referrerDomain }).catch(e=>null);
                        const data = (resp && resp['refererDetail']) || [];
                        return data.map((o)=>Object.assign({}, item, o)).flat();
                    });
                    return data.flat();
                });
                data_view.flat().map((item)=>{
                    if(!item.searchQuery) item.searchQuery = get_search_query(item.referrerUrl);
                    xlsx_sheet_data.push([item.date, item.referrerDomain, item.searchQuery, item.referrerUrl, item.cv, item.cv_p])
                });
            }
            const xlsx_sheet =  XLSX.utils.aoa_to_sheet(xlsx_sheet_data);
            XLSX.utils.book_append_sheet(xlsx, xlsx_sheet, xlsx_sheet_name);
        }
        // 순위분석
        {
            Toastify({ text: `게시물 통계 데이터 가져오는 중...`, }).showToast();
            const xlsx_sheet_name = `게시물`;
            const xlsx_sheet_data = [];
            const data_rank = await Promise.map(range, async (date) => { const resp = await NB_blogStat['순위']['조회수']['게시물'](user.userId, date, 'WEEK'); return resp.rankCv; });
            const data_rank_articles_kv = _.mapValues(_.groupBy(data_rank.filter(v=>!!v).flat(), 'uri'), (items) => {
                const stats = Array.from(range).map((item) => {
                    const date = moment(item).format('YYYY-MM-DD');
                    const defs = _.assign({}, items[0], { cv: 0, rank: 0, date });
                    const data = _.find(items, { date });
                    return data || defs;
                });
                stats.cv_avg = _.meanBy(stats, 'cv');
                stats.cv_sum = _.sumBy(stats, 'cv');
                stats.rank_avg = _.meanBy(stats, 'rank');
                return stats;
            });
            const data_rank_articles = _.orderBy(_.map(data_rank_articles_kv, (items, uri)=>(items.uri = uri, items.title = _.get(items, '[0].title', '-'), items)), ['cv_sum', 'rank_avg'], ['desc', 'asc']);
            data_rank_articles.cv_avg = _.meanBy(data_rank_articles, 'cv_avg').toFixed(2);
            data_rank_articles.cv_sum = _.sumBy(data_rank_articles, 'cv_sum').toFixed(0);

            xlsx_sheet_data.push(['날짜', '주소', '제목', '순위', '전체 누적 조회수', '주간 평균 조회수', '주간 누적 조회수']);
            data_rank_articles.map((item) => {
                item.map((data) => xlsx_sheet_data.push([data.date, item.uri, item.title, data.rank, item.cv_sum, item.cv_avg, data.cv]));
            });
            const xlsx_sheet =  XLSX.utils.aoa_to_sheet(xlsx_sheet_data);
            XLSX.utils.book_append_sheet(xlsx, xlsx_sheet, xlsx_sheet_name);
        };
        //XLSX 저장
        const xlsx_opts = { bookType:'xlsx', bookSST:false, type:'array' };
        const xlsx_blob = XLSX.write(xlsx, xlsx_opts);
        saveAs(new Blob([xlsx_blob],{type:"application/octet-stream"}), xlsx_name);
        Toastify({ text: `저장 완료`, }).showToast();
    }
    const user = await NB_blogInfo('', 'BlogUserInfo'); if(!user) return;
    const wrap = document.querySelector('#nav.lnb__local-menu'); if(!wrap) return;
    const cont = wrap.querySelector('.lnb__download') || document.createElement('div'); cont.classList.add('lnb__depth1', 'lnb__download'); wrap.append(cont);
    const link = cont.querySelector('.lnb__title') || document.createElement('a'); link.classList.add('lnb__title'); link.textContent = '지표 다운로드 플러스'; cont.append(link);
    cont.addEventListener('click', (event)=>{
        event.preventDefault();
        event.stopPropagation();
        download();
    });
});