douyin-user-data-download

下载抖音用户主页数据!

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

  1. // ==UserScript==
  2. // @name douyin-user-data-download
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.5.1
  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 xhr = new XMLHttpRequest();
  67. xhr.open('GET', aweme_list[i].url.replace("http://", "https://"), true);
  68. xhr.responseType = 'blob';
  69. xhr.onload = (e) => {
  70. let a = document.createElement('a');
  71. a.href = window.URL.createObjectURL(xhr.response);
  72. a.download = (aweme_list[i].desc ? aweme_list[i].desc.replace(/[\/:*?"<>|]/g, "") : aweme_list[i].awemeId) + ".mp4";
  73. a.click()
  74. };
  75. xhr.onprogress = (event) => {
  76. if (event.lengthComputable) {
  77. button.textContent = "下载" + (event.loaded * 100 / event.total).toFixed(1) + '%';
  78. }
  79. };
  80. xhr.send()
  81. });
  82. targetNode.appendChild(button);
  83. targetNode.dataset.added = true;
  84. }
  85. }
  86.  
  87. function createButton(title, top) {
  88. top = top === undefined ? "60px" : top;
  89. const button = document.createElement('button');
  90. button.textContent = title;
  91. button.style.position = 'fixed';
  92. button.style.right = '5px';
  93. button.style.top = top;
  94. button.style.zIndex = '90000';
  95. document.body.appendChild(button);
  96. return button
  97. }
  98.  
  99. function txt2file(txt, filename) {
  100. const blob = new Blob([txt], {type: 'text/plain'});
  101. const url = URL.createObjectURL(blob);
  102. const link = document.createElement('a');
  103. link.href = url;
  104. link.download = filename.replace(/[\/:*?"<>|]/g, "");
  105. document.body.appendChild(link);
  106. link.click();
  107. document.body.removeChild(link);
  108. URL.revokeObjectURL(url);
  109. }
  110.  
  111. function downloadData() {
  112. let text = userKey.join(",") + "\n" + userData.join(",") + "\n\n";
  113. text += "作品描述,点赞数,评论数,收藏数,分享数,发布时间,下载链接\n";
  114. aweme_list.forEach(item => {
  115. text += ['"' + item.desc + '"', item.diggCount, item.commentCount,
  116. item.collectCount, item.shareCount, item.date, item.url].join(",") + "\n"
  117. });
  118. txt2file(text, userData[0] + ".csv");
  119. }
  120.  
  121. function interceptResponse() {
  122. const originalSend = XMLHttpRequest.prototype.send;
  123. XMLHttpRequest.prototype.send = function () {
  124. const self = this;
  125. this.onreadystatechange = function () {
  126. if (self.readyState === 4) {
  127. if (self._url.indexOf("/aweme/v1/web/aweme/post") > -1) {
  128. var json = JSON.parse(self.response);
  129. let post_data = json.aweme_list.map(item => Object.assign(
  130. {"awemeId": item.aweme_id, "desc": item.desc},
  131. {
  132. "diggCount": item.statistics.digg_count,
  133. "commentCount": item.statistics.comment_count,
  134. "collectCount": item.statistics.collect_count,
  135. "shareCount": item.statistics.share_count
  136. },
  137. {
  138. "date": new Date(item.create_time * 1000).toLocaleString(),
  139. "url": item.video.play_addr.url_list[0]
  140. }));
  141. aweme_list = aweme_list.concat(post_data);
  142. if (timer !== undefined)
  143. clearTimeout(timer);
  144. timer = setTimeout(() => createDownloadButton(), 1000);
  145. }
  146. }
  147. };
  148. originalSend.apply(this, arguments);
  149. };
  150. }
  151.  
  152. function scrollPageToBottom() {
  153. const SCROLL_DELAY = 1000; // Adjust the delay between each scroll action (in milliseconds)
  154. let scrollInterval;
  155.  
  156. function getScrollPosition() {
  157. return scrollY || pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
  158. }
  159.  
  160. function scrollToBottom() {
  161. scrollTo(0, document.body.scrollHeight);
  162. }
  163.  
  164. function hasReachedBottom() {
  165. return getScrollPosition() >= (document.body.scrollHeight - innerHeight);
  166. }
  167.  
  168. function scrollLoop() {
  169. if (!hasReachedBottom()) {
  170. scrollToBottom();
  171. } else {
  172. console.log("Reached the bottom of the page!");
  173. clearInterval(scrollInterval);
  174. }
  175. }
  176.  
  177. function startScrolling() {
  178. scrollInterval = setInterval(scrollLoop, SCROLL_DELAY);
  179. }
  180.  
  181. let button = createButton('开启自动下拉到底', '60px');
  182. button.addEventListener('click', startScrolling);
  183. }
  184.  
  185. // To start scrolling, call the function:
  186. scrollPageToBottom();
  187. interceptResponse();
  188. window.onload = () => {
  189. extractDataFromScript();
  190. let button = createButton("下载已加载数据", "81px");
  191. button.addEventListener('click', downloadData);
  192. };
  193.  
  194. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址