最近听书,喜马拉雅等收费,终于在某个网找到一部免费的,可以在线听。此网站没有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集),真是无语了,不知道这个网站的用户咋个正常使用下去啊。
上述解决方法参考了很多文章,由于当时忙解决问题,没有记录参考链接,实在抱歉,在此谢谢这些作者。