抖音用户主页数据下载

下载抖音用户主页数据!

当前为 2023-07-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 抖音用户主页数据下载
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.4
  5. // @description 下载抖音用户主页数据!
  6. // @author xxmdmst
  7. // @match https://www.douyin.com/user/*
  8. // @icon https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. window.aweme_list = [];
  16. let userKey = [
  17. "昵称", "关注", "粉丝",
  18. "获赞", "抖音号", "IP属地",
  19. "年龄", "签名", "作品数", "主页"
  20. ];
  21. window.userData = [];
  22. let timer;
  23.  
  24. function extractDataFromScript() {
  25. const scriptTag = document.getElementById('RENDER_DATA');
  26. if (!scriptTag) return;
  27. let data = JSON.parse(decodeURIComponent(scriptTag.innerHTML));
  28.  
  29. for (const prop in data) {
  30. if (data.hasOwnProperty(prop) && prop !== "_location" && prop !== "app") {
  31. const user = data[prop];
  32. let userInfo = user.user.user;
  33. userData.push(
  34. userInfo.nickname, userInfo.followingCount, userInfo.mplatformFollowersCount,
  35. userInfo.totalFavorited, (userInfo.uniqueId === "" ? userInfo.uniqueId : userInfo.shortId), userInfo.ipLocation,
  36. userInfo.age, '"' + (userInfo.desc === undefined ? '' : userInfo.desc) + '"', userInfo.awemeCount, "https://www.douyin.com/user/" + userInfo.secUid
  37. );
  38. let post_data = user.post.data.map(item => Object.assign(
  39. {"awemeId": item.awemeId, "desc": item.desc},
  40. item.stats,
  41. {
  42. "date": new Date(item.createTime * 1000).toLocaleString(),
  43. "url": "https:" + item.video.playAddr[0].src
  44. }));
  45. aweme_list = aweme_list.concat(post_data);
  46. }
  47. }
  48. timer = setTimeout(() => createDownloadButton(), 1000);
  49. }
  50.  
  51. function createDownloadButton() {
  52. let targetNodes = document.querySelectorAll("ul.EZC0YBrG > li.Eie04v01 > div > a");
  53. for (let i = 0; i < targetNodes.length; i++) {
  54. let targetNode = targetNodes[i];
  55. if (targetNode.dataset.added)
  56. continue;
  57. const button = document.createElement("button");
  58. button.textContent = "下载";
  59. button.style.position = "absolute";
  60. button.style.right = "5px";
  61. button.style.top = "5px";
  62. button.style.opacity = "0.5";
  63. button.addEventListener("click", function (event) {
  64. event.preventDefault();
  65. event.stopPropagation();
  66. let x = new XMLHttpRequest();
  67. x.open('GET', aweme_list[i].url.replace("http://", "https://"), true);
  68. x.responseType = 'blob';
  69. x.onload = (e) => {
  70. let a = document.createElement('a');
  71. a.href = window.URL.createObjectURL(x.response);
  72. a.download = (aweme_list[i].desc?aweme_list[i].desc.replace(/[\/:*?"<>|]/g, ""):aweme_list[i].awemeId) + ".mp4";
  73. a.click()
  74. };
  75. x.send()
  76. });
  77. targetNode.appendChild(button);
  78. targetNode.dataset.added = true;
  79. }
  80. }
  81.  
  82. function createButton(title, top) {
  83. top = top === undefined ? "60px" : top;
  84. const button = document.createElement('button');
  85. button.textContent = title;
  86. button.style.position = 'fixed';
  87. button.style.right = '5px';
  88. button.style.top = top;
  89. button.style.zIndex = '90000';
  90. document.body.appendChild(button);
  91. return button
  92. }
  93.  
  94. function txt2file(txt, filename) {
  95. const blob = new Blob([txt], {type: 'text/plain'});
  96. const url = URL.createObjectURL(blob);
  97. const link = document.createElement('a');
  98. link.href = url;
  99. link.download = filename.replace(/[\/:*?"<>|]/g, "");
  100. document.body.appendChild(link);
  101. link.click();
  102. document.body.removeChild(link);
  103. URL.revokeObjectURL(url);
  104. }
  105.  
  106. function downloadData() {
  107. let text = userKey.join(",") + "\n" + userData.join(",") + "\n\n";
  108. text += "作品描述,点赞数,评论数,收藏数,分享数,发布时间,下载链接\n";
  109. aweme_list.forEach(item => {
  110. text += ['"' + item.desc + '"', item.diggCount, item.commentCount,
  111. item.collectCount, item.shareCount, item.date, item.url].join(",") + "\n"
  112. });
  113. txt2file(text, userData[0] + ".csv");
  114. }
  115.  
  116. function interceptResponse() {
  117. const originalSend = XMLHttpRequest.prototype.send;
  118. XMLHttpRequest.prototype.send = function () {
  119. const self = this;
  120. this.onreadystatechange = function () {
  121. if (self.readyState === 4) {
  122. if (self._url.indexOf("/aweme/v1/web/aweme/post") > -1) {
  123. var json = JSON.parse(self.response);
  124. let post_data = json.aweme_list.map(item => Object.assign(
  125. {"awemeId": item.aweme_id, "desc": item.desc},
  126. {
  127. "diggCount": item.statistics.digg_count,
  128. "commentCount": item.statistics.comment_count,
  129. "collectCount": item.statistics.collect_count,
  130. "shareCount": item.statistics.share_count
  131. },
  132. {
  133. "date": new Date(item.create_time * 1000).toLocaleString(),
  134. "url": item.video.play_addr.url_list[0]
  135. }));
  136. aweme_list = aweme_list.concat(post_data);
  137. if (timer !== undefined)
  138. clearTimeout(timer);
  139. timer = setTimeout(() => createDownloadButton(), 1000);
  140. }
  141. }
  142. };
  143. originalSend.apply(this, arguments);
  144. };
  145. }
  146.  
  147. function scrollPageToBottom() {
  148. const SCROLL_DELAY = 1000; // Adjust the delay between each scroll action (in milliseconds)
  149. let scrollInterval;
  150.  
  151. function getScrollPosition() {
  152. return scrollY || pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
  153. }
  154.  
  155. function scrollToBottom() {
  156. scrollTo(0, document.body.scrollHeight);
  157. }
  158.  
  159. function hasReachedBottom() {
  160. return getScrollPosition() >= (document.body.scrollHeight - innerHeight);
  161. }
  162.  
  163. function scrollLoop() {
  164. if (!hasReachedBottom()) {
  165. scrollToBottom();
  166. } else {
  167. console.log("Reached the bottom of the page!");
  168. clearInterval(scrollInterval);
  169. }
  170. }
  171.  
  172. function startScrolling() {
  173. scrollInterval = setInterval(scrollLoop, SCROLL_DELAY);
  174. }
  175.  
  176. let button = createButton('开启自动下拉到底', '60px');
  177. button.addEventListener('click', startScrolling);
  178. }
  179.  
  180. // To start scrolling, call the function:
  181. scrollPageToBottom();
  182. interceptResponse();
  183. window.onload = () => {
  184. extractDataFromScript();
  185. let button = createButton("下载已加载数据", "81px");
  186. button.addEventListener('click', downloadData);
  187. };
  188.  
  189. })();