PHP之抖音无水印解析源码

电玩女神 2024-04-17 20:26 171阅读 0赞
  1. <?php
  2. header("Content-Type:text/json;charset=utf-8");
  3. error_reporting(0);
  4. $url = @$_GET['url'];//抖音视频地址
  5. $isFormat = @$_GET['isFormat'];//是否格式化数据,默认true
  6. $old = @$_GET['old'];//是否使用旧版数据格式,默认false
  7. class Douyin
  8. {
  9. private $UA = "Mozilla/5.0 (Linux; Android 8.0.0; MI 6 Build/OPR1.170623.027; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36";
  10. private function getSubstr($str, $leftStr, $rightStr)
  11. {
  12. $left = strpos($str, $leftStr);
  13. $right = strpos($str, $rightStr, $left);
  14. if ($left < 0 or $right < $left) return '';
  15. return substr($str, $left + strlen($leftStr), $right - $left - strlen($leftStr));
  16. }
  17. private function getAwemeId($link, $UA)
  18. {
  19. $link = ltrim($link);//移除左侧空白字符
  20. if (strstr($link, 'http://v.douyin.com/')) {
  21. $context = stream_context_create(array('http' => array('header' => 'User-Agent:' . $UA)));
  22. $html_text = file_get_contents($link, 0, $context);
  23. $str = $this->getSubstr($html_text, "itemId: \"", "\",");
  24. if (!strstr($html_text, "itemId: ")) {
  25. return false;
  26. }
  27. return $str;
  28. }//短链接支持
  29. if (strstr($link, "https://www.iesdouyin.com")) {//长链接
  30. $str = $this->getSubstr($link, "video/", "/?");
  31. return $str;
  32. }//长链接支持
  33. return false;
  34. }
  35. private function getOutPutForError($errorMes, $old)
  36. {
  37. if ($old) {
  38. $error = [
  39. 'status' => false,
  40. 'message' => $errorMes
  41. ];
  42. return json_encode($error);
  43. } else {
  44. $error = [
  45. 'status' => false,
  46. 'errorMes' => $errorMes
  47. ];
  48. return json_encode($error);
  49. }
  50. }
  51. private function getFormatVideoData($data)
  52. {
  53. $detail = @$data;
  54. $info = @$detail['share_info'];//视频分享信息
  55. $aweme_id = @$detail['aweme_id'];
  56. $user_name = @$detail['author']['nickname'];//作者昵称
  57. $shortId = @$detail['author']['short_id'];//作者抖音号
  58. $user_headImg = @$detail['author']['avatar_medium']['url_list'][0];//作者头像
  59. $image = @$detail['video']['origin_cover']['url_list'][0];//封面图片
  60. $urls = @$detail['video']['play_addr']['url_list'];//无水印地址
  61. $music_urls = @$detail['music']['play_url']['url_list'];//音乐地址
  62. $userId = @$detail['author_user_id'];//用户userId
  63. $dynamic_cover = @$detail['video']['dynamic_cover']['url_list'][0];//封面动态图地址
  64. $longVideo = @$detail['long_video'][0]['video']['bit_rate'];//长视频
  65. if (!$longVideo) {
  66. $longVideo = [];
  67. }
  68. $videoData = [
  69. 'nickname' => $user_name,
  70. 'shortId' => $shortId,
  71. 'userId' => $userId,
  72. 'awemeId' => $aweme_id,
  73. 'headImage' => $user_headImg,
  74. 'image' => $image,
  75. 'dynamic_cover' => $dynamic_cover,
  76. 'urls' => $urls,
  77. 'long_video' => $longVideo,
  78. 'music_urls' => $music_urls,
  79. 'info' => $info,
  80. ];
  81. return $videoData;
  82. }
  83. private function getDevices()
  84. {
  85. return explode("\n", str_replace("\r", "", file_get_contents("./douyinDevice.txt")));//去除回车\r
  86. }
  87. private function getVersions()
  88. {
  89. return [
  90. '680' => '6.8.0',
  91. '251' => '2.5.1',
  92. '140' => '1.4.0'
  93. ];
  94. }
  95. private function getApis($deviceInfos, $api_n, &$api_positions)
  96. {
  97. $versions = $this->getVersions();
  98. //$base_api="https://aweme.snssdk.com/aweme/v1/aweme/detail/?origin_type=link&retry_type=no_retry&{device_info}&ac=wifi&channel=update&aid=1128&app_name=aweme&version_code={version_code}&version_name={version_name}&device_platform=android&ssmix=a&device_type=MI+8&device_brand=xiaomi&language=zh&os_api=22&os_version=5.1.1&uuid=865166029463703&openudid=ec6d541a2f7350cd&manifest_version_code=251&resolution=1080*1920&dpi=480&update_version_code=2512&ts=1561136204&as=a1e500706c54fd8c8d&cp=004ad55fc8d60ac4e1&aweme_id=";
  99. $apis = [];
  100. $rand_devices = [];//随机设备信息
  101. $devices_size = sizeof($deviceInfos);//实际设备信息条数
  102. $real_size = $devices_size < $api_n ? $devices_size : $api_n;//最终获取的设备信息数量
  103. //获取随机设备信息
  104. for ($i = 0; $i < $real_size;) {
  105. try {
  106. $rand = random_int(0, sizeof($deviceInfos) - 1);
  107. $rand_device = $deviceInfos[$rand];
  108. if (key_exists($rand_device, $rand_devices)) {
  109. continue;
  110. } else {
  111. $rand_devices[] = $rand_device;
  112. $i++;
  113. }
  114. } catch (Exception $e) {
  115. exit($this->getOutPutForError("PHP随机数错误"));
  116. }
  117. if($rand_device!=""){
  118. $api_positions[]=$rand;
  119. }else{
  120. $api_positions[]=999;
  121. }
  122. }
  123. //生成API
  124. foreach ($versions as $version_code => $version_name) {
  125. $version_apis = [];
  126. foreach ($rand_devices as $device) {
  127. $version_apis[] = "https://aweme.snssdk.com/aweme/v1/aweme/detail/?origin_type=link&retry_type=no_retry&$device&ac=wifi&channel=update&aid=1128&app_name=aweme&version_code=$version_code&version_name=$version_name&device_platform=android&ssmix=a&device_type=MI+8&device_brand=xiaomi&language=zh&os_api=22&os_version=5.1.1&uuid=865166029463703&openudid=ec6d541a2f7350cd&manifest_version_code=$version_code&resolution=1080*1920&dpi=480&update_version_code=2512&ts=1561136204&as=a1e500706c54fd8c8d&cp=004ad55fc8d60ac4e1&aweme_id=";
  128. }
  129. $apis[$version_code] = $version_apis;
  130. }
  131. return $apis;
  132. }
  133. /**
  134. * @param $url
  135. * @param $isFormat
  136. * @param $old
  137. * @return false|string
  138. */
  139. private function parseVideoByLink($url, $isFormat, $old)
  140. {
  141. $awemeId = $this->getAwemeId($url, $this->UA);
  142. $api_positions = [];//记录device位置
  143. $api_positions_error = [];//记录哪一个出错
  144. $api_version = '';//记录使用哪一个版本API
  145. $api_n = 4;//控制每次取得的设备信息数量
  146. $deviceInfos = $this->getDevices();
  147. $versions_apis = $this->getApis($deviceInfos, $api_n, $api_positions);
  148. if(!$versions_apis) return $this->getOutPutForError("设备信息缺失",$old);
  149. $header = array("Accept-Encoding: utf-8",
  150. "User-Agent: okhttp/3.10.0.1"
  151. );
  152. $context = stream_context_create(array("http" => array("header" => $header)));
  153. if ($awemeId) {
  154. $isSuccess = false;
  155. foreach ($versions_apis as $version_code => $apis) {
  156. $count = -1;
  157. $api_version = $version_code;
  158. $api_positions_error_version = [];//记录每个版本出错API
  159. foreach ($apis as $api) {
  160. $count++;
  161. $api_position = $api_positions[$count];
  162. $data = json_decode(file_get_contents($api . $awemeId, 0, $context), true);
  163. $detail = @$data['aweme_detail'];
  164. $forward_item=@$detail['forward_item'];
  165. if($detail&&$forward_item){//用户动态的分享链接
  166. $detail=$forward_item;
  167. }
  168. $short_id=@$detail['author']['short_id'];
  169. if ($detail && $short_id) {
  170. $isSuccess = true;
  171. break;
  172. }
  173. $api_positions_error_version[] = $api_position;
  174. }
  175. $api_positions_error[$version_code] = $api_positions_error_version;
  176. if ($isSuccess) {
  177. break;
  178. }
  179. }
  180. $str_position = null;//储存失败接口位置
  181. foreach ($api_positions_error as $version_code => $eps) {
  182. if (!empty($eps))
  183. $str_position .= '{' . $version_code . ':';
  184. foreach ($eps as $ep) {
  185. $str_position .= "[$ep]";
  186. }
  187. if (!empty($eps))
  188. $str_position .= '}';
  189. }
  190. if (!$isSuccess) {
  191. return $this->getOutPutForError("抖音接口调用失败$str_position", $old);
  192. }
  193. if($old){
  194. if($isFormat) {
  195. $out = $this->getFormatVideoData($detail);
  196. }else{
  197. $out['data']=$detail;
  198. }
  199. $out['status']=true;
  200. $out['message']=$url;
  201. $out['api_position']=$api_position;
  202. $out['error_api']=$str_position;
  203. $out['api_version']=$api_version;
  204. $out['dataType_new']=!$old;
  205. return json_encode($out);
  206. }else{
  207. $out=[
  208. 'status'=>true,
  209. 'message'=>$url,
  210. 'data'=>null,
  211. 'api_position'=>$api_position,
  212. 'api_version'=>$api_version,
  213. 'dataType_new'=>!$old,
  214. 'error_api'=>$str_position
  215. ];
  216. if($isFormat) {
  217. $out['data'] = $this->getFormatVideoData($detail);
  218. }else{
  219. $out['data']=$detail;
  220. }
  221. return json_encode($out);
  222. }
  223. }
  224. return $this->getOutPutForError('链接不正确', $old);
  225. }
  226. private function checkParams($url, &$isFormat, &$old)
  227. {
  228. if (empty($url)) return false;
  229. if ($isFormat == null) $isFormat = true;
  230. if ($old == null) $old = false;
  231. return true;
  232. }
  233. function get($url, $isFormat, $old)
  234. {
  235. $pass = $this->checkParams($url, $isFormat, $old);
  236. if (!$pass)
  237. return $this->getOutPutForError("地址无效", $old);
  238. else
  239. return $this->parseVideoByLink($url, $isFormat, $old);
  240. }
  241. }
  242. $douyin = new Douyin();
  243. echo $douyin->get($url, $isFormat, $old);

原文地址:
https://github.com/zbfzn/douyin-clear-php/blob/master/parseByLink.php

发表评论

表情:
评论列表 (有 0 条评论,171人围观)

还没有评论,来说两句吧...

相关阅读

    相关 【Python爬虫】水印

    在网上看过一些相关教程,有一些解析抖音无水印视频的教程。说是教程,其实大部分都是提供接口,或引流或卖接口。究竟是怎么实现的去水印,就小小地研究了一下。 1.浏览器分析

    相关 去掉水印

    一、想法由来 个人一直比较喜欢抖音上面有创意的小视频,但无奈水印太多,故想去之留片干净的土地! 二、具体操作 (1)复制抖音app原始链接,例如[这个][Link