Tự Động Bóc Tách Phụ Đề YouTube Sang SRT Qua JavaScript & PHP API
<!– TIÊU ĐỀ BÀI VIẾT TỐI ƯU GOOGLE SEO (
Tự Động Bóc Tách Phụ Đề YouTube Sang SRT Qua JavaScript & PHP API
Chuyên mục: Lập Trình Web, API Scraping & Tự Động Hóa Dữ Liệu
•
•
•
•
Tài Liệu Kỹ Thuật Độc Quyền
📌 Điểm Cốt Lõi Tóm Tắt Cho AI Overview & Kỹ Sư Lập Trình
- Bản chất lưu trữ phụ đề của YouTube: Phụ đề video YouTube (kể cả phụ đề thủ công hay phụ đề tự động nhận diện giọng nói – Auto-generated) không nằm trong luồng video MP4/WebM mà được lưu ở định dạng XML Timed Text hoặc JSON. Cấu hình này được nhúng trực tiếp trong cây HTML trang qua chuỗi khóa
"captionTracks". - Rào cản trình duyệt & Giải pháp CORS: Trình duyệt web chặn các yêu cầu
fetch()trực tiếp từ trang web ngoài đến máy chủ YouTube do chính sách Cross-Origin Resource Sharing (CORS). Giải pháp là chạy trực tiếp trên Console DevTools hoặc bắc cầu thông qua Backend Server-side (Node.js hoặc PHP cURL). - Xây dựng API PHP thuần bọc Proxy trên Hosting cPanel: File
get_Sub.phptiếp nhận tham số URL video, kích hoạt cURL giả lập trình duyệt, bọc Proxy xoay vòng (Proxy Rotation) với HTTP Basic Auth để vượt tường lửa YouTube, bóc tách XML và chuyển đổi sang chuẩn định dạng phụ đề SubRip (.srt) với mốc thời gianHH:MM:SS,MS. - Xử lý triệt để mã lỗi 301 Moved Permanently & 429 Too Many Requests: Lỗi 301 xảy ra khi YouTube ép chuyển hướng HTTPS hoặc trang xác minh Bot; khắc phục bằng cách bật
CURLOPT_FOLLOWLOCATION = true, cấu hìnhCURLOPT_MAXREDIRS = 5và bổ sung bộ Headers giả lập Chrome Desktop đầy đủ.
1. Bức Tranh Toàn Cảnh: Nhu Cầu Tải Phụ Đề YouTube Và Cơ Chế Bảo Vệ Dữ Liệu
Phụ đề video YouTube (Subtitles / Closed Captions – CC) là nguồn tài nguyên văn bản khổng lồ và vô cùng giá trị. Chúng được các nhà phát triển phần mềm sử dụng cho nhiều mục đích thiết thực: huấn luyện các mô hình xử lý ngôn ngữ tự nhiên (NLP), làm dữ liệu đầu vào cho quy trình bóc băng âm thanh (Speech-to-Text), dịch thuật tự động đa ngôn ngữ, tóm tắt video bằng AI (AI Video Summarization) và tạo phụ đề rời cho các trình phát đa phương tiện như VLC, PotPlayer.
Hình 1: Mô hình dòng chảy dữ liệu phụ đề từ máy chủ YouTube sang định dạng SRT thông qua giao thức API Server-side
Tuy nhiên, khi các lập trình viên bắt tay vào tự xây dựng công cụ tải phụ đề, họ nhanh chóng va phải 3 rào cản kỹ thuật nghiêm ngặt:
- Cơ chế hạn chế CORS của trình duyệt: YouTube không bật tiêu đề
Access-Control-Allow-Origin: *cho các endpoint chứa dữ liệu phụ đề. Do đó, một website chạy ở domain riêng không thể dùng lệnhfetch()hoặcaxiostừ Client để lấy trực tiếp dữ liệu. - Định dạng dữ liệu không đồng nhất: YouTube lưu trữ phụ đề dưới dạng tài liệu XML với các thuộc tính
start(thời điểm bắt đầu tính bằng giây) vàdur(độ dài hiển thị), đi kèm các ký tự mã hóa HTML (như&,'). Chúng không thể sử dụng ngay mà bắt buộc phải chuyển đổi sang cấu trúc chuẩn SubRip (.srt) với các dòng đánh số và định dạng thời gian00:00:00,000 --> 00:00:00,000. - Hệ thống tường lửa Rate Limit & Bot Detection: Nếu bạn gửi quá nhiều yêu cầu cào dữ liệu từ cùng một địa chỉ IP máy chủ Hosting, YouTube sẽ lập tức phản hồi mã chuyển hướng
301 Moved Permanently,403 Forbiddenhoặc429 Too Many Requestsnhằm ép người dùng nhập mã CAPTCHA.
2. Trích Xuất Phụ Đề Trực Tiếp Bằng JavaScript Tại Console DevTools
Cách tiếp cận nhanh nhất mà không cần cài đặt bất kỳ công cụ backend nào là thực thi mã JavaScript trực tiếp trên tab video đang mở tại trình duyệt. Nhờ chạy trong chính ngữ cảnh của YouTube (Same-Origin Context), trình duyệt cho phép đọc toàn bộ DOM và sử dụng Cookie phiên làm việc mà không hề bị chặn bởi CORS:
Quy trình 5 bước thực hiện trên trình duyệt:
- Truy cập video YouTube bất kỳ có tính năng phụ đề (biểu tượng [CC]).
- Bật phụ đề hiển thị ít nhất một lần để đảm bảo đối tượng cấu hình được khởi tạo trong bộ nhớ DOM.
- Nhấn phím
F12(hoặc tổ hợp phímCtrl + Shift + Itrên Windows /Cmd + Option + Itrên macOS) và chuyển sang tab Console. - Dán đoạn mã JavaScript xử lý bên dưới và nhấn
Enter. - Tệp phụ đề có đuôi
.srtsẽ tự động được tạo ra và kích hoạt lệnh tải xuống ổ cứng máy tính của bạn ngay lập tức.
(async () => {
try {
// 1. Quét chuỗi captionTracks trong mã nguồn DOM của YouTube
const html = document.documentElement.innerHTML;
const match = html.match(/”captionTracks”:s*([.*?])/);
if (!match) throw new Error(“Video này không chứa dữ liệu phụ đề hoặc chưa bật CC!”);
const tracks = JSON.parse(match[1]);
const selectedTrack = tracks[0]; // Lấy track phụ đề mặc định đầu tiên
console.log(`⏳ Đang tải phụ đề: ${selectedTrack.name.simpleText || “Default”}`);
// 2. Fetch dữ liệu XML phụ đề từ CDN của YouTube
const res = await fetch(selectedTrack.baseUrl);
const xmlText = await res.text();
// 3. Parser XML sang định dạng SRT tiêu chuẩn
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, “text/xml”);
const nodes = xmlDoc.getElementsByTagName(“text”);
let srtOutput = “”;
const formatTime = (sec) => {
const d = new Date(0);
d.setSeconds(Math.floor(sec));
const ms = Math.floor((sec % 1) * 1000).toString().padStart(3, ‘0’);
return d.toISOString().substr(11, 8) + ‘,’ + ms;
};
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const start = parseFloat(node.getAttribute(“start”));
const dur = parseFloat(node.getAttribute(“dur”) || “0”);
const text = node.textContent.replace(/&/g, ‘&’).replace(/"/g, ‘”‘).replace(/'/g, “‘”);
srtOutput += `${i + 1}n${formatTime(start)} –> ${formatTime(start + dur)}n${text}nn`;
}
// 4. Kích hoạt tải file .srt qua Blob Object
const blob = new Blob([srtOutput], { type: “text/srt;charset=utf-8” });
const url = URL.createObjectURL(blob);
const a = document.createElement(“a”);
a.href = url;
a.download = `${document.title.replace(/[\/:*?”<>|]/g, “”) || “subtitles”}.srt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log(“✅ Tải phụ đề SRT thành công!”);
} catch (err) {
console.error(“❌ Lỗi:”, err.message);
}
})();
3. Xây Dựng Backend Node.js Tự Động Hóa Xuất File SRT
Nhiều bạn thắc mắc: “Có thể chạy mã nguồn Node.js thuần túy (với các hàm hệ thống như fs.writeFileSync hay require()) ngay trên trình duyệt người dùng được không?”. Câu trả lời dứt khoát là KHÔNG THỂ vì trình duyệt hoạt động trong cơ chế cô lập bảo mật (Browser Sandbox) nghiêm ngặt để bảo vệ tài nguyên hệ điều hành của người dùng.
Để phục vụ hàng ngàn người dùng truy cập web dán link tải phụ đề, kiến trúc chuẩn là xây dựng một dịch vụ Server-side Node.js sử dụng framework Express kết hợp thư viện youtube-transcript:
Mã nguồn hoàn chỉnh của máy chủ Node.js (server.js):
const express = require(‘express’);
const cors = require(‘cors’);
const { YoutubeTranscript } = require(‘youtube-transcript’);
const app = express();
app.use(cors()); // Bật CORS cho phép các domain khác kết nối
app.use(express.json());
function formatTime(seconds) {
const pad = (n, z = 2) => n.toString().padStart(z, ‘0’);
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
const ms = Math.floor((seconds % 1) * 1000);
return `${pad(h)}:${pad(m)}:${pad(s)},${pad(ms, 3)}`;
}
app.post(‘/api/get-subtitles’, async (req, res) => {
const { videoUrl } = req.body;
if (!videoUrl) return res.status(400).json({ error: ‘Thiếu URL video YouTube!’ });
try {
const transcript = await YoutubeTranscript.fetchTranscript(videoUrl);
let srtContent = ”;
transcript.forEach((item, index) => {
const start = item.offset / 1000;
const end = start + (item.duration / 1000);
const text = item.text.replace(/&/g, ‘&’).replace(/"/g, ‘”‘).replace(/'/g, “‘”);
srtContent += `${index + 1}n${formatTime(start)} –> ${formatTime(end)}n${text}nn`;
});
res.setHeader(‘Content-Type’, ‘text/srt; charset=utf-8’);
res.send(srtContent);
} catch (err) {
res.status(500).json({ error: ‘Không thể lấy phụ đề từ video này. Kiểm tra lại tính năng CC!’ });
}
});
app.listen(3000, () => console.log(‘🚀 Server Node.js đang chạy tại cổng 3000’));
Trong thực tế, đại đa số quản trị viên website và blogger tại Việt Nam sử dụng các gói Shared Hosting (chạy cPanel hoặc DirectAdmin) hỗ trợ ngôn ngữ PHP. Việc triển khai một tệp duy nhất get_Sub.php đem lại sự tiện lợi vượt bậc: không yêu cầu cấu hình máy chủ ảo VPS, không cần cài đặt môi trường Node.js hay tiến trình PM2 phức tạp, và dễ dàng tích hợp trực tiếp vào hệ thống WordPress.
Đặc biệt, để ngăn chặn hoàn toàn việc địa chỉ IP của gói Hosting bị Google/YouTube đưa vào danh sách đen (Blacklist), tệp PHP dưới đây được thiết kế sẵn khối Proxy Wrapper hỗ trợ cả HTTP/HTTPS Proxy và xác thực tài khoản (HTTP Basic Authentication):
// get_Sub.php – API Endpoint bóc tách phụ đề YouTube sang SRT bọc Proxy
header(“Access-Control-Allow-Origin: *”);
header(“Access-Control-Allow-Methods: GET, POST, OPTIONS”);
header(“Access-Control-Allow-Headers: Content-Type, Authorization”);
if ($_SERVER[‘REQUEST_METHOD’] === ‘OPTIONS’) { exit(0); }
// CẤU HÌNH HỆ THỐNG PROXY (Thay đổi theo IP của bạn)
define(‘USE_PROXY’, true); // Đổi thành false nếu muốn test IP gốc
define(‘PROXY_SERVER’, ‘123.45.67.89:8080’);
define(‘PROXY_AUTH’, ‘username:password’); // Để trống nếu không có mật khẩu
function formatSrtTime($seconds) {
$h = floor($seconds / 3600);
$m = floor(($seconds % 3600) / 60);
$s = floor($seconds % 60);
$ms = floor(($seconds – floor($seconds)) * 1000);
return sprintf(“%02d:%02d:%02d,%03d”, $h, $m, $s, $ms);
}
function fetchUrlWithProxy($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Tự động chuyển hướng 301/302
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$headers = [
‘User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36’,
‘Accept-Language: vi,en-US;q=0.9,en;q=0.8’,
‘Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8’
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if (USE_PROXY) {
curl_setopt($ch, CURLOPT_PROXY, PROXY_SERVER);
if (!empty(PROXY_AUTH)) {
curl_setopt($ch, CURLOPT_PROXYUSERPWD, PROXY_AUTH);
}
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ($httpCode === 200 && $response) ? $response : false;
}
$videoUrl = $_REQUEST[‘videoUrl’] ?? ”;
if (empty($videoUrl)) {
header(‘Content-Type: application/json’, true, 400);
echo json_encode([“error” => “Vui lòng cung cấp tham số videoUrl hợp lệ.”]);
exit;
}
$html = fetchUrlWithProxy($videoUrl);
if (!$html) {
header(‘Content-Type: application/json’, true, 500);
echo json_encode([“error” => “Không thể kết nối YouTube hoặc Proxy bị chặn.”]);
exit;
}
preg_match(‘/”captionTracks”:s*([.*?])/’, $html, $matches);
if (empty($matches[1])) {
header(‘Content-Type: application/json’, true, 404);
echo json_encode([“error” => “Video này không có phụ đề (CC).”]);
exit;
}
$tracks = json_decode($matches[1], true);
$xmlContent = fetchUrlWithProxy($tracks[0][‘baseUrl’]);
$xml = @simplexml_load_string($xmlContent);
if (!$xml) {
header(‘Content-Type: application/json’, true, 500);
echo json_encode([“error” => “Lỗi phân tích cú pháp XML phụ đề.”]);
exit;
}
$srtOutput = “”; $idx = 1;
foreach ($xml->text as $node) {
$start = (float)$node[‘start’];
$dur = (float)($node[‘dur’] ?? 0);
$text = html_entity_decode((string)$node, ENT_QUOTES, ‘UTF-8’);
$srtOutput .= $idx . “n” . formatSrtTime($start) . ” –> “ . formatSrtTime($start + $dur) . “n” . $text . “nn”;
$idx++;
}
header(‘Content-Type: text/srt; charset=utf-8’);
header(‘Content-Disposition: attachment; filename=”youtube_subtitles.srt”‘);
echo $srtOutput;
exit;
5. Bóc Tách Kỹ Thuật Debug: Xử Lý Triệt Để Mã Lỗi 301 Moved Permanently
Khi thử nghiệm gọi file API PHP bằng công cụ dòng lệnh cURL (ví dụ: curl "http://khuyendung.net/App/Youtube/get_Sub.php?videoUrl=..."), nhiều kỹ sư gặp tình trạng kết quả trả về một trang mã HTML thông báo lỗi:
<body><h1>301 Moved Permanently</h1><p>The document has been permanently moved.</p></body></html>
Hiện tượng này bắt nguồn từ 2 nguyên nhân cốt lõi trong giao thức web:
- Nguyên nhân 1 – Chuyển hướng cưỡng bức giao thức (HTTP ➔ HTTPS): Máy chủ của bạn (như tên miền khuyendung.net) hoặc máy chủ YouTube cấu hình chứng chỉ SSL/TLS tự động ép chuyển hướng từ cổng 80 (HTTP) sang cổng 443 (HTTPS). Nếu cURL mặc định không được thiết lập cờ theo dõi, nó sẽ dừng lại ngay tại phản hồi trung gian 301.
- Nguyên nhân 2 – Cơ chế Bot Detection chuyển hướng sang CAPTCHA: YouTube phát hiện User-Agent mặc định của cURL (hoặc IP Hosting datacenter không có cookie thực tế), máy chủ phản hồi mã chuyển hướng 302/301 sang trang yêu cầu đăng nhập tài khoản Google.
Giải pháp sửa lỗi 2 lớp:
- Tại mã nguồn PHP: Bắt buộc bổ sung cờ
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);vàcurl_setopt($ch, CURLOPT_MAXREDIRS, 5);. Dòng cấu hình này yêu cầu cURL tự động gửi tiếp request đến địa chỉ mới trong tiêu đềLocationcho tới khi nhận về mã 200 OK thực sự. - Tại dòng lệnh Terminal (cURL client): Luôn thêm tham số
-L(viết tắt của Location) để công cụ cURL tự động nhảy theo chuyển hướng. Ví dụ lệnh chuẩn:
curl -L -X POST "http://khuyendung.net/App/Youtube/get_Sub.php" -d "videoUrl=https://www.youtube.com/watch?v=MÃ_VIDEO" -o "phude.srt"
6. Mã Nguồn Client-side Nhúng Vào Website Tự Động Kích Hoạt Tải SRT
Khi file get_Sub.php đã sẵn sàng trên hosting, bạn có thể nhúng đoạn mã JavaScript sau vào bất kỳ website nào (WordPress, Landing Page, React hoặc HTML tĩnh). Đoạn mã sử dụng kỹ thuật response.blob() để hứng dữ liệu nhị phân và mô phỏng sự kiện nhấp chuột ảo tải file về máy tính:
try {
console.log(“⏳ Đang gửi request sang API PHP bọc Proxy…”);
const endpoint = `http://khuyendung.net/App/Youtube/get_Sub.php?videoUrl=${encodeURIComponent(youtubeUrl)}`;
const res = await fetch(endpoint);
if (!res.ok) {
const err = await res.json();
throw new Error(err.error || “Lỗi máy chủ khi lấy phụ đề”);
}
// Hứng dữ liệu Blob nhị phân từ Header Content-Disposition
const blob = await res.blob();
const downloadUrl = URL.createObjectURL(blob);
const link = document.createElement(‘a’);
link.href = downloadUrl;
link.download = ‘youtube_subtitles.srt’;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(downloadUrl);
console.log(“✅ Tải file .srt thành công!”);
} catch (error) {
console.error(“❌ Lỗi:”, error.message);
}
}
7. Bảng Đối Chiếu Các Phương Pháp Bóc Tách Phụ Đề YouTube 2026
Để lựa chọn phương án phát triển phần mềm tối ưu theo ngân sách và độ phức tạp dự án, bảng phân tích dưới đây so sánh chi tiết 3 hình thức triển khai phổ biến nhất:
| Tiêu Chí So Sánh | JavaScript DevTools Console | Node.js Serverless / Express | PHP cURL Proxy (get_Sub.php) |
|---|---|---|---|
| Độ Phức Tạp Triển Khai | Cực kỳ đơn giản (Không cần server) | Trung bình (Cần VPS/Node environment) | Rất dễ (Upload 1 file lên cPanel) |
| Xử Lý Rào Cản CORS | 100% Vượt qua (Same-Origin) | 100% Vượt qua (Bắc cầu Server) | 100% Vượt qua (Bắc cầu cURL) |
| Khả Năng Chống Chặn IP (Bot) | Tuyệt đối (Dùng IP cá nhân thực) | Dễ bị ban nếu không gắn Proxy | Rất cao (Tích hợp Proxy Rotation) |
| Phù Hợp Cho Ai? | Cá nhân tải lẻ từng video nhanh | Các ứng dụng SaaS lớn đa tính năng | Webmaster WordPress, Affiliate, Tooling |
8. Khuyến Nghị Thiết Bị Phần Cứng Cho Kỹ Sư Chạy Crawler & Proxy Server
Để vận hành các hệ thống tự động cào dữ liệu, quản lý pool Proxy gia đình (Residential Proxy) và xử lý phụ đề video hàng loạt 24/7 mà không làm ảnh hưởng đến máy tính làm việc chính, quý bạn đọc có thể tham khảo các thiết bị chuyên dụng trên sàn Shopee thông qua các nút dưới đây:
🛒 Mua Mini PC Intel Tiết Kiệm Điện Làm Proxy Server
🛒 Mua USB Dcom 4G Xoay IP Tránh Chặn Bot YouTube
🛒 Mua Ổ Cứng Di Động SSD Lưu Trữ Hàng Ngàn File SRT
🛒 Mua Router Cân Bằng Tải Kết Nối API Ổn Định
9. Câu Hỏi Thường Gặp (FAQ) Về Kỹ Thuật Lấy Phụ Đề YouTube
Làm sao để lấy phụ đề tiếng Việt nếu video có nhiều ngôn ngữ hoặc chỉ có phụ đề tự động?
Trong mảng $captionTracks, mỗi phần tử đều có thuộc tính languageCode (ví dụ: vi, en, ja) và cờ kind: "asr" (phụ đề tự động nhận diện giọng nói). Bạn chỉ cần dùng vòng lặp PHP tìm track có $track['languageCode'] === 'vi'. Nếu video không có tiếng Việt, bạn có thể truyền thêm tham số &tlang=vi vào thuộc tính baseUrl để YouTube tự động dịch sang tiếng Việt.
Tại sao cURL trả về thông báo “Không tìm thấy dữ liệu captionTracks”?
Có hai khả năng: Thứ nhất, video đó do tác giả chủ động tắt hoàn toàn tính năng phụ đề (CC). Thứ hai, YouTube trả về trang đồng ý Cookie (Consent page) hoặc trang chặn Bot thay vì trang xem video. Việc nâng cấp User-Agent và kích hoạt Proxy xoay vòng sẽ giải quyết dứt điểm vấn đề này.
Có thể xuất phụ đề sang các định dạng khác như VTT hoặc JSON được không?
Hoàn toàn được. Với WebVTT (.vtt), bạn chỉ cần thay dấu phẩy phân tách mili-giây bằng dấu chấm (00:00:00.000) và thêm tiêu đề WEBVTT ở dòng đầu tiên của tệp. Với JSON, bạn chỉ cần đóng gói mảng gồm các đối tượng {start, end, text} và xuất qua lệnh json_encode().
Nguồn Dữ Liệu & Tài Liệu Tham Khảo Kỹ Thuật (Plain Text)
1. YouTube Data API v3 Specification: Captions Resource and Timed Text Ingestion Standards.
2. W3C Cross-Origin Resource Sharing (CORS) Security Architecture and Browser Preflight Handling.
3. PHP Official Documentation: Client URL Library (cURL), CURLOPT_FOLLOWLOCATION and Proxy Authentication Protocols.
4. SubRip Subtitle Format (.SRT) Specification: Timecode Representation, Sequencing and Text Encoding Rules.
5. MDN Web Docs: DOMParser API, Blob Objects and URL.createObjectURL Client File Generation.
6. Node.js youtube-transcript Open-source Module: Reverse Engineering YouTube Innertube API Endpoint Schemas.
7. StackBlitz WebContainers Engine: In-browser Node.js Execution Environment powered by WebAssembly.