抖音视频解析代码

抖音视频是个好东西,可以1080P,无水印解析,唯一的缺点就是要填写 cookie。

播放演示

已获取的长久视频地址

代码:

修复了移动端总是 "code": 500, 无法正常获取视频链接的BUG。

<?php
/**
 * 抖音视频解析接口
 * 用法:/parse.php?url=抖音分享链接
 */
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');

// ==================== 配置 ====================
$customCookie = 'ttwid=1%7CRejU7p-9wM-Qu_kG3IwTGTUCLrZHqB14nkyV-aaViJI%7C1790666684%7Cc31a024ddfe52459acaff945bb806643c229e0682cbced738176c70945f676a2';      // 抖音 Cookie(可从浏览器开发者工具获取 www.douyin.com 标头 set-cookie 的 ttwid 的值)
$defaultRatio = '1080p'; // 默认画质
// =============================================

$shareUrl = $_GET['url'] ?? '';
if (!$shareUrl) {
    echo json_encode(['code' => 400, 'msg' => '缺少 url 参数'], JSON_UNESCAPED_UNICODE);
    exit;
}

try {
    $videoId = getDouyinVideoId($shareUrl, $customCookie);
    if (!$videoId) {
        throw new Exception('解析失败,可能被风控或页面结构已变化');
    }

    $playUrl = "https://aweme.snssdk.com/aweme/v1/play/?video_id={$videoId}&ratio={$defaultRatio}&line=0";

    $result = [
        'code'     => 0,
        'msg'      => 'success',
        'url'      => $playUrl,
    ];

    echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (Exception $e) {
    echo json_encode(['code' => 500, 'msg' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
}

function curlInit(string $url, string $cookie = '', bool $follow = true, array $headers = [])
{
    $ch = curl_init();
    $opt = [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => $follow,
        CURLOPT_MAXREDIRS      => 5,
        CURLOPT_TIMEOUT        => 15,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_USERAGENT      => 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
    ];
    if ($cookie) $opt[CURLOPT_COOKIE] = $cookie;
    if ($headers) $opt[CURLOPT_HTTPHEADER] = $headers;
    curl_setopt_array($ch, $opt);
    return $ch;
}

function expandShortUrl(string $shortUrl, string $cookie = ''): ?string
{
    $ch = curlInit($shortUrl, $cookie);
    $ok = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $effectiveUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
    curl_close($ch);

    return ($ok !== false && $httpCode < 400 && $effectiveUrl) ? $effectiveUrl : null;
}

function extractAwemeId(string $longUrl): ?string
{
    return preg_match('/(?:video|note)\/(\d+)/', $longUrl, $m) ? $m[1] : null;
}

/**
 * 判断字符串是否为抖音 video_id(v0 / v1 / v1e ... 前缀 + 数字字母)
 */
function isVideoId(string $value): bool
{
    return (bool) preg_match('/^v[0-9][0-9a-z]+/i', $value);
}

function getVideoIdFromSharePage(string $awemeId, string $cookie = ''): ?string
{
    $ch = curlInit("https://www.iesdouyin.com/share/video/{$awemeId}", $cookie, true, [
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language: zh-CN,zh;q=0.9',
        'Referer: https://www.douyin.com/',
    ]);

    $html = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($html === false || $httpCode >= 400) return null;

    if (!preg_match('/window\._ROUTER_DATA\s*=\s*(.*?)<\/script>/s', $html, $m) &&
        !preg_match('/window\.__INITIAL_STATE__\s*=\s*(.*?)<\/script>/s', $html, $m)) {
        return null;
    }

    $data = json_decode(rtrim(trim($m[1]), ';'), true);
    if (!is_array($data)) return null;

    $paths = [
        ['loaderData', 'video_(id)/page', 'videoInfoRes', 'item_list', 0, 'video', 'play_addr', 'uri'],
        ['videoInfoRes', 'item_list', 0, 'video', 'play_addr', 'uri'],
        ['item_list', 0, 'video', 'play_addr', 'uri'],
    ];

    foreach ($paths as $path) {
        $value = $data;
        foreach ($path as $key) {
            if (is_array($value) && array_key_exists($key, $value)) {
                $value = $value[$key];
            } else {
                $value = null;
                break;
            }
        }
        if (is_string($value) && isVideoId($value)) return $value;
    }

    return findVideoId($data);
}

function findVideoId($data): ?string
{
    if (!is_array($data)) return null;

    foreach ($data as $value) {
        if (is_string($value) && isVideoId($value)) {
            return $value;
        }
        $found = findVideoId($value);
        if ($found !== null) return $found;
    }

    return null;
}

function getDouyinVideoId(string $shareUrl, string $cookie = ''): ?string
{
    $longUrl = expandShortUrl($shareUrl, $cookie) ?: $shareUrl;
    $awemeId = extractAwemeId($longUrl);

    return $awemeId ? getVideoIdFromSharePage($awemeId, $cookie) : null;
}

cookie可以是一个未登陆账号的 ttwid 值,获取方法:电脑浏览器打开抖音网页,开发者工具获取 www.douyin.com 标头 set-cookie 的 ttwid 的值,一般 cookie 有效期大概一年左右。

url 的值就是长久视频链接,一般不会变动。

注意

  • 如果要在网页上引用,记得给网页头部添加 <meta name="referrer" content="no-referrer"> 完全隐藏请求的来源信息,否则会被跨域拦截。
  • 如果是typecho引用,可在主题头部添加中更灵活的 <meta name="referrer" content="same-origin">,防止没有头请求无法提交评论。

评论 (0)