?

Log in

No account? Create an account

Качая подкасты - АКАПУЛЬКОПСИС NOW!

Dec. 11th, 2017

02:10 am - Качая подкасты

Previous Entry Share Next Entry

Однажды я делился своим опытом велосипедостроения, когда накодил на питоне качалку аудиофайлов из rss потоков. Потом имел неосторожность на работе обмолвиться о своем поделии, и коллега спросил меня: "а что, wget нельзя было?". Я ответил уклончиво, стесняясь признаться, что слышу про эту утилиту... не совсем впервые, конечно, но почти. Пообещал сам себе, что в следующий раз ради расширения кругозора попробую обойтись одним башем и другими подручными средствами. Хотя питон тоже почти всегда в системе из коробки есть, но вот тем не менее. Сейчас мне опять приспичило много чего накачать и вспомнил о том обете. Bash, curl, grep, awk и wget мне были в помощь, благо ставить их не надо, они есть.

Мне захотелось иметь все подкасты Sprachbar с сайта dw.com. На рутрекере есть раздача, я оттуда про этот подкаст и узнал, однако там сейчас не совсем полный набор. Существуют готовые решения для скачивания добра с сайтов, тот же wget имеет ключи для рекурсивной загрузки по ссылкам. Но мне не нужно было вообще все, а только набор mp3 и pdf файлов, причем красиво разложенных и поименованных. Поэтому накропал скрипт, щас покажу какой. Мотивацией было не желание похвастать, какой я прекрасный код написал, а скорее наоборот. Уверен, что его можно переписать в два раза короче и в десять раз эффективнее. Но он был написан с нуля в том смысле, что одним глазом я смотрел в редактор, а вторым в документацию, где для меня почти все было в новинку. Буквально каждая строчка рождалась в режиме "ага, вот так значит тоже можно". Результат меня вдохновил и порадовал: все заработало и скачалось. Приятно, когда компьютер делает то, о чем его просишь. Всегда бы так.



Сначала сам скрипт, а потом будет история его создания с позитивной моралью в конце

 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
#!/bin/sh
# Качалка архива подкаста Sprachbar с сайта http://www.dw.com 

dwstart="http://www.dw.com/de/deutsch-lernen/sprachbar/s-9011"
archiv_list=`curl $dwstart | grep '<a href="/de/sprachbar-archiv' | awk -F"\"" '{print " http://www.dw.com"$2}'` 

#ссылка на страницу с архивом: (например http://www.dw.com/de/sprachbar-archiv-a/a-2216456)
for archiv_url in $archiv_list
do
  #имя каталога для архивов по буквам: /sprachbar-archiv-a, /sprachbar-archiv-b и т.д.
  dirname=`echo "$archiv_url" | awk -F"/" '{print $5}'`
  
  if [ ! -d $dirname ]; then
    mkdir $dirname
  fi
  
  cd $dirname
    podlist=`curl -s $archiv_url | grep '<a href="/de/[^/]*/a-[0-9]*">' | awk -F"\"" '{print " http://www.dw.com"$2}' `
    if [ ! -e "podlist.txt" ]; then
      echo "$podlist" >> "podlist.txt"
    fi
    for podcast in $podlist
    do
      if echo "$podcast" | grep -i "sprachbar-archiv" >/dev/null
      then
        echo "skip " "$podcast"
      else  
        podname=`echo "$podcast" | awk -F"/" '{print $5}'`
        echo "process " "$podname" "( " "$podcast" " )"

        pdfname="$podname"".pdf"
        mp3name="$podname"".mp3"
        
        # выкачиваем pfd файл из http://www.dw.com/downloads/28067485/ab-die-post.pdf
        pdflinks=`curl -s $podcast | grep 'pdf' | awk -F"\"" '{ print "http://www.dw.com"$2}'`
        for pdfurl in $pdflinks
        do
          if [ ! -e $pdfname ]; then
            echo "saving file from" "$pdfurl" " into " "$pdfname"
            wget -O "$pdfname" "$pdfurl"
            sleep 5
          else
            echo "file " "$pdfname" " already exist"
          fi
        done
        
        # выкачиваем mp3 файл из http://www.dw.com/overlay/media... + http://radio-download.dw...dwdownload.mp3
        medialinks=`curl $podcast | grep '/overlay/media' | awk -F"\"" '{print "http://www.dw.com"$2}'`
        for mediaurl in $medialinks
        do
          mp3links=`curl -s $mediaurl | grep '^<a href=\"[^"]*mp3' | awk -F "\"" '{print $2}'`
          for mp3 in $mp3links
          do
            if [ ! -e $mp3name ]; then
              echo "saving file from" "$mp3" "into" "$mp3name"
              wget -O "$mp3name" "$mp3"
              sleep 5
            else  
              echo "file " "$mp3" " already loaded"
            fi 
          done
        done
      fi   
    done 
  cd ".."
  
done


На сайте dw.com подкаст sprachbar расположен вот так примерно:



C заглавной страницы Sprachbar, как впрочем и с любой другой этого подкаста, есть ссылки на архивы аудиозаписей.



На странице архива ссылки на подкасты



На странице подкаста есть прямая ссылка на файл с транскриптом.



Иногда есть и прямая ссылка на mp3, но чаще ссылка на попап



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

То есть файловая иерархия выглядит так

http://www.dw.com/de/deutsch-lernen/sprachbar/s-9011
 http://www.dw.com/de/sprachbar-archiv-a/a-2216456                   Архив A
  http://www.dw.com/de/ab-die-post/a-16454927                         Подкаст №1 
   http://www.dw.com/downloads/28067485/ab-die-post.pdf                Ссылка на pdf файл 
    http://www.dw.com/overlay/media/de/ab-die-post/16431472/16454927   Всплывающее окно с медиа содержимым
     http://radio-download.dw.com/Events/ ... /65CF2EDA_1_dwdownload.mp3 
  http://www.dw.com/de/abk%C3%BCrzungen/a-2987918                     Подкаст №2 
  ...
 http://www.dw.com/de/sprachbar-archiv-b/a-2216464                   Архив B

Открыл терминал и начал эксперименты в консоли. Сначала получил содержимое самой первой страницы. Хотя вместо s-9011 тут можно подставить почти любую, потому что ссылки на каталог архивов есть на каждой странице подкаста.

curl http://www.dw.com/de/deutsch-lernen/sprachbar/s-9011

В выводе увидел такие строчки

<a href="/de/sprachbar-archiv-a/a-2216456">
<a href="/de/sprachbar-archiv-b/a-2216464"></pre>
...

Это ссылки на нужные архивы, выцепил их с помощью утилиты grep, которая ищет в тексте строки по заданному образцу

curl http://www.dw.com/de/deutsch-lernen/sprachbar/s-9011 | grep '<a href="/de/sprachbar-archiv'

<a href="/de/sprachbar-archiv-a/a-2216456">
<a href="/de/sprachbar-archiv-b/a-2216464">
<a href="/de/sprachbar-archiv-cd/a-2216467">
...
<a href="/de/sprachbar-archiv-xyz/a-2216821">

Если вместо html тега до первой кавычки подставить http://www.dw.com, убрать обе кавычки и закрывающую скобку, то получится список ссылок на страницы с архивами. Утилита awk поможет в этом

curl http://www.dw.com/de/deutsch-lernen/sprachbar/s-9011 | grep '<a href="/de/sprachbar-archiv' | awk -F"\"" '{print "http://www.dw.com"$2}'

В чем смысл последней команды? Программа awk воспринимает текст как таблицу и позволяет выполнять всякие действия над полями. Первый параметр -F"\"" означает, что указываем кавычку в качестве символа разделителя столбцов. То есть каждая строка состоит из трех полей. Первое и последнее - это html теги, а между ними как раз нужный адрес. Печатаю его (print $2), прицепив сначала "http://www.dw.com"

Вот результат работы:

http://www.dw.com/de/sprachbar-archiv-a/a-2216456
http://www.dw.com/de/sprachbar-archiv-b/a-2216464
...
http://www.dw.com/de/sprachbar-archiv-xyz/a-2216821

Есть полный список корректных ссылок на архивы, по которым теперь можно идти и добираться уже до ссылок на подкасты и текст. Если сurl'ом поглядеть на страницу с первым архивом, в выводе можно найти ссылки на конкретные подкасты

<a href="/de/ab-die-post/a-16454927">
<a href="/de/abkürzungen/a-2987918">
<a href="/de/adeliges-allerlei/a-3090984">

Их опять вытаскиваю с помощью grep и awk.

curl http://www.dw.com/de/sprachbar-archiv-a/a-2216456 | grep '<a href="/de/[^/]*/a-[0-9]*">' | awk -F"\"" '{print " http://www.dw.com"$2}'

...
http://www.dw.com/de/ab-die-post/a-16454927
http://www.dw.com/de/abkürzungen/a-2987918
http://www.dw.com/de/adeliges-allerlei/a-3090984

Регулярное выражение
a href="/de/[^/]*/a-[0-9]*"
находит ссылки вида /de/ab-die-post/a-16454927. К сожалению, под этот образец подходят и ссылки на архивы /de/sprachbar-archiv-a/a_NNNN. Отфильтровать их у меня по-быстрому не получилось, поэтому потом в цикле проверяю ссылки явно на наличие подстроки sprachbar-archiv и пропускаю их.

Наконец добирался до странички с подкастом и сначала добываю прямую ссылку на pdf файл с транскриптом.

curl http://www.dw.com/de/ab-die-post/a-16454927 | grep 'pdf' | awk -F"\"" '{ print "http://www.dw.com"$2}'

http://www.dw.com/downloads/28067485/ab-die-post.pdf

Потом ссылку на всплывающе окно с медиасодержимым и оттуда ссылку на mp3

curl http://www.dw.com/de/ab-die-post/a-16454927 | grep '/overlay/media' | awk -F"\"" '{print "http://www.dw.com"$2}'

http://www.dw.com/overlay/media/de/ab-die-post/16431472/16454927

curl http://www.dw.com/overlay/media/de/ab-die-post/16431472/16454927 | grep '^<a href=\"[^"]*mp3' | awk -F "\"" '{print $2}'

http://radio-download.dw.com/Events/dwelle/dira/mp3/deutschkurse/sprachbar/65CF2EDA_1_dwdownload.mp3

Ок, есть две ссылки на файлы. Их можно выкачать утилитой wget.

wget http://www.dw.com/downloads/28067485/ab-die-post.pdf

Для аудифайла еще подменяю имя на красивое

wget -O ab-die-post.mp3 http://radio-download.dw.com/Events/dwelle/dira/mp3/deutschkurse/sprachbar/65CF2EDA_1_dwdownload.mp3

Завернул в цикл, добавил проверки, чтобы можно было запустить повторно и не тянуть заново уже скаченное. Еще, держа в памяти как меня забанили на гутенберге за автоматизацию выкачивания, добавил пятисекундную задержку, чтобы не создавать слишком плотную нагрузку. Наверное тут можно было и без этого обойтись, но в случае реальной опасности бана можно наоборот задержку сделать случайной продолжительности и добавить закачку из-под прокси, у wget есть такой режим. В строчках 36, 49 и 52 поиск ссылок на файлы сделан через итерацию по списку, а пишется только первый элемент, что выглядит ошибкой. Если честно, так оно и есть, и здесь код как говорится "попахивает", но во всех местах, из-за которых пришлось так наговнокодить нужный документ шел как раз первым, поэтому и так сойдет не стал закапываться, раз уж рабочий вариант.

Вот и все. Позитивная мораль здесь в том, что последние строчки скрипта писались гораздо быстрее первых. Пусть даже самые простые регулярки или синтаксис awk кажутся на первый взгляд птичьим языком (да и на второй взгляд тоже), но осваивается этот свист довольно просто. Гугл в помощь. Так что если вам вдруг когда захочется по-быстрому накидать ходилку по сайтам или качалку, не выходя из терминала, то делюсь восторгом неофита: curl, grep, awk, wget!



Оригинал записи на dreamwidth.org.

Comments:

[User Picture]
From:alexanderr
Date:December 11th, 2017 12:22 am (UTC)
(Link)
вот именно этим меня всегда удивляют программисты. нежеланием изучать, что уже есть и наработано, а все всегда с нуля. в этом есть конечно и позитивная сторона, но ее мало. с одной стороны, можно понять нежелание разбираться в чужом коде или тем более пользоваться им вслепую, не разобравшись. но, с другой стороны, если каждый раз начинать с нуля, то далеко не уйдешь. вот если бы инженеры так поступали и каждый бы придумывал свои особенные транзисторы, логику, все свое, оригинальное, то они бы никогда не достигли такой скорости прогресса, которую мы наблюдаем.
(Reply) (Thread)
[User Picture]
From:palindromer
Date:December 11th, 2017 05:46 am (UTC)
(Link)
Думаю, всякий разработчик может привести доводы и за и против велосипедизма. В обучении без него никуда, а в промышленном программировании лучше избегать, конечно.
(Reply) (Parent) (Thread)
[User Picture]
From:yuriy_bezsonov
Date:December 11th, 2017 06:13 am (UTC)
(Link)
Это ж лучший способ что-то изучить - изобрести велосипед.
А потом ещё и для этого велосипеда написать фреймворк 😀👍
(Reply) (Parent) (Thread)
[User Picture]
From:palindromer
Date:December 11th, 2017 07:04 pm (UTC)
(Link)
И потом агитировать всех: "используйте мой фреймворк, не изобретайте велосипед!"
(Reply) (Parent) (Thread)
[User Picture]
From:freedom_of_sea
Date:December 11th, 2017 04:24 pm (UTC)

реально быстрее навелосипедить

(Link)
чем понять как задействовать библиотеку
(Reply) (Parent) (Thread)
[User Picture]
From:woodenfriend
Date:December 11th, 2017 07:07 am (UTC)
(Link)
о, хорошая практика, надо держаться в тонусе.
линукс вообще приятен тем, что там куча скриптовых языков сразу под рукой, настроены и готовы хоть сейчас в бой.

я тут некоторое время назад решил, что никуда не годится не уметь вгетить в основном языке програмирывания, и полез в интернеты деградировать читать как это делается. конечно же, никто велосипедов не строит, берут готовую либу и вперёд. но даже взять готовую libcurl и скачать всё что надо за отведённые нибисами пятнадцать свободных минут в день как-то до сих пор не удалось. позор мне
(Reply) (Thread)
[User Picture]
From:palindromer
Date:December 11th, 2017 07:07 pm (UTC)
(Link)
Да, эта подручная доступность очень кстати. Можно поддаться внезапному порыву и начать сразу что-то делать, а не остыть из-за необходимости устанавливать и настраивать инструмент.
(Reply) (Parent) (Thread)
[User Picture]
From:freedom_of_sea
Date:December 11th, 2017 04:27 pm (UTC)
(Link)
1. curl и wget - примерно одно и то же. Было бы правильнее использовать что-то одно.
2. wget имеет ключ -A pdf -A mp3 что означает сохранять только эти типы файлов.
(Reply) (Thread)
[User Picture]
From:palindromer
Date:December 11th, 2017 07:10 pm (UTC)
(Link)
Буду знать. Подозреваю, что в этом коде безграничные возмножности насчет поправить и вообще переделать как надо. Но трудно мотивировать себя на рефакторинг, когда скрипт свою задачу уже выполнил.
(Reply) (Parent) (Thread)