title logo

Предисловие

Начну данную заметку издалека.

Некоторые люди программисты в этой стране имеют желание изучить английский язык. И если со чтением документации проблем не возникает, поскольку зачастую профессия обязывает понимать английский текст, то с аудированием/слушанием всё более печально. Причем всё еще более печально в сферах которые не относятся к разработке ПО/не являются смежными.То есть понимать простую разговорную речь - подчас весьма непросто.

Для желающих погрузиться в витиеватый и не менее могучий английский язык, есть различные аудиофайлы для адаптации восприятия к английской речи. Например LearnEnglish podcasts, который предоставляет British Council.
Но данные варианты, как правило, имеют серьезный недостаток - их нужно скачивать руками каждый раз по мере появления/обновления.

В данной заметке, будет описан механизм ежедневного получения “свежих” выпусков подкастов.

Заметка состоит из трёх частей - краткое описание конфигурационного файла, собственно кода на Python и описания дополнительного посредника, который вы можете заменить на свой. Назначение этого посредника - доставить файлы к вам на компьютер и сложить в папочку, где их можно будет забирать когда они вам понадобятся. Для этой цели можно использовать Яндекс.Диск или Dropbox.

Требования к реализации

Логично что для ежедневного обновления - потребуется ежедневная проверка на наличие новых выпусков интересующих подкастов.Обычно для повторяющихся задач используют что-то вида cron

Многие подкасты имеют RSS-ленту(для тех кто не курсе - информация о том что такое RSS-легко гуглится), которая и будет анализироваться для получения свежих выпусков. Также потребуется установить пакеты feedparser и requests для Python(версия 3.5+) и какой-либо кастомный посредник которого вы предпочитаете. Для работы с посредником может хорошо подойти пакет webdav.

Часть 1.Конфигурационный файл podcasts.json

Сам по себе конфигурационный файл имеет достаточно простую структуру и хранится в JSON-файле который расположен рядом с запускаемым скриптом.Приведу пример того файла который используется у меня сейчас.

podcasts.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[
{
"dir": "HowStuffWorks",
"urls": [
"http://www.howstuffworks.com/podcasts/stuff-you-missed-in-history-class.rss",
"http://www.howstuffworks.com/podcasts/stuff-to-blow-your-mind.rss"
]
},
{
"dir": "BBC",
"urls": [
"http://podcasts.files.bbci.co.uk/b00srz5b.rss",
"http://podcasts.files.bbci.co.uk/b00lvdrj.rss"
]
},
{
"dir": "NPR",
"urls": [
"https://www.npr.org/rss/podcast.php?id=510298",
"https://www.npr.org/rss/podcast.php?id=484813121",
"https://www.npr.org/rss/podcast.php?id=381444907"
]
},
{
"dir": "DifferentEnglish",
"urls": [
"http://feeds.feedburner.com/TheUpgradeByLifehacker",
"http://nerdist.libsyn.com/rss",
"http://www.startupsfortherestofus.com/feed",
"http://feeds.feedburner.com/TEDTalks_audio"
]
}
]

Данный файл обеспечивает структурирование подкастов по папкам с заданными именами. Хороший метод если вы хотите разделить отдельно русскоязычные подкасты, такие, как “Радио Т” от англоязычных, но обрабатывать их получение универсальным скриптом.

Часть 2.Скрипт получения аудиофайлов.

Скрипт тоже не отличается сложностью, и просто работает ориентируясь на дату публикации записи в RSS-ленте. Это позволяет каждый день получать только самые свежие записи(однодневной давности) и не скачивать старые выпуски.

runner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import json
import os
import re
from datetime import datetime, timedelta
from os.path import basename
import feedparser
import requests
from yadisk_client import yadisk_upload
current_dir = os.path.dirname(os.path.realpath(__file__))
#Регулярка для вычленения имени файла,
#которое будет использоваться для последующего сохраения
regex = r"([\w\d\s\S]+.mp3)"
#Место сохранения, будет постоянно перезаписываться
path_local = '/tmp/podcast.mp3'
#User-Agent нужен потому что некоторые сервисы блокируют UA urlib и python-request
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/39.0.2171.95 Safari/537.36'}
def upload_new_pod_casts(url, directory):
f = feedparser.parse(url)
#В любой RSS ленте есть вложеный элемент, который предоставляет доступ к записям
entries = f['entries']
#Забираем только те которые самые новые
expected_publish_date = datetime.now() - timedelta(days=1)
start_time = expected_publish_date.timetuple()
records = filter(lambda entry:
entry.get('published_parsed', start_time) > start_time, entries)
for record in records:
for link in record['links']:
type_link = link.get('type', '')
#Забираем только аудиозаписи игнорируя видео и текст.
if 'audio' in type_link:
try:
#Выделяем название файла для лучше читаемости.
#Зачастую аудиофайлы бывают без тегов,
#так что имя файла - единственное на что мы може расчитывать
location = link.get('href')
matches = re.findall(regex, basename(location))
file_name = matches[-1]
#Пробуем загрузить файл по ссылке которую выдрали из RSS
pod_cast_file = requests.get(location, headers=headers, stream=True)
#Сохраняем в файл-времянку, перезаписывается на каждой итерации
with open(path_local, 'wb') as f:
for chunk in pod_cast_file:
f.write(chunk)
f.close()
path_for_disk = 'podcasts/{1}/{0}/{2}'.format(
directory, datetime.now().strftime("%d-%m-%y"), file_name)
yadisk_upload(path_local, path_for_disk)
except:
pass
#Обрабатываем файл с конифгурацией
with open('{}/podcasts.json'.format(current_dir)) as data_file:
podcasts = json.load(data_file)
for podcast in podcasts:
directory = podcast['dir']
for url in podcast['urls']:
upload_new_pod_casts(url, directory)

Момент с настройкой ежедневного выполнения данного скрипта - пропущен намерено.
Автор полагает, что данное действие(настройка работы по крону/расписанию)- процедура весьма тривиальная и не вызовет сложностей у читателя

Часть 3. Посредник (Я.Диск/Dropbox)

Данная часть является опциональной, поскольку если вы запускаете предыдущий скрипт локально - возможно вам и не потребуется “заливать” файлы в облачное хранилище. В моем случае я использую этот скрипт на удалённом хосте, и на каждой итерации заливаю скачанный файл на Яндекс.Диск, чтоб потом автоматически синхронизировать всё содержимое и каждое утро получать упорядоченную структуру папок с аудиофайлами на конкретный день.

Если вы не хотите использовать никакое промежуточное звено/облачное хранилище - просто удалите импорт и вызов функции yadisk_upload. Для других - привожу пример конфига обмена с yadisk посредством webdav

yadisk_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import webdav.client as wc
options = {
'webdav_hostname': "https://webdav.yandex.ru",
'webdav_login': 'your_login',
'webdav_password': 'your_pass'
}
client = wc.Client(options)
def yadisk_upload(podcast, remote_path):
client = wc.Client(options)
try:
client.upload_sync(local_path=podcast, remote_path=remote_path)
except wc.RemoteParentNotFound as e:
if 'parent' in str(e):
#We create structure!
path_dirs = remote_path.split('/')[:-1]
levels = []
for directory in path_dirs:
levels.append(directory)
try:
client.mkdir('/'.join(levels))
except:
pass
#Try upload again
client.upload_sync(local_path=podcast, remote_path=remote_path)

В качестве альтернативы Я.Диску можно использовать хранилище на DropBox. Обмен с данным сервисом происходит через вызов функции dbx_upload

dropbox_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import dropbox
from dropbox.client import DropboxClient
token = 'your_token'
dbx = dropbox.Dropbox(token)
dbx_client = DropboxClient(token)
def dbx_upload(path_local, remote_path):
podcast = open(path_local, 'rb')
size = os.path.getsize(path_local)
uploader = dbx_client.get_chunked_uploader(podcast, size)
while uploader.offset < size:
try:
uploader.upload_chunked()
uploader.finish(remote_path)
except:
break

Итого.

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

Это хорошо заметно на следующем BASH-листинге

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
user@pc:~/CloudStore/YaDisk/podcasts$ ls -R$
.:
25-08-17 26-08-17
./25-08-17:
BBC DifferentEnglish HowStuffWorks
./25-08-17/BBC:
p05c0ww2.mp3
./25-08-17/DifferentEnglish:
CarolynBertozzi_2016X.mp3
RobinHanson_2017.mp3
Nerdist_896_Michael_Cudlitz.mp3
./25-08-17/HowStuffWorks:
2017-08-23-stbym-cliodynamics-001.mp3
2017-08-24-sysk-personality-tests-final.mp3

Исходники можно взять на GitHub, обернуть это дело в cron и получать новые подкасты когда угодно. Приятного вам прослушивания и совершенствования восприятия английской речи.