一、简介
本文将介绍我利用 python 从传统的手动操作下载到批量自动化下载的进化史。
二、事情是这样的
前几天发现一部还不错的剧,《糟糕历史》,是的是我喜欢的记录片。
image我在天天看美剧上找到了它的下载页面,天天看美剧上是提供的 ed2k 链接进行下载。
image如果你有装迅雷那点击对应地址会提示启动迅雷进行下载,而我作为一个不太合格的老司机是没有装迅雷的。我通常喜欢使用百度网盘的离线下载功能先下载至网盘收藏起来,等有空再慢慢下载下来看。
这意味着我要点开每个链接,然后复制它的下载地址,然后再到百度网盘客户端,打开离线下载页面,填入下载地址,然后点击开始下载按钮,然后重复去打开下一个链接,重复进行这样的操作。可是它有五季之多,那我要点到手软啊。
但是作为一个有想法的程序猿,我想这难不倒我,前段时间不刚看了两行 python 代码吗,刚好派上用场。
我要利用 python 来抓取天天看美剧上的下载链接,然后控制百度网盘客户端自动完成离线下载的任务。
让我们先来看个视频了解下它是怎么工作的吧。(原始视频太长所以剪辑过,请原谅我的剪辑技术。。。)
如果你不想折腾和看代码相关的东西而正好有相关需求,那给我留言吧,我可以帮你下载好,然后百度云私密分享给你。
三、说干就干
3.1 拿到下载链接
这个 python 特别擅长,python 的爬虫教程网上一搜一大把,这就不多做介绍了,我现在也越来越相信我之前看到说互联网上半数以上的流量来自于爬虫所为。话不多说,直接上代码。
# 文件 auto_download.py
import re
import requests
from bs4 import BeautifulSoup
from bdy_control import download_link
# 填入天天美剧的下载页面地址
url = "http://www.msj1.com/archives/5238.html"
response = requests.get(url, headers={'user-agent': 'Mozilla/5.0'}, timeout=30)
soup = BeautifulSoup(response.content, "html.parser")
for link in soup.find_all(href=re.compile("^ed2k")):
print('开始下载:%s' %(link.get_text()))
download_link(link.get('href'))
print('添加离线下载任务完成!!!')
是的,就这么简单,所以我现在也越来越理解那句 人生苦短,我用 python
这里主要用了两个库,一个是 requests 用于请求网页,一个是 BeautifulSoup 用于 HTML 的解析。
主要流程是,请求网页内容后传给 BeautifulSoup 解析,然后利用正则表达式查找匹配 ed2k 的链接,之后再传给 download_link 函数进行下载操作。
download_link 是我自己封装的自动控制百度网盘客户端进行离线操作的接口,具体实现见下分解。
3.2 如何操控百度网盘客户端
这开始对我来说有点难度,没关系我知道一定可以的。因为我之前见识过 windows 上的一个类似自动化测试的软件,可以控制其他软件,google 后了解到这是利用 windows 系统 API 做到的。然后再查 python 上能不能做,果然是可以的,让我找到了一个让 python 能访问到 windows 系统 API 的库 pywin32。那有了访问 windows API 的能力我们再来简单说下如何操控百度网盘客户端的思路吧。
首先我们要先了解 windows 下的两个东西,一个是句柄,一个是消息循环。如果你做过 MFC 开发,那你对这两样东西一定不会陌生,庆幸的是我学习过 MFC 并做过几个小应用。
句柄实际上是一个数字,在 C 语言中可理解为指针,它指向一个对象的实例,通过句柄我们可以对对象进行访问,或者可以把它理解为资源标识符。比如窗口,我们拿到窗口句柄后就可以控制窗口对象上的东西了。
对于消息循环,我们 windows 下的每一个应用都是建立在消息循环之上的,windows 上的应用属于典型的消息驱动型。每个应用会自建一个消息队列,程序通过不断的存取分发消息来运行。所以对象之间可以利用消息队列进行通信,做到一个对象改变另一个对象。
接下来我们来分析一下百度网盘客户端的下载过程:
1、我们点击离线下载按钮会打开新建下载任务窗口,如下图所示,
image2、在这里我们要填入下载的地址然后点击开始下载按钮后即会进入到离线下载任务列表窗口,如下图所示,
image3、然后我们可以通过点击离线下载任务列表的后台运行按钮让离线下载任务列表隐藏后,就可以重复上述步骤开启下一次离线任务的添加了。
所以这里我们涉及三个窗口和三个按钮的操作。
3.3 拿到窗口句柄
拿到窗口句柄我们通过如下接口完成,
PyHANDLE = FindWindow(ClassName, WindowName)
我们只需传入窗口类名,或窗口的名称即可拿到这个窗口句柄,与之相关的还有个接口是
PyHANDLE = FindWindowEx(Parent, ChildAfter, ClassName, WindowName)
它是用来查找一个窗口下的子窗口句柄的,这里我们都会用到。
关于找窗口句柄这里不得不介绍一个利器 spy++,它是微软提供的一个工具可以浏览和查找当前系统下的所有窗口,并可监视窗口的消息。
3.4 点击按钮
开始我的思路是想通过拿到对应按钮的句柄,然后发送 click 事件给对应按钮来完成的,但我发现百度网盘的客户端上那些按钮应该来自于自定义 GUI 画的按钮,我暂时还没有办法拿到它们的句柄,所以我不得不换个思路,即找到按钮的坐标(这也可以通过spy++来找到)然后操控鼠标过去点击它来完成。
然后我找到 PyMouse 这样一个 python 库完美解决这个问题并封装成了一个简单函数如下:
# 文件 bdy_control.py
# 百度网盘离线下载按钮的坐标
download_button_x = 520
download_button_y = 110
# 新建离线下载任务页面开始下载按钮坐标
start_download_button_x = 480
start_download_button_y = 255
# 离线任务列表后台运行按钮
back_run_button_x = 485
back_run_button_y = 360
def click_window_button(name, x, y):
'''
模拟鼠标点击一个窗口的某一位置,通常是按钮的位置
'''
window_handle = win32gui.FindWindow(None, name)
# 查找超时处理,这里省略。。。
left, top, right, bottom = win32gui.GetWindowRect(window_handle)
x += left
y += top
mouse = PyMouse()
mouse.click(x, y)
3.5 填入下载地址开启下载
下载地址我们需要填入到新建下载任务窗口的一个文本编辑框内,好在用 spy++ 查看到是可以直接找到它的,这样我们只需要给它一个 WM_SETTEXT 消息并带入我们的下载链接地址即可,这样一来我们的任务基本就完成了,实现如下:
def download_link(link):
'''
使用百度网盘离线下载功能下载 link 的内容
'''
# 打开离线下载页面,通过模拟鼠标点击离线下载按钮实现
click_window_button('欢迎使用百度网盘', download_button_x, download_button_y)
time.sleep(1)
# 得到离线下载页面编辑框的句柄并填充下载地址
download_window_handle = win32gui.FindWindow(None, '新建离线下载任务窗口')
# 离线下载任务窗口出现较慢实际这里需要加入超时等待,这里省略。。。
edit_handle = win32gui.FindWindowEx(download_window_handle, 0, None, None)
win32api.SendMessage(edit_handle, win32con.WM_SETTEXT, 0, link)
# 点击开始下载按钮
click_window_button('新建离线下载任务窗口', start_download_button_x, start_download_button_y)
time.sleep(2)
# 点击后台运行按钮,结束本次下载任务
if(0x01 == click_window_button('离线下载任务列表', back_run_button_x, back_run_button_y)):
print('等待 离线下载任务列表 超时!!!')
time.sleep(2)
3.6 再看自动下载
如上我的最初想法已全部实现了,但它有个很明显的缺点是,在下载过程中由于鼠标被控制了,所以整个过程我只能傻傻的看着它,而这个过程在下载《糟糕历史》中花了差不多四分多钟的时间。
那么我们当然还有其他方法了,对的,那就是利用 python 模拟登陆网页版的百度网盘,然后通过 http 来发起离线下载任务。
是的这个想法将作为我的下一步目标。
四、写在最后
如果你看到了这里想必对此还是有点兴趣的,我将它托管在了 github 下面是它的地址:
我作为一个 python 初学者开始看起来有点困难的想法,做下来却不到百行的代码就实现了,人生苦短,我要用 python。
如果你是一个 python 老司机,如本文有表述不正确或代码很糟糕的地方还请指出,谢过。
最后欢迎扫码关注我准备好接收自动下载的升级版本吧。
image
网友评论