Парсинг fetch/xhr запросов пайтон

Всем привет, при парсинге сайта https://akniga.org/ впервые столкнулся с проблемой отсутствия ссылки на медиаконтент. введите сюда описание изображения

Беглый гугл подсказал смотреть на код страницы, обнаружил что данные подгружаются с сервера в запросах вкладки сеть. необходимо получить ссылку этого файла (pl.m3u8) с его помощью можно загрузить всю аудиодорожку и сохранить её с помощью ffmpeg на пк.

Буду благодарен любой помощи!


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

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

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

Уверен, что есть какие-то библиотеки, которые автоматизируют этот процесс, я же покажу просто "на пальцах", как это работает, так как сам особо с этим не работал, но только что для себя проанализировал.

На сайте вы можете найти HLS (M3U8) файл, который содержит в себе метаданные об имеющихся вложенных потоках. Чтобы скачать его, вам достаточно перейти по ссылке в браузере, ее можно увидеть прямо в консоли разработчика:

https://h7.akniga.club/b/86477/pl.m3u8?res=your_key&expires=1704007036

На выходе вы получите файл с вложенными сегментами. Вот примерный вид этого файла:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:30
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://h11.akniga.club/datakeys/enc.key",IV=your IV
#EXTINF:30.016000,
seq0.ts
#EXTINF:29.994667,
seq1.ts
#EXTINF:29.994667,
seq2.ts
#EXTINF:29.994667,
seq3.ts
#EXTINF:30.016000,
seq4.ts
#EXTINF:29.994667,
seq5.ts
...

По сути говоря поток в данном случае зашифрован методом AES-128.

Ключ вы получаете по ссылке https://h11.akniga.club/datakeys/enc.key а вектор инициализации (IV) уже явно прописан в файле. Явно прописывать в файле ничего не нужно, так как ffmpeg сам получит этот ключ и будет декодировать сегменты, но путь к нему можно прописать по надобности и вручную.

Затем вам нужно скачать файлы с расширением *.ts. Исходя из файла .M3U8 в моем примере - их было 94, у вас может быть другое значение. Но так как вы говорите, что ffmpeg сам выгружает, тогда этот этап можно пропустить.

Тем не менее простенький пример, как быстро выгрузить эти файлы:

import requests


count_of_ts = 94

for count in range(0, count_of_ts + 1):
    print(f'Downloading segment {count}')
    response = requests.get(f'https://h7.akniga.club/b/86477/seq{count}.ts')
    with open(f'seg{count}.ts', 'wb') as file:
        file.write(response.content)

Вы можете автоматизировать этот процесс и улучшить код, так как это не более чем простой наглядный пример, чтобы показать, как быстро скачать все файлы вручную - моя задача донести вам всего лишь концепт работы. Уверен, что есть лучший способ, или может даже ffmpeg сам скачивает - это стоит вам исследовать.

После этого используйте ffmpeg чтобы собрать медиапоток в медиафайл.

Я использовал Windows, и я не часто работал с ffmpeg, поэтому в mp3 не удалось конвертировать, но в mp4 у меня сегменты были собраны в медиафайл. Если не ошибаюсь, то должны быть какие-то дополнительные драйвера, чтобы можно было перекодировать в mp3. В любом случае mp4 можно конвертировать в mp3 через какой-то другой сервис.

Так как я просто показываю пример, как быстро собрать этот медиафайл, то использовал вот такую команду:

ffmpeg -protocol_whitelist "file,http,https,tcp,tls,crypto" -allowed_extensions ALL -i book.m3u8 -acodec copy output_test.mp4

Вам же рекомендую использовать более тонкую настройку ffmpeg и разобраться что означает каждый флаг для него.

Обновление:

Я не писал в комментариях, что использование seleniumwire - это хорошее решение. Лучше того, я написал что лучшее решение, так это сделать на обычных реквестах, нужно только разобраться, как генерируется res для XHR-запроса, то есть исследовать вам js-код, но все зависит от ваших предпочтений.

Тем не менее, вариант с seleniumwire рабочий. Как и написал, вам нужно установить небольшую задержку, потому что ответ от XHR приходит не сразу. Чтобы снизить нагрузку на процессор, то можно открыть selenium в headless режиме - это, по сути говоря, скроет графический интерфейс браузера. Вот самый простой пример на первой попавшейся книге:

from selenium.webdriver.chrome.options import Options
from seleniumwire import webdriver

options = Options()
options.add_argument('--headless')

driver = webdriver.Chrome(options=options)
driver.get('https://akniga.org/lansdeyl-dzho-r-dzhentlmenskiy-otel')
driver.implicitly_wait(3)

find_url = None
for request in driver.requests:
    if '.m3u8' in request.url:
        find_url = request.url
        print(find_url)
        break

driver.quit()
# дальнейшая работа с ссылкой

Вариант не идеален, так как установлено неявное ожидание для selenium с вероятностным, а не точным поиском ссылки, тем не менее для демонстрационных целей код работает, вам нужно только улучшить его.

→ Ссылка