美文网首页爬虫python学习网络爬虫
工具向——喜马拉雅音频下载

工具向——喜马拉雅音频下载

作者: treelake | 来源:发表于2017-04-30 23:02 被阅读1633次

    电子屏幕看得眼睛疼,便想找有声书听听,在喜马拉雅FM上搜到了想要的资源,可是无法直接下载,只能在线听。
    略搜了一下没有找到方便的下载工具,在Tempermonkey上倒是找到了一个简单的js脚本在页面上显示下载链接,不过还要一个个点击并重命名,太麻烦,对普通用户也不友好。
    于是用python实现了个简单的多线程下载工具,批量下载页面上的音频,并使用pyinstaller生成windows下的单个可执行文件,大小也可以接受,18M,这样普通windows用户不需要python环境直接双击执行即可,比较方便。
    注:是免费音频批量下载,非破解收费资源。原始版本见github

    普通用户使用

    链接: https://pan.baidu.com/s/1c21mJKG 密码: 87ur

    • 下载后双击打开,按提示输入音频存放的地址,如放在F盘的maoxuan文件夹中,回车确定。
    • 然后输入要下载的音频页面,如毛泽东选集第一页,直接复制地址栏,粘贴并回车。


    • 便跳转到下载状态,等待下载完成即可:


      Paste_Image.png

    python代码

    • 命名为ximalaya3.py
    import requests
    from bs4 import BeautifulSoup
    from multiprocessing.dummy import Pool, Lock, freeze_support
    import os
    import sys
    
    
    # http://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response
    def input_page_url_with_change_dir():
        '''
        转移到要存储的文件夹位置并获取专辑页面地址
        '''
        print('请输入存储文件夹(回车确认):')
        while True:
            dir_ = input()
            if os.path.exists(dir_):
                os.chdir(dir_)
                break
            else:
                try:
                    os.mkdir(dir_)
                    os.chdir(dir_)
                    break
                except Exception as e:
                    print('请输入有效的文件夹地址:')
    
        print('请输入想下载FM页面的网址(回车确认) -\n'
              '如 http://www.ximalaya.com/20251158/album/2758791:')
        page_url = input()
        return page_url
    
    
    page_url = input_page_url_with_change_dir()
    
    headers = {
        'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                       'Chrome/57.0.2987.133')
    }
    
    
    def get_json_urls_from_page_url(page_url):
        '''
        获取该专辑页面上所有音频的json链接
        '''
        res = requests.get(page_url, headers=headers)
        soup = BeautifulSoup(res.content, "html5lib")
        mp3_ids = soup.select_one('.personal_body').attrs['sound_ids']
        json_url = 'http://www.ximalaya.com/tracks/{id}.json'
        urls = [json_url.format(id=i) for i in mp3_ids.split(',')]
        return urls
    
    
    mp3_json_urls = get_json_urls_from_page_url(page_url)
    n_tasks = len(mp3_json_urls)
    lock = Lock()
    shared_dict = {}
    # 对字典的单步写操作为原子操作,不用考虑竞态,见下参考
    # https://docs.python.org/3.6/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
    # http://stackoverflow.com/questions/6953351/thread-safety-in-pythons-dictionary
    # d = {1:1}; import dis; dis.dis('d[1] = "x"')
    # http://document.ihg.uni-duisburg.de/Documentation/Python/lib/node56.html
    
    
    def get_mp3_from_json_url(json_url):
        '''
        访问json链接获取音频名称与下载地址并开始下载
        '''
        mp3_info = requests.get(json_url, headers=headers).json()
        title = mp3_info['album_title'] + '+ ' + mp3_info['title'] + '.m4a'
        path = mp3_info['play_path']
        title = title.replace('|', '-')  # 避免特殊字符文件名异常
    
        if os.path.exists(title):
            return 'Already exists!'
    
        # http://stackoverflow.com/questions/13137817/how-to-download-image-using-requests
        while True:
            try:
                with open(title, 'wb') as f:
                    response = requests.get(path, headers=headers, stream=True)
    
                    if not response.ok:
                        # shared_dict.pop(title)
                        print('response error with', title)
                        continue
    
                    total_length = int(response.headers.get('content-length'))
    
                    chunk_size = 4096
                    dl = 0
                    shared_dict[title] = 0
    
                    for block in response.iter_content(chunk_size):
                        dl += len(block)
                        f.write(block)
                        done = int(50 * dl / total_length)
                        shared_dict[title] = done
    
                    global n_tasks
                    with lock:
                        n_tasks -= 1
                    shared_dict.pop(title)
                    return 'Done!'
    
            except Exception as e:
                print('other error with', title)
                os.remove(title)
    
    
    # http://stackoverflow.com/questions/28057445/python-threading-multiline-progress-report
    # http://stackoverflow.com/questions/15644964/python-progress-bar-and-downloads
    def report_status():
        '''
        根据共享字典汇报下载进度
        '''
        import time
        n = len(mp3_json_urls)
    
        print(u'准备下载...')
        while len(shared_dict) == 0:
            time.sleep(0.2)
    
        while len(shared_dict) != 0:
            line = ''  # "\r"
            for title, done in shared_dict.items():
                line += "%s\n - [%s%s]\n" % (
                    title, '=' * done, ' ' * (50 - done)
                )
            line += '\n**** 剩余/总任务 = %s/%s ****' % (n_tasks, n)
            os.system('cls')
            sys.stdout.write(line)
            sys.stdout.flush()
            time.sleep(1)
    
    
    # if __name__ == '__main__':
    # 多线程下载并报告状态
    freeze_support()
    with Pool(6) as pool:
        # http://stackoverflow.com/questions/35908987/python-multiprocessing-map-vs-map-async
        r = pool.map_async(get_mp3_from_json_url, mp3_json_urls)
        report_status()
        r.wait()
        os.system('cls')
        print('下载完成!')
    
    
    # http://stackoverflow.com/questions/28349359/pyinstaller-single-file-executable-doesnt-run
    # pyinstaller ximalaya3.py --onefile --noupx
    # https://greasyfork.org/zh-CN/scripts/24662-%E5%96%9C%E9%A9%AC%E6%8B%89%E9%9B%85%E9%9F%B3%E4%B9%90%E4%B8%8B%E8%BD%BD/code
    
    • pip install pyinstaller
    • pyinstaller ximalaya3.py --onefile --noupx即可生成可执行文件(在生成的dist文件夹中)

    相关文章

      网友评论

      • Shadowsocks2:您好,这个代码最后更新是8个月前,现在12月了,还能正确运行吗?

        这么好的文章都没人打赏支持,真是的~~~那个打赏的微信用户是我
      • Fi_Sh_Yu:大佬...打开提示与windows 系统版本不兼容...需要32 or 64位的version....啥意思列.
      • kwingart:挺好的,只是想问问po主,这个只能下载100个,可以突破这个限制么?
      • AshinYan:鼓励一下😀
      • Adam_5f0a:使用的python3吗?
        Adam_5f0a:@treelake 好的,谢谢
        treelake:@Adam_5f0a 是的
      • oracle1226:有个地方不太明白
        http://www.ximalaya.com/tracks/{id}.json'
        这个地址除了'{id}.json'
        前面的部分是从哪里得到的?
        oracle1226: @treelake 好的。谢谢。我试试
        treelake:点击某个音频播放,在控制台里看网络请求
      • 唐僧娶媳妇:我的说是木马是什么原因
        treelake:没有恶意程序,可能是误报吧。我这没有报木马
      • casaba:厉害了我的哥
        忠洋哥:太厉害了,即学到编程,有下载到音频
        treelake:빨리 여자 친구를 찾아
      • 我叫钱小钱:看上去就很厉害的样子
        treelake:谢谢鼓励 :blush:
      • dc4f5f51b796:关键是收费音频
      • 唐僧娶媳妇:没学过编程,看不太懂。这个软件可以直接用吗?以前下载过这个软件,但是下载的东西压根不知道存放到哪儿
        treelake: @唐僧娶媳妇 不客气😃
        唐僧娶媳妇: @treelake 嗯,谢谢啦。我试一下
        treelake: @唐僧娶媳妇 这个直接使用就行,放哪里也无所谓的。

      本文标题:工具向——喜马拉雅音频下载

      本文链接:https://www.haomeiwen.com/subject/hsaxtxtx.html