Ответ 1
Связанные веб-сайты, которые вы предоставили, уже содержат все, что вам нужно знать для решения этой проблемы. Большая часть работы может быть выполнена с помощью простых регулярных выражений. Это необходимые шаги для декодирования такой скремблированной подписи:
- Загрузите html5player.js, который используется на странице видео, которое вы хотите загрузить.
- Определите имя функции, которая используется для декодирования закодированной подписи.
- Используя это имя, извлеките определение функции из script.
- Запустите извлеченную функцию в JS-интерпретаторе и просто расшифруйте подпись с ней.
Получение html5player.js
Первый шаг не должен быть проблемой, как вы уже это сделали. Большинство URL script всегда будут одинаковыми (https://s.ytimg.com/yts/jsbin/html5player-VERSION/html5player.js
). Время от времени изменяется только часть версии (например, de_DE-vflR89yTY). Это означает, что вам просто нужно найти версию script на видеостранице и адаптировать по умолчанию script URL. В результате URL-адрес будет выглядеть примерно так: https://s.ytimg.com/yts/jsbin/html5player-de_DE-vflR89yTY/html5player.js
Версия игрока: html5player-([\w\d\-]+)\\\/html5player\.js
Версия может быть найдена в первой группе захвата.
Извлечение функции декодирования
Прежде чем мы сможем извлечь функцию, мы должны знать ее имя. К сожалению, имя decode-funtion может меняться от версии к версии, но часть кода, использующего эту функцию, обычно не изменяется.
Имя функции декодирования: \.sig\|\|([a-zA-Z0-9$]+)\(
Имя функции будет в первой группе захвата.
Теперь, когда мы знаем имя, мы можем извлечь эту функцию, снова используя regex. Просто замените # NAME # на имя функции.
Определение декодированной функции: function #NAME#\([^\)]+\){.*?};
Помимо фактической декодированной функции нам нужно извлечь некоторые служебные функции, которые использует функция декодирования. Эти функции содержатся в объекте. Прежде чем мы сможем извлечь определение объекта из html5player.js, мы должны определить имя объекта.
Не забудьте использовать извлеченное определение функции в качестве ввода для регулярного выражения на этот раз.
Имя объекта-помощника: ;([A-Za-z0-9]+)\.
Имя объекта будет в первой группе захвата.
Используя имя объекта и некоторое регулярное выражение, мы можем извлечь определение объекта из player- script так же, как мы это сделали с определением функции раньше. Замените # NAME # на имя объекта с последнего шага.
Определение вспомогательного объекта: var #NAME#={.*?};
Теперь у нас есть все необходимое для восстановления скремблированной подписи.
Декодирование подписи
Последний шаг - использовать извлеченные функции для декодирования подписи. Чтобы выполнить это в .NET, мы должны проанализировать и выполнить код JavaScript. К счастью, есть готовые к использованию решения, которые могут сделать это для нас. Одним из них является интерпретатор JS Jint. Другой вариант - использовать один из Script Двигатели, доступные в Windows. Simon Mourier предоставляет удобную оболочку вокруг этих движков в следующем ответе: fooobar.com/questions/32399/...
Вот небольшая примерная программа, которая объединяет все вышеописанные шаги и использует оболочку Simon для декодирования первой подписи, которую она может найти на жестко закодированной странице видео, и печатать как закодированные, так и декодированные подписи на консоли.
Код написан на С#, но его легко преобразовать в VB.NET.
class Program
{
private const string PlayerScriptUrlTemplate = "https://s.ytimg.com/yts/jsbin/html5player-{0}/html5player.js";
private const string DecodeFunctionPatternTemplate = @"function #NAME#\([^\)]+\){.*?};";
private const string HelperObjectPatternTemplate = @"var #NAME#={.*?};";
private static readonly Regex SignatureRegex = new Regex(@"s=(?<Signature>[A-F0-9]+\.[A-F0-9]+)");
private static readonly Regex PlayerVersionRegex = new Regex(@"html5player-(?<PlayerVersion>[\w\d\-]+)\\\/html5player\.js");
private static readonly Regex DecodeFunctionNameRegex = new Regex(@"\.sig\|\|(?<FunctionName>[a-zA-Z0-9$]+)\(");
private static readonly Regex HelperObjectNameRegex = new Regex(@";(?<ObjectName>[A-Za-z0-9]+)\.");
static void Main()
{
const string videoUrl = "https://www.youtube.com/watch?v=6pIyg35wiB4";
var client = new WebClient();
var videoPageData = client.DownloadString(videoUrl);
var encodedSignature = SignatureRegex.Match(videoPageData).Groups["Signature"].Value;
var playerVersion = PlayerVersionRegex.Match(videoPageData).Groups["PlayerVersion"].Value;
var playerScriptUrl = string.Format(PlayerScriptUrlTemplate, playerVersion);
var playerScript = client.DownloadString(playerScriptUrl);
var decodeFunctionName = DecodeFunctionNameRegex.Match(playerScript).Groups["FunctionName"].Value;
var decodeFunction = Regex.Match(playerScript, DecodeFunctionPatternTemplate.Replace("#NAME#", decodeFunctionName)).Value;
var helperObjectName = HelperObjectNameRegex.Match((decodeFunction)).Groups["ObjectName"].Value;
var helperObject = Regex.Match(playerScript, HelperObjectPatternTemplate.Replace("#NAME#", helperObjectName)).Value;
var engine = new ScriptEngine(ScriptEngine.JavaScriptLanguage);
var decoderScript = engine.Parse(helperObject + decodeFunction);
var decodedSignature = decoderScript.CallMethod(decodeFunctionName, encodedSignature).ToString();
// Jint variant
//var engine = new Engine();
//var decoderScript = engine.Execute(helperObject).Execute(decodeFunction);
//var decodedSignature = decoderScript.Invoke(decodeFunctionName, encodedSignature).ToString();
Console.WriteLine("Encoded Signature\n{0}.\n{1}", encodedSignature.Split('.').First(), encodedSignature.Split('.').Last());
Console.WriteLine();
Console.WriteLine("Decoded Signature\n{0}.\n{1}", decodedSignature.Split('.').First(), decodedSignature.Split('.').Last());
Console.ReadLine();
}
}