python selenium 下载 某听书网一部书籍

最近听书,喜马拉雅等收费,终于在某个网找到一部免费的,可以在线听。此网站没有app,手机提供网页方式。本来打算将就听了,结果发现手机网页太多黄色广告,加上最近在研究scrapy,就尝试爬一下。

按步骤说一下方法和遇到的困难及解决方案。

0.由于此网站播放页面,采用audio控件播放,每个链接是动态链接,经过多次研究好像是调用了一个加密了的js代码,动态生成客户端cookie。当访问服务器时,服务器检测此cookie,返回动态的音频链接。点击控件播放时候,根据cookie检测链接是否有效,如果无效返回一个几秒的音频提示错误。如果短时间手动刷新或者其他方式都无法获得有效链接。

1.scrapy失败

开始用scrapy爬网页后,调用urllib去下载。后来多次失败,发现了cookie。以为是scrapy的urllib没有带cookie导致的,就使用scrapy的filedownloadmiddleware去下载,还是失败。

后来又发现cookie不是服务器返回的。研究很久发现,cookie是客户端js生成的,scrapy无法调用,加上sojson.v5我无法破解,故放弃转selenium。

 

 

 

2.发现selenium可以调用chrome实现。遂开始:

1.初始化浏览器,设置默认保存文件夹

def init_chrome():
    # 自定义下载配置
    chromeOptions = webdriver.ChromeOptions()
    chromeOptions.add_argument(r"user-data-dir=C:\Users\Admin\AppData\Local\Google\Chrome\User Data")
    prefs = {"download.default_directory": download_path,
             'download.prompt_for_download': False,  # 是否弹窗询问
             "download.directory_upgrade": False,  # 还不清楚这个配置是干嘛的
             'profile.default_content_setting_values': {
                 'images': 2,
             },  # 不加载图片
             "safebrowsing.enabled": False  # 是否提示安全警告
             }

    chromeOptions.add_experimental_option("prefs", prefs)

    browser = webdriver.Chrome(r"D:\TOOLS\chromedriver_win32\chromedriver.exe", chrome_options=chromeOptions)
    return browser

2.主循环,先进来目录(因为点每集的链接都是js生成了cookie再去服务器访问的,所以不能直接进)。这里pages是我下载剩下页面的数据(尽管尽量模拟正常人的操作,结果还是会有失败的,后面会说)

    browser.get("http://www.audio****.com/book/1040.html")  # 进来目录
    print("sleep 10")
    time.sleep(10)
    pages = [17, 18, 19, 22, 23, 26, 30, 34, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92]
    retry = 0
    for page in pages:
        while retry < 5:
            goto_sub_page(browser, page)          # 进来每集
            res = get_audio(browser, page)        # 下载音频 
            return_listpage_from_subpage(browser) # 返回目录 
            if res:
                retry = 0
                break
            else:
                retry += 1
                print('refresh and try again:' + str(retry))
        retry = 0

    browser.close()

3.进来每集页面,也必须默认点击链接进入,否则cookie可能不对

def goto_sub_page(browser, page):
    sub_page = browser.find_element_by_xpath('//*[@id="wrapper"]/div/div/div/div[4]/div/ul/li[' + str(page) + ']/a')
    sub_page.click()
    print("goto sub page:" + str(page) + " and sleep 10")
    time.sleep(10)

4.下载音频

这里有几个坑要处理,

4.1不能直接使用audio的source链接去下载,因为没有客户端js代码生成的cookie,即使拿到链接下载也是无效文件

4.2为此必须使用audio去下载,研究很久发现audio无法直接下载,只有右键弹出菜单后选择另外文件,然后操作chrome存盘。

另外chrome即使设置了默认保存位置和下载不提示,还是会弹出保存文件的对话框。因为是链接不是同源的(url不是同一个服务器)。

后来考虑使用生成一个超链接,模拟click,结果发现也不行(a标签加了download属性就支持下载,但是还是要同源才行)

4.3 selenium可以控制audio弹出右键菜单,但是不支持发生按键,只有使用win32api模拟按键,按顺序发V另存,发送Enter控制chrome存盘。

 

def get_audio(browser, page):
    try:
        # if not os.path.exists("audio"):
        #    os.mkdir("audio")

        if check_page_file_exist(page):                # 如果文件已存在就跳过
            print("file exist ,goto next page")
            return True

        audio_control = browser.find_element_by_tag_name("audio")   # 找到audio控件
        audio_control.click()
        src_ontrol = browser.find_element_by_tag_name("source")
        file_url = src_ontrol.get_attribute("src")
        file_name = str(file_url).split('/')[-1]
        file_name = file_name.replace('.m4a', '.mp3')
        print("\nfile_name:")
        print(file_name)

        browser.execute_script("return arguments[0].play();", audio_control)  # 模拟点击播放
        print("after play sleep 15")
        time.sleep(15)
        webdriver.ActionChains(browser).context_click(audio_control).perform()  # 弹出audio右键菜单
        print("after right menu sleep 5")
        time.sleep(5)
        win32api.keybd_event(86, 0, 0)                                         # 发生按键V和回车,让chrome存盘
        win32api.keybd_event(86, 0, win32con.KEYEVENTF_KEYUP, 0)
        print("after send key V sleep 10")
        time.sleep(10)
        win32api.keybd_event(13, 0, 0)
        win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0)
        print("after send key Enter sleep 10")
        print('wait 5min before next step')
        time.sleep(5 * 60)

        file_name = download_path + file_name
        temp_file_name = file_name + ".crdownload"                               # 检查chrome下载是否结束
        new_file_name = download_path + str(page) + ".mp3"
        while os.path.exists(temp_file_name):
            print("wait for download finish")
            time.sleep(10)

        if os.path.exists(file_name):                                          # 根据文件大小判断下载是否是有效文件
            size = os.path.getsize(file_name)
            if size < 15192:
                return False
            else:
                os.rename(file_name, new_file_name)
                print('wait 14min before next page')
                time.sleep(14 * 60)
                print('go to next page')
                return True
        else:
            return False
    except Exception as e:
        print(e)
        return False

5.另外一个坑,由于我在服务器上用远程桌面测试的,结果远程桌面一断,发送按键的功能就失效。后来编一个批处理,使用批处理来端口远程桌面才行。(三条语句是一个意思,只是防止有多个远程桌面的会话,一起处理了)

参考链接 解决Windows远程桌面断开后模拟按键失效,windows断开远程连接之后不渲染

@%windir%\System32\tscon.exe 0 /dest:console
@%windir%\System32\tscon.exe 1 /dest:console
@%windir%\System32\tscon.exe 2 /dest:console

6.即使采用上述手段,每集下载也尽量延时,还是有些页面下载报错音频(94集有20集),真是无语了,不知道这个网站的用户咋个正常使用下去啊。

上述解决方法参考了很多文章,由于当时忙解决问题,没有记录参考链接,实在抱歉,在此谢谢这些作者。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值