Скачивание видео из постов в вк python

Всем привет!
Стоит задача написать код для скачивания видео из Вконтакте, используя ссылку.
Пример ссылки:
https://m.vk.com/video-28402905_456290220
Никак не могу прийти к толковому решению. Как варианты пробовал использовать сервис savefrom.net и подобные, отправлять на него запросы с помощью requests, но опять же ничего.
Помогите пожалуйста с решением.


Ответы (1 шт):

Автор решения: Johan

Что ж, вот алгоритм действий, которые нужно выполнить, чтобы скачать видео:

  1. Находим ссылку на плейлист;

введите сюда описание изображения

  1. Загружаем плейлист со ссылками на видео различного разрешения, открывает и смотрим содержимое;

введите сюда описание изображения

Вот, для примера ссылка на видео с разрешением 720p.

  1. Скачиваем плейлист с фрагментами видео, открываем и смотрим названия фрагментов;

введите сюда описание изображения

Таким образом, именно в данном случае, ссылка на фрагмент видео будет:

https://vkvd195.mycdn.me/expires/1662334705380/srcIp/185.210.141.162/pr/40/srcAg/CHROME/ms/45.136.22.202/type/3/sig/JCpfvUsM1YE/ct/8/urls/185.226.52.156/clientType/14/zs/15/id/2927887321615/video/HIGH00000.ts

Почему в данном случае? Дело в том, что не всегда в плейлистах к фрагментам указаны именно такие ссылки и не всегда именно в этом виде.Таким образом, если писать код под этот, и похожие, случаи, это будет выглядеть примерно вот так:

import os.path
import shutil

from requests import get
from bs4 import BeautifulSoup

headers = {
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
                  'Safari/537.36',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
              'application/signed-exchange;v=b3;q=0.9 '
}


def rep_symbol(text: str):
    tex = text.replace("'", "").replace('"', '').replace('|', '_').replace(' | ', '_').replace('/', '_'). \
        replace('\\', '_').replace('*', '_').replace('?', '').replace('<', '').replace('>', '_').replace(':', ''). \
        replace(';', '').replace('.', '').strip()
    return tex


def get_m3u8(url: str) -> tuple:
    req = get(url=url, headers=headers)
    soup = BeautifulSoup(req.text, 'lxml')
    title = rep_symbol(soup.find('h1', class_='VideoPageInfoRow__title').text).replace(" ", "_")
    m3u8 = soup.find('div', class_='VideoPage__video').find_all('source')[0].get('src')
    return title, m3u8


def parse_m3u8(m3u8: str) -> dict:
    links = {}
    res = get(url=m3u8, headers=headers).text.splitlines()
    for num in range(0, len(res)):
        if "480" in res[num]:
            links.update({"480": res[num+1]})
        elif "720" in res[num]:
            links.update({"720": res[num+1]})
        elif "360" in res[num]:
            links.update({"360": res[num+1]})
    return links


def download_chunk(title: str, links: dict):
    dir_vid = os.path.join(os.getcwd(), 'video', title)
    if not os.path.exists(os.path.join(os.getcwd(), 'video')):
        os.mkdir(os.path.join(os.getcwd(), 'video'))
    if not os.path.exists(dir_vid):
        os.mkdir(dir_vid)

    if "720" in links:
        url = links["720"]
        find_str = "HIGH"
    elif "480" in links:
        url = links["480"]
        find_str = "MEDIUM"
    elif "360" in links["360"]:
        url = links["360"]
        find_str = "LOW"
    else:
        return 'Не найдено ссылок для загрузки'

    video_name = f'id{url.split("/")[-3]}'
    res = get(url=url, headers=headers).text.splitlines()
    chunk_path = []
    print('\nЗагрузка фрагментов')
    for item in res:
        print(f'\r - Загружаю: {item}', end='')
        if item.startswith(find_str):
            chunk = get(url=f'{url}{item}', headers=headers)
            with open(os.path.join(dir_vid, item), 'wb') as ch:
                chunk_path.append(os.path.join(dir_vid, item))
                ch.write(chunk.content)

    print('\nКонвертация')
    with open(os.path.join(dir_vid, f'{video_name}.ts'), 'wb') as merged:
        for cnk in chunk_path:
            with open(cnk, 'rb') as mergefile:
                shutil.copyfileobj(mergefile, merged)
    print(f'{os.path.join(dir_vid, video_name)}.ts')
    os.system(f"ffmpeg -i {os.path.join(dir_vid, video_name)}.ts {os.path.join(dir_vid, video_name)}.mp4")
    for it_ch in chunk_path:
        os.remove(it_ch)
    os.remove(f'{os.path.join(dir_vid, video_name)}.ts')

    print('\nЗагрузка и конвертирование завершено.')
    print(f'Видео сохранено в папку: {dir_vid}')


def main():
    url = input('Введите ссылку на страницу видео: ')
    if 'https://m.vk.com/video' not in url:
        print('Введите корректную ссылку')
        return
    title, m3u8 = get_m3u8(url)
    links = parse_m3u8(m3u8)
    download_chunk(title, links)


if __name__ == "__main__":
    main()

Извиняюсь за код. Некогда было "облагораживать". Данный код будет загружать видео, для которых справедлива вышеприведенная ссылка, то есть, название в плейлистах с фрагментами соответствует вышеприведенному шаблону. Но, если данному коду попадется плейлист со ссылками на фрагменты подобного рода:

введите сюда описание изображения

то, он работать не будет. Здесь ссылка будет иметь вид:

https://pvv4.vkuservideo.net/c509108/video/hls/1/e4eMj86OT0xOzIy/videos/488fd84252/index-f4-v1-a1.m3u8

То есть, для начала, ее надо еще получить из данного плейлиста, убрать то, что за вопросом, вместе с ним. Ну и то, что прилетит в плейлисте с фрагментами будет выглядеть так:

введите сюда описание изображения

Теперь, после того, как вы скачали плейлист с названиями фрагментов, вам нужно составить ссылку на фрагменты. А она будет выглядеть следующим образом:

https://pvv4.vkuservideo.net/c509108/video/hls/1/e4eMj86OT0xOzIy/videos/488fd84252/seg-3-f4-v1-a1.ts

Из ссылки на плейлист с фрагментами нужно убрать название плейлиста, и подставить название фрагмента.

Таким образом, что мы получаем. Если вы захотите скачивать почти любое видео из ВК, вам нужно подумать, как вышеприведенный скрипт, сделать более универсальным. Ну или написать на его основе свой. А почему почти любое видео? Потому, что пока, я ссылок или способов на скачивание видео из ВК, которое добавлено туда в виде стрима, не нашел.

Надеюсь, вам поможет это объяснение.

P.S.: Забыл добавить, что после того, как вы загрузите фрагменты, вам их нужно объединить в один файл, что выполняется вот в этой части кода:

with open(os.path.join(dir_vid, f'{video_name}.ts'), 'wb') as merged:
        for cnk in chunk_path:
            with open(cnk, 'rb') as mergefile:
                shutil.copyfileobj(mergefile, merged)

Затем, то, что вы объедините конвертировать в mp4:

    os.system(f"ffmpeg -i {os.path.join(dir_vid, video_name)}.ts {os.path.join(dir_vid, video_name)}.mp4")

А затем удалить загруженные фрагменты и объединенный файл. Конвертация происходит посредством утилиты ffmpeg. Таким образом, у вас она должна быть установлена в ОС. Данный код, будет работать в Linux, но для Windows вам нужно поискать, как запускать утилиту из командной строки.

Вот скриншот с загруженным видео, которое у вас в вопросе, с помощью вышеприведенного скрипта:

введите сюда описание изображения

→ Ссылка