// ==UserScript== // @name 抖音用户主页数据下载 // @namespace http://tampermonkey.net/ // @version 0.2 // @description 下载抖音用户主页数据! // @author xxmdmst // @match https://www.douyin.com/user/* // @icon https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; let aweme_list = []; let userKey = [ "昵称", "关注", "粉丝", "获赞", "抖音号", "IP属地", "年龄", "签名", "作品数", "主页" ]; let userData = []; function extractDataFromScript() { const scriptTag = document.getElementById('RENDER_DATA'); if (!scriptTag) return; let data = JSON.parse(decodeURIComponent(scriptTag.innerHTML)); for (const prop in data) { if (data.hasOwnProperty(prop) && prop !== "_location" && prop !== "app") { const user = data[prop]; let userInfo = user.user.user; userData.push( userInfo.nickname, userInfo.followingCount, userInfo.mplatformFollowersCount, userInfo.totalFavorited, userInfo.shortId, userInfo.ipLocation, userInfo.age, '"' + userInfo.desc + '"', userInfo.awemeCount, "https://www.douyin.com/user/" + userInfo.secUid ); let post_data = user.post.data.map(item => Object.assign({"desc": item.desc}, item.stats, {"url": "https:" + item.video.playAddr[0].src})); aweme_list = aweme_list.concat(post_data); } } } function createButton(title, top) { top = top === undefined ? "60px" : top; const button = document.createElement('button'); button.textContent = title; button.style.position = 'fixed'; button.style.right = '5px'; button.style.top = top; button.style.zIndex = '90000'; document.body.appendChild(button); return button } function txt2file(txt, filename) { const blob = new Blob([txt], {type: 'text/plain'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename.replace(/[\/:*?"<>|]/g, ""); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } function downloadData() { let text = userKey.join(",") + "\n" + userData.join(",") + "\n\n"; text += "作品描述,点赞数,评论数,收藏数,分享数,下载链接\n"; aweme_list.forEach(item => { text += ['"' + item.desc + '"', item.diggCount, item.commentCount, item.collectCount, item.shareCount, item.url].join(",") + "\n" }); txt2file(text, userData[0] + ".csv"); } function interceptResponse() { const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function () { const self = this; this.onreadystatechange = function () { if (self.readyState === 4) { if (self._url.indexOf("/aweme/v1/web/aweme/post") > -1) { var json = JSON.parse(self.response); console.log(json.aweme_list); let post_data = json.aweme_list.map(item => Object.assign( {"desc": item.desc}, { "diggCount": item.statistics.digg_count, "commentCount": item.statistics.comment_count, "collectCount": item.statistics.collect_count, "shareCount": item.statistics.share_count, }, {"url": item.video.play_addr.url_list[0]})); aweme_list = aweme_list.concat(post_data); } } }; originalSend.apply(this, arguments); }; } function scrollPageToBottom() { const SCROLL_DELAY = 1000; // Adjust the delay between each scroll action (in milliseconds) let scrollInterval; function getScrollPosition() { return scrollY || pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; } function scrollToBottom() { scrollTo(0, document.body.scrollHeight); } function hasReachedBottom() { return getScrollPosition() >= (document.body.scrollHeight - innerHeight); } function scrollLoop() { if (!hasReachedBottom()) { scrollToBottom(); } else { console.log("Reached the bottom of the page!"); clearInterval(scrollInterval); } } function startScrolling() { scrollInterval = setInterval(scrollLoop, SCROLL_DELAY); } let button = createButton('开启自动下拉到底', '60px'); button.addEventListener('click', startScrolling); } // To start scrolling, call the function: scrollPageToBottom(); interceptResponse(); window.onload = () => { extractDataFromScript(); let button = createButton("下载已加载数据", "81px"); button.addEventListener('click', downloadData); }; })();