赞
踩
大家好,有时候我们会遇到一些较为陈旧的网站,却惊喜地发现它们非常好用。不过,遗憾的是,这些网站往往已经存在近十年,处于一种半死不活的状态。当尝试百度搜索如何下载这些网站时,会发现已有工具可以实现这一功能,例如HTTrack或者wget(Unix命令)。即使在Windows系统中,也可以通过Shell来实现这些功能。然而,每次使用这些工具都非常痛苦。例如,涉及到登录功能时,就需要手动保存cookie等信息。而且在使用过程中,还可能遇到许多有趣的bug。
因此,我决定尝试实现一个更加精细的网站下载器,目的是为了让整个网站的下载过程变得更加轻松。
首先,我发现自己实际上什么都不会。虽然我能轻易地想到需要使用哪些工具和实现哪些功能,但我不知道如何具体实现它们。我写了一个计划,但它并没有太大的用处。虽然我的论文并不是一个标准的程序设计论文——它没有完整的类图、流程图、E-R图等,但论文的意义并不在于此。它主要描述了我作为一个初学者面临的困难,以及我想要理解但却不知道的内容。因此,这篇论文也非常适合像我一样的初学者,尽管我也不完全确定,毕竟我自己也是一个初学者。
在开始编写代码之前,首先需要熟悉整个程序的结构。这包括理解不同类之间的相互作用以及正确使用所需的库。同时,遵循PEP8规范对于面向对象编程(OOP)来说非常重要。这些准备工作能确保代码具有良好的可读性,这不仅有助于未来的自己更容易理解代码,同时也保证了代码的可维护性(便于修改和扩展)和安全性(有效处理异常和错误)。
在程序设计中,面向对象编程(OOP)是一种常用的方法,因为它提供了一套基本的模型和规则。有时,我们在编程时不自觉地使用了OOP的概念,即使我们可能并不完全意识到这一点。理解OOP的基本概念有助于理解以下内容。在此,介绍一些OOP的基本概念。封装和接口之间的区别可能难以理解,因为它们紧密相连,但各有其独特的职责。以下是对这些概念的具体解释:
封装不仅仅是将事物分开,它更多的是关于隐藏实现细节,只向外部暴露必要的接口。封装的核心是将数据(属性)和操作这些数据的方法(行为)组合在一起。例如,如果下载操作直接实现在Download类内,那么在需要扩展功能时,就需要修改Download类的代码。而通过封装,可以形成独立的对象,其目的是隐藏对象的内部实现细节,只向外界提供一个清晰、简洁的接口。在Download类中,可以通过接口为每种下载方法指定自己的类,从而可以使用策略模式(一种设计模式)来处理这种多样性。Download类和接口(如IDownload)都不了解具体实现方法的细节。
例如,Download类创建了一个名为IDownload的接口(或抽象类)《interface》。这个接口不关心具体如何实现这些方法,只是声明这些方法必须存在。接口包含了所有下载操作必须实现的方法,例如:start(), pause(), resume(), cancel()等。根据不同的下载需求,可以实现多个具体的下载类,如WgetDownload, SeleniumDownload, RequestsDownload等。在程序的其他部分,如Download类,当需要进行下载操作时,代码只与IDownload接口交互,而不是直接与具体的下载类交互。这意味着可以根据需要选择使用哪个下载类,而无需修改Download类的代码。
本部分旨在辅助理解论文的核心内容,下述描述仅用于概述它们的基本功能。
```python
class User: # 代表用户实体,管理用户数据和行为,如用户名、密码、账户等。
# 具体代码
pass
class Account: # 代表特定服务上的账户,管理认证和会话信息。
class Download: # 管理网络下载过程,处理下载相关逻辑。
class File: # 管理文件读写操作,提供对本地文件系统的接口。
class DownloaderAppUI: # 管理用户界面相关的元素和事件。
class DownloaderAppLogic: # 作为 UI 和底层功能之间的桥梁,管理应用程序的主要逻辑。
class NetworkManager: # 处理所有网络请求和会话管理。
class StorageManager: # 管理本地数据存储,如文件和 Cookies。
class Logger: # 提供统一的日志记录接口。
class ErrorHandler: # 处理应用程序中的异常和错误。
class ConfigManager: # 管理应用程序的配置设置。
class CaptchaSolver: # 分别负责代理服务器配置和验证码解析。
# 其他,如 class ProxyManager: 等
```
- 一个 User 可以有多个 Account,而每个 Account 只能属于一个 User。
- User 负责存储和管理一些基本的数据,可以视为系统的底层。
- Account 主要负责管理和保存与账户相关的信息。
- NetworkManager 负责处理网络请求和会话管理。
- 网络请求包括获取 cookie(不仅限于此),而会话管理则涉及保存 cookie。这里的“会话”指的是用户登录后,系统用以记住用户身份的机制。
- 简单来说,NetworkManager 是 User 和 Account 的真正逻辑层(一部分),而 StorageManager 用于保存 cookie 的组件。
- 使用 PyQt 的各种控件(如 QLineEdit, QLabel 等)来构建用户界面,这个层不包含具体逻辑。
- QLineEdit 可用于接收用户输入,例如用户名、密码等(这只是一个简单的例子,实际应用可能更为复杂)。
- QLabel 可用于显示信息,例如登录状态、下载进度等(控件可以根据需求进行扩展)。
- User、Account 和 Download 是业务逻辑层的一部分。
- DownloaderAppLogic 类负责处理用户的输入(如登录、下载操作)并调用其他类(如 User、Account、Download)来执行具体任务。
- 例如,用户点击下载按钮后,逻辑层会处理下载请求。DownloaderAppLogic 获取 QLineEdit 的内容,传递给 User 类,然后 User 类通过 Account 类进行操作,最终到达 Download 类。在这个过程中,各个类可能会向 DownloaderAppLogic 发送各种信息(如登录状态、下载进度、错误信息等),则 DownloaderAppLogic 通过 QLabel 将这些信息展示在 UI 上,以便用户了解当前状态。
- 这一层实际上与界面层 (DownloaderAppUI) 没有直接的联系,所有的交互都是通过逻辑层来实现的。
- 可以将 DownloaderAppUI 和 DownloaderAppLogic 的关系看成为类(class)之间的联系,它们相互协作,但各自负责不同的功能模块。
1. wget:这种方式模拟 Unix 系统中的 wget 命令行工具。
2. Selenium:是一个用于自动化网络浏览器操作的工具,可以适用于网络请求(不好用,但是可以模仿用户操作,则避免反虫)。
3. Requests + BeautifulSoup:结合了 Requests 库(用于发送网络请求)和 BeautifulSoup 库(用于处理 HTML 内容)。
无论是使用 Requests 库还是 Selenium 工具获取的 HTML 源码,通常情况下,这些源码只包含 HTML 代码本身,而不包括 CSS 样式、图片文件或 JavaScript 脚本。这些额外的资源通常是通过 HTML 文档中的链接加载的,例如通过 `<link>`, `<script>`, `<img>` 等标签指定的链接。这意味着在处理下载任务时,可能需要额外的步骤来处理这些链接指向的资源。
在软件开发中,日志记录和错误处理是确保程序稳定性和安全性的关键环节。这通常涉及到两个主要组件:Logger 和 ErrorHandler。
负责记录各种类型的信息,这包括错误、警告、普通信息消息以及调试消息。它的使用并不仅限于记录错误。通常,Logger 记录的信息主要用于开发者分析和调试,而不是直接展示给用户。
专门用于处理程序中出现的错误和异常。它的主要目的是获取异常,以防止程序因未处理的错误而崩溃。ErrorHandler 通常会将获到的错误信息转换成用户可以理解的形式,并显示给用户。例如,ErrorHandler 代码的形式:
```python
class ErrorHandler:
def handle_error(self, error):
# 处理错误的逻辑
def some_method(error_handler):
try:
# 执行可能引发异常的操作
except SomeException as e:
error_handler.handle_error(e)
```
将异常处理逻辑集中到专门的 ErrorHandler 类中,可以提高代码的可维护性和可读性。与 Logger 相比,ErrorHandler 更专注于异常处理,而不是信息记录。因此,通常不会在 Logger 类中实现类似 ErrorHandler 的复杂处理逻辑,以避免过度职责。
- 1.1 是否需要登录?通过 `ConfigManager` 的 `saveLoginState(self, remember: bool)` 方法来决定。
- 1.2 选择账户数据(可以是多个账户或单个账户)通过 `ConfigManager` 的 `saveAccountPref(self, multiple_accounts: bool)` 方法来实现。
备注:在下面的步骤中,都会用到 `Logger` 和 `ErrorHandler`,关于日志记录及错误处理的具体内容已在“日志记录及错误处理”部分进行了详细描述。
- 2.1 用户需选择登录方式:“手动”或“自动”,通过 `ConfigManager` 的 `setLoginInfoSource(self, source: str)` 方法来设置。
- 2.2 如果选择“手动”方式:
- 2.2.1 用户通过 UI 输入登录信息(用户名/邮箱+密码),不在网站本身。
- 2.2.2 “QR 码登录”,通过 `ConfigManager` 的 `setManualLoginMethod(self, method: str):` 方法来设置,这样做是为了方便未来的扩展。
- 2.3 如果选择“自动”方式,则从文件中读取登录信息(用户名/邮箱+密码),通过 `ConfigManager` 的 `setAutoLoginMethod(self, method: str):` 方法来实现,并采用分层方法处理。
- 3.1 用户需要在“手动”和“自动”中选择一种登录方式,这通过 `ConfigManager` 的 `saveLoginMethodPreference(self, method: str)` 方法来实现。
- 4.1 判断是否需要进行验证,通过 `ConfigManager` 的 `setVerificationRequirement(self, required: bool)` 方法来设置。
- 4.2 选择验证方法:
- 4.2.1 “扫码”验证。
- 4.2.2 “email 验证码”验证。
- 4.2.3 “手机号验证码”验证。
- 4.2.4 类似于“Google Authenticator”的验证方法。
- 这些方法可以通过 `ConfigManager` 的 `selectVerificationMethod(self, method: str)` 方法来选择。
- 4.3 选择获取验证码的方式:
- 4.3.1 “手动”获取验证码。
- 4.3.2 “半自动”获取验证码。
- 4.3.3 “自动”获取验证码。
- 这些策略可以通过 `ConfigManager` 的 `setCaptchaRetrievalStrategy(self, strategy: str)` 方法来设置。
- 5.1 设置登录过程中是否需要通过验证码,可以通过 `ConfigManager` 的 `enableCaptchaForLogin(self, required: bool)` 方法来决定。
- 5.2 选择验证码处理方式:
- 5.2.1 “手动”处理验证码。
- 5.2.2 “自动”处理验证码。
- 这些处理方式可以通过 `ConfigManager` 的 `setCaptchaHandlingStrategy(self, strategy: str, **kwargs)` 方法来设置。
- 5.2.1.1 在“手动”模式下,用户需自行手动完成验证码的验证。
- 5.2.2.1 在“自动”模式下,可以选择“使用 API 解决验证码”或“使用专门的验证码解决库”。:
- 6.1 用户点击按钮以启动登录过程。DownloaderAppUI 并不是从一开始就展示所有设置选项,而是根据用户的选择逐步显示。例如,在 DownloaderAppUI 的“登录模块”中,初始化时仅显示部分界面,让用户决定是否需要登录。其他未激活的选项在界面上表现为不可用状态(如用灰色显示),以明确指示用户这些部分暂时不可选择。
- 6.2 如果用户在(1.1)中选择了需要登录,则(1.2)和(2)变为可选且必须的选项,用户未选择这些选项时将无法启动登录过程。
- 6.3 DownloaderAppLogic 会根据 ConfigManager 的设置逐步判断是否显示各个选项,同时也会基于 ConfigManager 来处理可能出现的异常等情况。
1. DownloaderAppUI 负责收集用户信息,并将这些信息传递给 DownloaderAppLogic。
2. DownloaderAppLogic 接着确定如何处理这些数据。例如,它可能会先查看 ConfigManager 中的设置,然后利用 Account 类尝试执行登录操作。
3. Account 类可能会通过 NetworkManager 向服务器发送请求,以进行用户认证。
4. 用户认证的结果会被返回给 DownloaderAppLogic,随后由其处理后续逻辑。这可能包括更新 User 类的登录状态,或者通过 DownloaderAppUI 向用户反馈登录成功或失败的信息。
1. 初始化:DownloaderAppUI 在每次启动程序时进行初始化。
2. 显示选项和信息:根据 DownloaderAppLogic 的指示,DownloaderAppUI 显示新的选项和信息。
3. 信息保存:DownloaderAppUI 收集的属性信息由 DownloaderAppLogic 保存到 ConfigManager 中。
4. 执行与处理:DownloaderAppUI 收集的执行命令由 DownloaderAppLogic 根据其异常处理机制和 ConfigManager 的设置来处理。
5. 用户选择与执行:例如,用户决定是否需要登录,以及选择信息获取的方法。DownloaderAppLogic 根据用户的选择和其异常处理机制,在 DownloaderAppUI 中显示相应的信息窗口(如手动方法)或选择文件路径(如自动方法)。
- 如果需要手动输入信息,DownloaderAppUI 将信息直接发送到 Account。
- 如果是通过文件获取信息,DownloaderAppUI 会引用 StorageManager。StorageManager 从文件中获取信息并发送到 Account。
完成上述过程后,会将“信息成功获取”的状态更新为 True。这包括“选择部分”和“获取输入部分”的处理。
选择有自己的逻辑,先选a,然后如果a为true那一定选择b,c,d等,首先输入不会影响选择,就是能先完成选择才开始输入,或者选择部分啊就立刻输入,继续。但是实际上逻辑更复杂。并且但是选择和输入跟能不能启动登陆有联系。
DownloaderAppUI 首先显示是否选择进行登录验证的选项。由于之前已选择登录,因此界面自动增加了包含验证码等新的选择项。
DownloaderAppLogic 接收用户的选择,并将这些选择(例如是否启用验证码验证)的布尔值保存在 ConfigManager 中。
- 手动和自动模式:如果已有固定的验证码保存在 Account 中,那么对于“手动”模式,验证码验证通过 DownloaderAppUI 自行完成;对于“自动”模式,则通过 StorageManager 来处理。
- 如果验证码是登录过程中才产生的,那么在登录过程中将根据 StorageManager 的判断进行处理。例如,在手动模式下,NetworkManager 会在登录过程中暂停,等待用户完成验证码验证。
- NetworkManager 是实现登录的主要类,依赖于 Account、ConfigManager 等。当 DownloaderAppUI 启动登录时,DownloaderAppLogic 指示 NetworkManager 开始登录过程。
- 在半自动模式下,NetworkManager 可尝试打开电子邮件网页并试图登录,寻找验证码。每一步都会向 DownloaderAppUI 反馈,以确认操作是否正确。如果 DownloaderAppUI 返回 False,或者 NetworkManager 直接获取失败,则会从半自动模式切换到手动模式。
- 自动模式与半自动模式类似,但不会在每一步都询问用户确认。这种模式可能会出现错误,最终可能需要切换回手动模式。
此部分及其后续流程,由于对登录过程的影响较大,半自动和自动模式可能存在使用上的困难。这意味着,如果出现错误导致登录失败,系统可能需要询问用户是否重新登录。同时,为了避免重复出错,建议在下一次尝试登录时将验证和验证码(captcha)的处理方式更改为“手动”模式。
1. 这一部分的流程与之前的流程相似,但有一个关键区别:用户无需从文件或 UI 提前获取答案,因为验证码处理只在登录开始后才需要进行。
2. 一旦 DownloaderAppUI 启动登录流程,就会将全部处理权交给 NetworkManager,并且它会调用 CaptchaSolver 来解决验证码问题。
3. 手动和自动模式的逻辑在这里是相似的,但不包括半自动模式。
在上面两个需要在DownloaderAppUI,手动部分显示为“推荐”,同时,在用户选择半自动或自动操作时,应提醒用户这两种方式的效率可能相对较低。
1. 一旦登录成功,NetworkManager 会将 cookie 保存在 StorageManager 中。
2. 接着,NetworkManager 将登录状态更新为成功。
3. 需要注意的是,在这个过程中会涉及到日志记录(logger)和异常处理。这些操作会根据具体的逻辑情况来执行,并随之动态编写。
- 7.1 用户输入他们想要下载内容的网站地址(URL)。这可以通过 `configManager.saveDownloadURLs(url_list)` 方法来实现。
- 7.2 用户还可以输入不希望访问的链接(这样做是为了避免程序自动退出账户)。这可以通过获取 `configManager.getBlockedURLs()` 方法返回的禁止访问的链接列表 `blocked_url_list` 来完成。
流程描述:
1. 用户首先在界面上(如 DownloaderAppUI)输入多个 URL。
2. 接着,DownloaderAppUI 将这些输入的 URL 传递给 DownloaderAppLogic。
3. 然后,DownloaderAppLogic 会调用 `configManager.getBlockedURLs()` 方法来保存这些 URL。
- 8.1 选择下载方法:
- 8.1.1 “正常”模式:以 wget 作为主要工具进行下载。
- 8.1.2 “高级”模式:使用 selenium + request(以 download 为主工具)。若选择“高级”模式,需先检查该工具是否已实现。如果未实现,则显示“抱歉,您选择的工具暂时不支持”,并自动切换至“正常”模式。
- 8.2 选择保存路径(下载文件的存储位置)。
- 8.3 设置递归下载深度(决定下载内容的层级深度)。
- 8.4 设置下载速度,例如:在一分钟内下载 20 页。
- 8.5 设置大小限制,例如:当下载的网站文件夹达到 2GB 时,自动结束下载。
- 8.6 选择特殊目标,如在下载过程中或下载后自动寻找特定目标,例如:百度盘、夸克盘等链接并解析。
- 通过 `configManager.setDownloadPreferences(preferences)` 方法设置下载相关的用户偏好,如下载路径、下载速度限制等。
- 9.1 在下载过程中提供以下选项:
- 9.1.1 暂停下载:通过 `downloader.pauseDownload()` 方法实现。
- 9.1.2 取消下载:通过 `downloader.cancelDownload()` 方法实现。
- 9.1.3 继续下载:通过 `downloader.resumeDownload()` 方法实现。
- 9.1.4 重新启动下载:通过 `downloader.restartDownload()` 方法实现。这个操作不会删除当前已下载的内容,而是在新的文件夹中重新开始下载。
- 注意:这些方法应在 `Download` 类中得到实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。