API仅供学习交流使用,禁止用于商业用途、违法用途等,否则后果自负!
特别注意:user-agent要设置成手机代理,不然多次请求会出现验证码
短链接格式:https://v.kuaishou.com/xxxxxx
接口地址:https://api.kuaishouzt.com/rest/zt/show/web/xxx
关键字段:photo.manifest.adaptationSet.representation.url
以下有两种方法,原文仅供参考。
方法一
- 首先还是获取一个快手短视频或者图集的链接,演示如下:
https://v.kuaishou.com/dlyk1
- 然后传到后端,后端会获取到其重定向到地址,但是考虑到有可能会重定向几次,所以做了一些处理,代码如下:
async getFinalRedirectUrl(url, maxRedirects = 5) {
let currentUrl = url;
let redirectCount = 0;
while (redirectCount < maxRedirects) {
try {
const response = await this.curl(currentUrl, {
method: "GET",
dataType: "text",
followRedirect: false,
});
if (response.headers.location) {
currentUrl = new URL(response.headers.location, currentUrl).href;
console.log(`重定向 ${redirectCount + 1}: ${currentUrl}`);
redirectCount++;
} else {
// 没有更多重定向,返回当前URL
return currentUrl;
}
} catch (error) {
console.error(`重定向过程中出错 (${currentUrl}):`, error);
throw error;
}
}
console.warn(`达到最大重定向次数 (${maxRedirects})`);
return currentUrl;
}
- 获取到的原地址应该如下:
https://v.m.chenzhongtech.com/fw/photo/3xnh6zqupyhfm5e?cc=share_copylink&followRefer=151&shareMethod=TOKEN&docId=9&kpn=KUAISHOU&subBiz=BROWSE_SLIDE_PHOTO&photoId=3xnh6zqupyhfm5e&shareId=18116220443971&shareToken=X44fHQnInAjW1py&shareResourceType=PHOTO_OTHER&userId=3xw7it7sxf6k7pe&shareType=1&et=1_i%2F2007717946053474769_scn0&shareMode=APP&efid=0&originShareId=18116220443971&appType=1&shareObjectId=5241064204127827041&shareUrlOpened=0×tamp=1729591920122
- 研究域名可以得到其中的
ID
,也就是链接中的3xnh6zqupyhfm5e
,然后进行下一步,构造移动版网页URL,如下:
const headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
};
const mobileUrl = `https://c.kuaishou.com/fw/photo/${photoId}`;
// 发送请求获取信息
const response = await this.curl(mobileUrl, {
headers: this.headers,
dataType: "text",
});
console.log("响应数据:", response);
- 最后就是得到响应后的处理了
parseContentInfo(html) {
const jsonMatch = html.match(
/<script>window\.INIT_STATE = (.*?)<\/script>/
);
if (!jsonMatch) return null;
try {
const initState = JSON.parse(jsonMatch[1]);
// 找到包含内容信息的键
const contentInfoKey = Object.keys(initState).find(
(key) => key.startsWith("tusjoh") && (initState[key].photo || initState[key].atlas)
);
console.log("内容信息键:", contentInfoKey);
if (!contentInfoKey) {
console.error("无法找到内容信息");
return null;
}
const contentInfo = initState[contentInfoKey];
console.log("内容信息:", contentInfo);
const isAtlas = !!contentInfo.atlas;
const photoInfo = contentInfo.photo;
const atlasInfo = contentInfo.atlas;
const baseInfo = {
type: isAtlas ? "image_set" : "video",
author: photoInfo.userName,
uid: photoInfo.userId,
avatar: photoInfo.headUrls[0].url,
like_count: photoInfo.likeCount,
comment_count: photoInfo.commentCount,
time: photoInfo.timestamp,
title: photoInfo.caption,
cover: photoInfo.coverUrls[0].url,
view_count: photoInfo.viewCount,
share_count: photoInfo.shareCount || 0,
platform: "kuaishou",
};
if (isAtlas) {
baseInfo.images = atlasInfo.list.map((path, index) => ({
url: `https://${atlasInfo.cdn[0]}${path}`,
width: atlasInfo.size[index].w,
height: atlasInfo.size[index].h,
}));
} else {
baseInfo.url = photoInfo.mainMvUrls[0].url;
baseInfo.duration = photoInfo.duration;
}
return baseInfo;
} catch (error) {
console.error("解析JSON数据时出错:", error);
return null;
}
}
PS:快手的数据有一个很长的键,我们需要找到它,然后在进行处理,代码中的 contentInfoKey
就是起到这个作用,不过以后会不会变就不知道了,主要是方法,变了以后再改即可。
// 找到包含内容信息的键:tusjoh
const contentInfoKey = Object.keys(initState).find(
(key) =>
key.startsWith("tusjoh") && (initState[key].photo || initState[key].atlas)
);
通过 php
编写完整的:
原始版
<?php
class KuaishouParser {
private $maxRedirects = 5;
private $userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
/**
* 获取最终重定向URL
* @param string $url 原始URL
* @return string 最终URL
*/
public function getFinalRedirectUrl($url) {
$redirectCount = 0;
$currentUrl = $url;
while ($redirectCount < $this->maxRedirects) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $currentUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => $this->userAgent
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headers = $this->parseHeaders($response);
curl_close($ch);
if ($httpCode >= 300 && $httpCode < 400 && isset($headers['Location'])) {
$currentUrl = $this->resolveUrl($headers['Location'], $currentUrl);
echo "重定向url " . ": $currentUrl\n";
$redirectCount++;
} else {
return $currentUrl;
}
}
echo "警告:达到最大重定向次数 ({$this->maxRedirects})\n";
return $currentUrl;
}
/**
* 解析响应头
* @param string $response 响应内容
* @return array 头信息数组
*/
private function parseHeaders($response) {
$headers = [];
$headerLines = explode("\r\n", substr($response, 0, strpos($response, "\r\n\r\n")));
foreach ($headerLines as $line) {
if (strpos($line, ': ') !== false) {
list($key, $value) = explode(': ', $line, 2);
$headers[$key] = $value;
}
}
return $headers;
}
/**
* 解析URL相对路径
* @param string $relative 相对路径
* @param string $base 基础URL
* @return string 绝对URL
*/
private function resolveUrl($relative, $base) {
if (parse_url($relative, PHP_URL_SCHEME) != '') {
return $relative;
}
$baseParts = parse_url($base);
$path = preg_replace('#/[^/]*$#', '', $baseParts['path']);
if ($relative[0] == '/') {
$path = '';
}
$absolute = $baseParts['scheme'] . '://' . $baseParts['host'];
if (isset($baseParts['port'])) {
$absolute .= ':' . $baseParts['port'];
}
$absolute .= $path . '/' . $relative;
$absolute = preg_replace('#/(\./)+#', '/', $absolute);
return preg_replace('#/[^/]+/\.\./#', '/', $absolute);
}
/**
* 提取内容ID
* @param string $url 最终URL
* @return string|null 内容ID
*/
private function extractPhotoId($url) {
$path = parse_url($url, PHP_URL_PATH);
if (preg_match('#/photo/([a-zA-Z0-9]+)#', $path, $matches)) {
return $matches[1];
}
return null;
}
/**
* 解析内容信息
* @param string $html 页面HTML
* @return array|null 解析结果
*/
public function parseContentInfo($html) {
if (preg_match('#<script>window\.INIT_STATE = (.*?)</script>#is', $html, $matches)) {
try {
$initState = json_decode($matches[1], true);
if (json_last_error() !== JSON_ERROR_NONE) {
return null;
}
$contentInfoKey = null;
foreach (array_keys($initState) as $key) {
if (preg_match('/^tusjoh/', $key) && (isset($initState[$key]['photo']) || isset($initState[$key]['atlas']))) {
$contentInfoKey = $key;
break;
}
}
if (!$contentInfoKey) {
return null;
}
$contentInfo = $initState[$contentInfoKey];
$isAtlas = isset($contentInfo['atlas']);
$photoInfo = $contentInfo['photo'] ?? [];
$atlasInfo = $contentInfo['atlas'] ?? [];
$baseInfo = [
'type' => $isAtlas ? 'image_set' : 'video',
'author' => $photoInfo['userName'] ?? '',
'uid' => $photoInfo['userId'] ?? '',
'avatar' => $this->getFirstUrl($photoInfo['headUrls'] ?? []),
'like_count' => $photoInfo['likeCount'] ?? 0,
'comment_count' => $photoInfo['commentCount'] ?? 0,
'time' => $photoInfo['timestamp'] ?? 0,
'title' => $photoInfo['caption'] ?? '',
'cover' => $this->getFirstUrl($photoInfo['coverUrls'] ?? []),
'view_count' => $photoInfo['viewCount'] ?? 0,
'share_count' => $photoInfo['shareCount'] ?? 0,
'platform' => 'kuaishou'
];
if ($isAtlas) {
$baseInfo['images'] = [];
foreach (($atlasInfo['list'] ?? []) as $index => $path) {
$cdn = $atlasInfo['cdn'][0] ?? '';
$size = $atlasInfo['size'][$index] ?? [];
$baseInfo['images'][] = [
'url' => "https://$cdn$path",
'width' => $size['w'] ?? 0,
'height' => $size['h'] ?? 0
];
}
} else {
$baseInfo['url'] = $this->getFirstUrl($photoInfo['mainMvUrls'] ?? []);
$baseInfo['duration'] = $photoInfo['duration'] ?? 0;
}
return $baseInfo;
} catch (Exception $e) {
error_log("解析JSON错误: " . $e->getMessage());
return null;
}
}
return null;
}
/**
* 获取数组中第一个URL
* @param array $urls URL数组
* @return string|null 第一个URL
*/
private function getFirstUrl($urls) {
if (!empty($urls) && isset($urls[0]['url'])) {
return $urls[0]['url'];
}
return null;
}
/**
* 主解析方法
* @param string $inputUrl 输入的分享URL
* @return array 解析结果
*/
public function parse($inputUrl) {
// 获取最终重定向URL
$finalUrl = $this->getFinalRedirectUrl($inputUrl);
// 提取内容ID
$photoId = $this->extractPhotoId($finalUrl);
if (!$photoId) {
return ['error' => '无法提取内容ID'];
}
// 构造移动版URL
$mobileUrl = "https://c.kuaishou.com/fw/photo/{$photoId}";
// 获取移动版页面内容
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $mobileUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => $this->userAgent,
CURLOPT_HTTPHEADER => [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8'
]
]);
$html = curl_exec($ch);
curl_close($ch);
if (!$html) {
return ['error' => '获取页面内容失败'];
}
// 解析内容信息
$result = $this->parseContentInfo($html);
if (!$result) {
return ['error' => '解析内容信息失败'];
}
return $result;
}
}
// 使用示例
if (isset($_GET['url'])) {
$parser = new KuaishouParser();
$result = $parser->parse(urldecode($_GET['url']));
header('Content-Type: application/json; charset=utf-8');
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} else {
header('Content-Type: text/plain; charset=utf-8');
echo "请提供URL参数,示例:?url=https://v.kuaishou.com/dlyk1";
}
?>
修改版(去除了反斜线)
<?php
class KuaishouParser {
private $maxRedirects = 5;
private $userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
/**
* 获取最终重定向URL
* @param string $url 原始URL
* @return string 最终URL
*/
public function getFinalRedirectUrl($url) {
$redirectCount = 0;
$currentUrl = $url;
while ($redirectCount < $this->maxRedirects) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $currentUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => $this->userAgent
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headers = $this->parseHeaders($response);
curl_close($ch);
if ($httpCode >= 300 && $httpCode < 400 && isset($headers['Location'])) {
$currentUrl = $this->resolveUrl($headers['Location'], $currentUrl);
$redirectCount++;
} else {
return $this->cleanUrl($currentUrl);
}
}
return $this->cleanUrl($currentUrl);
}
/**
* 解析响应头
* @param string $response 响应内容
* @return array 头信息数组
*/
private function parseHeaders($response) {
$headers = [];
$headerText = substr($response, 0, strpos($response, "\r\n\r\n"));
foreach (explode("\r\n", $headerText) as $i => $line) {
if ($i === 0) {
$headers['http_code'] = $line;
} else {
list($key, $value) = explode(': ', $line);
$headers[$key] = $value;
}
}
return $headers;
}
/**
* 解析URL相对路径
* @param string $relative 相对路径
* @param string $base 基础URL
* @return string 绝对URL
*/
private function resolveUrl($relative, $base) {
if (parse_url($relative, PHP_URL_SCHEME) != '') {
return $relative;
}
$baseParts = parse_url($base);
$path = preg_replace('#/[^/]*$#', '', $baseParts['path']);
if ($relative[0] == '/') {
$path = '';
}
$absolute = $baseParts['scheme'] . '://' . $baseParts['host'];
if (isset($baseParts['port'])) {
$absolute .= ':' . $baseParts['port'];
}
$absolute .= $path . '/' . $relative;
$absolute = preg_replace('#/(\./)+#', '/', $absolute);
return preg_replace('#/[^/]+/\.\./#', '/', $absolute);
}
/**
* 提取内容ID
* @param string $url 最终URL
* @return string|null 内容ID
*/
private function extractPhotoId($url) {
$path = parse_url($url, PHP_URL_PATH);
if (preg_match('#/photo/([a-zA-Z0-9]+)#', $path, $matches)) {
return $matches[1];
}
return null;
}
/**
* 解析内容信息
* @param string $html 页面HTML
* @return array|null 解析结果
*/
public function parseContentInfo($html) {
if (preg_match('#<script>window\.INIT_STATE\s*=\s*(.*?)</script>#is', $html, $matches)) {
try {
$initState = json_decode($matches[1], true);
if (json_last_error() !== JSON_ERROR_NONE) {
return null;
}
$contentInfoKey = null;
foreach (array_keys($initState) as $key) {
if (preg_match('/^tusjoh/', $key) && (isset($initState[$key]['photo']) || isset($initState[$key]['atlas']))) {
$contentInfoKey = $key;
break;
}
}
if (!$contentInfoKey) {
return null;
}
$contentInfo = $initState[$contentInfoKey];
$isAtlas = isset($contentInfo['atlas']);
$photoInfo = $contentInfo['photo'] ?? [];
$atlasInfo = $contentInfo['atlas'] ?? [];
$baseInfo = [
'type' => $isAtlas ? 'image_set' : 'video',
'author' => $photoInfo['userName'] ?? '',
'uid' => $photoInfo['userId'] ?? '',
'avatar' => $this->cleanUrl($this->getFirstUrl($photoInfo['headUrls'] ?? [])),
'like_count' => $photoInfo['likeCount'] ?? 0,
'comment_count' => $photoInfo['commentCount'] ?? 0,
'time' => $photoInfo['timestamp'] ?? 0,
'title' => $photoInfo['caption'] ?? '',
'cover' => $this->cleanUrl($this->getFirstUrl($photoInfo['coverUrls'] ?? [])),
'view_count' => $photoInfo['viewCount'] ?? 0,
'share_count' => $photoInfo['shareCount'] ?? 0,
'platform' => 'kuaishou'
];
if ($isAtlas) {
$baseInfo['images'] = [];
foreach (($atlasInfo['list'] ?? []) as $index => $path) {
$cdn = $atlasInfo['cdn'][0] ?? '';
$size = $atlasInfo['size'][$index] ?? [];
$baseInfo['images'][] = [
'url' => $this->cleanUrl("https://$cdn$path"),
'width' => $size['w'] ?? 0,
'height' => $size['h'] ?? 0
];
}
} else {
$baseInfo['url'] = $this->cleanUrl($this->getFirstUrl($photoInfo['mainMvUrls'] ?? []));
$baseInfo['duration'] = $photoInfo['duration'] ?? 0;
}
return $baseInfo;
} catch (Exception $e) {
return null;
}
}
return null;
}
/**
* 获取数组中第一个URL
* @param array $urls URL数组
* @return string|null 第一个URL
*/
private function getFirstUrl($urls) {
if (!empty($urls) && isset($urls[0]['url'])) {
return $urls[0]['url'];
}
return null;
}
/**
* 清理URL中的反斜杠
* @param string $url 原始URL
* @return string 清理后的URL
*/
private function cleanUrl($url) {
if (is_array($url)) {
return array_map([$this, 'cleanUrl'], $url);
}
return $url ? str_replace('\\', '', $url) : $url;
}
/**
* 主解析方法
* @param string $inputUrl 输入的分享URL
* @return array 解析结果
*/
public function parse($inputUrl) {
$finalUrl = $this->getFinalRedirectUrl($inputUrl);
$photoId = $this->extractPhotoId($finalUrl);
if (!$photoId) {
return ['error' => '无法提取内容ID'];
}
$mobileUrl = "https://c.kuaishou.com/fw/photo/{$photoId}";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $mobileUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => $this->userAgent,
CURLOPT_HTTPHEADER => [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8'
]
]);
$html = curl_exec($ch);
curl_close($ch);
if (!$html) {
return ['error' => '获取页面内容失败'];
}
$result = $this->parseContentInfo($html);
if (!$result) {
return ['error' => '解析内容信息失败'];
}
// 深度清理结果中的所有URL
array_walk_recursive($result, function(&$value, $key) {
if (is_string($value) && (strpos($key, 'url') !== false || strpos($value, 'http') === 0)) {
$value = str_replace('\\', '', $value);
}
});
return $result;
}
}
// 使用示例
if (isset($_GET['url'])) {
$parser = new KuaishouParser();
$result = $parser->parse(trim(urldecode($_GET['url'])));
header('Content-Type: application/json; charset=utf-8');
echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} else {
header('Content-Type: text/plain; charset=utf-8');
echo "请提供URL参数,示例:?url=https://v.kuaishou.com/dlyk1";
}
?>
方法二
用Java语言实现快手视频、及图文解析的代码:
- 代码示例
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.HttpCookie;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- @description: 快手视频解析
*/
@Slf4j
public class Analysis {
private static final String XG = "/";
/**
* 快手链接请求解析
* @param url 链接地址
* @return list
*/
private List<String> dealVideoKs(String url) {
try {
// 正则表达式提取链接
url = getPattern(url);
// 链接格式化
url = dealCharStrLast(url);
// 设置为浏览器格式的请求头
HashMap<String, String> headers = MapUtil.newHashMap();
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36");
HttpResponse httpResponse = HttpUtil.createGet(url).addHeaders(headers).execute();
if (httpResponse != null && httpResponse.getStatus() == 302) {
url = httpResponse.header("Location");
}
HttpResponse httpRes = HttpUtil.createGet(url).addHeaders(headers).execute();
if (httpRes != null) {
if (httpRes.getStatus() == 302) {
// 视频解析
return videoUrlKs(httpRes, headers);
} else if (httpRes.getStatus() == 200) {
// 图片解析
return imageUrlKs(httpRes);
}
}
} catch (Exception e) {
log.error("视频解析失败!{}", (Object) e.getStackTrace());
}
return new ArrayList<>();
}
/**
* 图片解析
* @param httpRes 请求
* @return list
*/
private List<String> imageUrlKs(HttpResponse httpRes) {
Document parse = Jsoup.parse(httpRes.body());
Elements script = parse.select("script");
String data = "";
for (Element element : script) {
if (element.data().contains("window.INIT_STATE =")) {
data = element.data();
break;
}
}
if (StringUtils.isNotBlank(data)) {
String trim = data.split("window.INIT_STATE =")[1].trim();
JSONObject object = JSON.parseObject(trim, JSONObject.class);
Iterator<String> iterator = object.keySet().iterator();
JSONObject atlas = null;
while (iterator.hasNext()) {
String key = iterator.next();
String value = object.get(key).toString();
JSONObject obj = JSON.parseObject(value, JSONObject.class);
if (obj.containsKey("fid") && obj.containsKey("atlas")) {
atlas = obj.getJSONObject("atlas");
break;
}
}
if (atlas != null) {
// 获取域名
String host = atlas.getJSONArray("cdn").getStr(0);
if (!host.contains("https://")) {
host = "https://" + host;
}
// 获取图片
List<String> list = new ArrayList<>();
JSONArray array = atlas.getJSONArray("list");
for (Object a : array) {
list.add(host + a);
}
return list;
}
}
return new ArrayList<>();
}
/**
* 快手视频地址解析
* @param httpRes 请求
* @param headers 请求头
* @return list
*/
private List<String> videoUrlKs(HttpResponse httpRes, HashMap<String, String> headers) throws Exception {
String cookie = "";
List<HttpCookie> cookies = httpRes.getCookies();
if (cookies != null && !cookies.isEmpty()) {
String didv = "didv", did = "did";
for (HttpCookie hc : cookies) {
if (didv.equals(hc.getName())) {
didv = hc.getValue();
}
if (did.equals(hc.getName())) {
did = hc.getValue();
}
}
cookie = "didv=" + didv + "; did=" + did;
}
// url链接参数
Map<String, String> map = getMapUrl(httpRes.header("Location"));
// 拼接快手视频地址
String videoUrl = "https://live.kuaishou.com/live_api/profile/feedbyid?photoId=" + map.get("photoId") + "&principalId=" + map.get("userId");
// 设置cookie参数
headers.put("Cookie", cookie);
HttpResponse execute = HttpUtil.createGet(videoUrl).addHeaders(headers).execute();
JSONObject object = JSON.parseObject(execute.body(), JSONObject.class);
JSONObject currentJson = object.getJSONObject("data").getJSONObject("currentWork");
String playUrl = currentJson.getStr("playUrl");
if (StringUtils.isNotBlank(playUrl)) {
return Collections.singletonList(playUrl);
}
return new ArrayList<>();
}
/**
* url链接参数
* @param url 链接
* @return map
*/
private Map<String, String> getMapUrl(String url) throws Exception {
Map<String, String> map = new HashMap<>();
// 解析请求
String[] split = new URL(url).getQuery().split("&");
for (String s : split) {
String[] userStr = s.split("=");
if (userStr.length > 1) {
map.put(userStr[0], userStr[1]);
}
}
return map;
}
/**
* 正则表达式提取链接
* @param url 地址
* @return string
*/
private static String getPattern(String url) {
// 定义正则表达式,并创建Pattern对象
Pattern pattern = Pattern.compile("https?://\\S+");
// 创建Matcher对象
Matcher matcher = pattern.matcher(url);
// 使用List来存储匹配的链接
List<String> urlList = new ArrayList<>();
// 循环查找匹配的链接
while (matcher.find()) {
// 将匹配的链接添加到列表中
urlList.add(matcher.group());
}
if (!urlList.isEmpty()) {
url = urlList.get(0);
}
return url;
}
/**
* 如果最后一个字符是 / 的话,则去掉 / 字符
* @param str 需要处理的链接
* @return string
*/
private String dealCharStrLast(String str) {
if (StringUtils.isNotBlank(str)) {
String last = str.substring(str.length() - 1);
if (XG.equals(last)) {
str = str.substring(0, str.length() - 1);
}
}
return str;
}
}
- 测试
public static void main(String[] args) {
String url = "https://v.kuaishou.com/4eQnbu";
List<String> urlList = dealVideoKs(url);
System.out.println(urlList);
}
- 使用依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.32</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.52</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
- 执行第二步测试得到的结果,复制到浏览器便可下载