在少数派会员社区中,一个反复出现的问题就是「现在用什么快捷指令可以下载 Instagram?」
说实话,如果说在快捷指令刚亮相的那两年,这还是一个既实用又适合教学的案例,那么随着 Instagram 的反爬策略如今越发极端,再试图仅依靠快捷指令来揽这种「瓷器活」,就显得既拔苗助长、又得不偿失了。虽然仍然不时能搜到一些有效方案,但基本都是朝生暮死,其中步骤也繁复得令人眼花缭乱,完全失去了快捷指令简洁易上手的意义。推特图片、YouTube 视频、网盘内容等的下载也是类似。
相比之下,如果把目光移出快捷指令的狭小世界,可用的方法仍然是很多的,并且尤其集中于技术人员喜爱的终端环境。例如,我们之前多次提及的 yt-dlp 能稳定下载包括 YouTube 和 Bilibili 在内各大平台的内容,Instagram 等图片网站的下载则可以通过 gallery-dl 实现。由于开源开发模式,这些项目也能很快适配各类服务的接口变化,不会像快捷指令那样能用多久纯靠天收,也不仅限于 iOS 平台。
如何在移动端拥有命令行
当然,由此产生的问题就是:如何不那么麻烦地在移动端用上命令行软件,替我们处理这些自动化需求呢?
显然,我们需要:(1) 一个支持移动操作系统的终端运行环境,它能够以某种方式 (2) 从外部应用接受输入并返回输出,并且能 (3) 不受沙箱限制地读写本机文件系统。
下面我们依次分析一下如何实现这些条件。
终端运行环境
尽管终端环境一般和桌面系统、特别是 Linux 系统联系在一起,但它并不以任何特定系统为运行前提,只是一种以纯文本处理输出和输出的交互方式。因此,只要构造一个基于纯文本的用户界面,在任何环境下都可以获得一个「命令行」。
当然,光有命令行的样子是不够的,重点在于能运行哪些软件。即使不能运行一个完整的 Linux 系统,至少也要能跑起一个外壳(shell)程序,并实现 ls
、echo
、rm
等核心命令。这方面,主要的障碍在于「语言不通」的问题:为桌面系统和处理器构建的软件,无法直接拿到移动系统和 ARM 处理器上运行。
为此,开发者们已经想出了一系列方法来曲线救国:
交叉编译和补丁。 这是最简单直接的思路,被 iOS 上的 a-Shell 和 Android 上的 Termux(自带默认环境)采用。开发者用 iOS 和 Android 的平台开发工具,将常见的 Linux 软件源代码编译为可以在这些系统上运行的版本(往往还要打上一些补丁),然后打包成应用。
这种方法性能最好、运行最稳定,但可用软件数量和更新速度相对受限,而且面临哪些软件应该内置(代价是安装体积大且与 GPL 许可不兼容)、哪些应该作为额外下载(代价是上手门槛高且可能被商店拒收)的两难。
因此,目前 a-Shell 只有三百多个内置软件包,外加约四十个编译为 WebAssembly 格式的可下载软件;Termux 稍丰富一些,官方源收录数略多于两千个,但仍然不到 Debian 等大型发行版的零头。
伪装环境。 Termux 提供的 proot-distro
包和 AnLinux、Andronix 等辅助工具采用这种方法。它们可以在手机上安装一个完整无修改的 Linux 系统,通过 PRoot 让这些来宾系统「以为」自己正在独立的根文件系统下运行。
这种方法的性能损失不大,软件数量也远多于交叉编译,但用起来比较折腾,而且安装完整系统会占用很多空间。在未 Root 设备上,伪装环境也不能让来宾系统获得真正的 Root 权限,需要提权的软件仍然是不能执行的。
模拟器。 采用这种方法、且适合普通用户尝试的方案主要是 iOS 应用 iSH。它通过软件方式模拟出一个 32 位 X86 架构环境,然后在这个环境中运行精简的发行版 Alpine Linux。
这样的好处是可以安装被模拟系统支持的所有软件,但缺陷则和所有模拟器一样,大量性能消耗在指令翻译上,并且很多软件即使装上也有兼容性问题(一个老大难是 Node.js)。
此外,在未越狱的 iOS 设备上运行 UTM,或者在未 root 的 Android 设备上运行 QEMU,所创建的来宾系统也是通过模拟方式运行的,但操作过于复杂,很难作为推荐。因此,模拟器方案总体上都只有尝鲜价值,实用意义不大。
虚拟机。 尽管理论上可以通过虚拟机安装一个支持 ARM 架构的 Linux 系统,并且性能会比模拟器好得多,但运行真正的虚拟化环境需要处理器硬件支持、操作系统支持且有 root 权限,这是一般用户很难满足的条件。
目前,只有越狱的 Apple silicon 型号 iPad 可以通过 UTM 安装虚拟机,也有人在已 root 的 Android 设备上通过 Android 13 起加入的虚拟化框架成功安装了虚拟机。因此,这种方法基本也停留在理念验证层面。
总的来说,如果要从这些方案中做推荐,走交叉编译路线的 a-Shell 和 Termux(自带默认环境)还是最实用和不折腾的选择。 尽管软件包数量有限,但已经覆盖了日常所需,何况还可以借助 Python 生态扩展出很多功能。因此,本文之后就重点以这两个工具为例演示。
与外部应用互通
应用之间的相互调用和数据传输是建立自动化流程的前提。例如,要调用命令行工具下载视频和图片,必须要能通过某种方式将目标网址传递给它。对此,a-Shell 和 Termux 分别都提供了适合所在平台的方式。
a-Shell 在安装后会向「快捷指令」app 添加三个动作,分别用于运行终端命令、传入文件和获取文件。此外,还支持通过 ashell://
这一 URL Scheme 启动应用,其后追加的内容会视作命令在启动后执行。
如果需要将运行结果传出给其他应用,可以使用 pbcopy
命令写入剪贴板,或者使用 open
命令调用其他应用的 URL Scheme,将待输出内容作为参数附入。
Termux 在安装后会出现在系统的分享菜单中,通过分享菜单传入的文本内容会被视作参数传递给位于 ~/bin/termux-url-opener
的脚本(默认无此文件,需要自己创建),通过调整该脚本内容即可基于分享内容实现不同功能。此外,可以通过安装额外的 Termux:Tasker 插件获得与 Android 端知名自动化工具 Tasker 互通的能力。
如果需要将运行结果传出给其他应用,可以在安装 Termux:API 插件后通过 termux-clipboard-set
命令写入剪贴板,或者使用 termux-share
通过分享菜单传出。
读写文件系统
在移动端使用命令行工具时,一个绕不开的障碍就是 iOS 和 Android 的沙盒环境:如果一切操作都只能发生在沙盒之内,实用价值就会非常有限。好在 a-Shell 和 Termux 分别提供了一些与本机文件系统互通的渠道,经过简单配置即可读写常见的外部路径。
a-Shell 在默认情况下,只有内部文件系统的 ~/Documents
是可以从外部访问的,对应「文件」app 中的 On My iPhone/a-Shell
。
如果需要读写额外的目录,可以通过 pickFolder
命令为外部文件夹创建一个同名的「书签」(可通过 showmarks
命令查阅),此后就可以通过 ~MARK
的格式调用。更多信息参见文档。
Termux 在默认情况下只通过「存储访问框架」向外提供内部文件系统主目录 ~
的内容,可以在外部文件管理器或文件选择窗口的侧边栏选择 Termux 访问。
如果需要读写额外的目录,需要运行一次 termux-setup-storage
命令授予权限,然后手机内部存储会被映射到 Termux 文件系统的 ~/storage/shared
路径下,可供命令行工具读写。更多信息参见文档。
至此,我们已经从理论层面说明了在移动端借助命令行程序解决自动化需求的可行性。接下来,我们就以下载 YouTube 视频和 Instagram 图片为例,具体演示如何让命令行工具在手机上跑起来,又如何与其他应用组合成自动化流程。
需要说明,选择这两个下载场景作为示例,主要是因为它们是比较热门和大众的需求,并且能相对完整地演示输入输出处理、文件操作等关键步骤,但命令行工具在移动端的潜力显然不止于此。例如,哪怕是软件数量「寥寥」的 a-Shell,其实也内置了全套的 LaTeX、Jupyter 工具,有兴趣的读者可以多多探索和尝试。
制作前准备:yt-dlp 和 gallery-dl 用法速成
在介绍具体的移动端配置方式之前,我们先简单介绍一下 yt-dlp 和 gallery-dl 的基本用法,作为后续步骤的铺垫。
同为下载媒体文件的项目,yt-dlp 和 gallery-dl 具有非常相似的语法,最简使用方式都是直接将要下载的资源网址作为参数传入,即:
yt-dlp <URL>
gallery-dl <URL>
默认的下载路径都是当前工作目录,如需更换,在 yt-dlp 中可以使用 -P, --paths <PATH>
选项,在 gallery-dl 中可以使用 -D, --directory <PATH>
选项。
对于视频下载,yt-dlp 提供了非常详尽的格式选项,可以通过 -f, --format <SELECTOR>
指定。其中的默认选项为 bv*+ba/b
,指的是按下列优先级尝试:
- 如果有分开提供的音视频流,则下载一个质量最好的视频流(无论是否自带音频)和一个质量最好的音频流,然后用 ffmpeg 合并;
- 如果只有内置音频的视频流,就从中选择一个质量最好的下载。
考虑到移动端播放 MP4 格式一般更为方便,我们可以在这一默认选项之前再加上 bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]
,从而优先获得质量最好的 MP4 格式视频。
此外,对于 IG、推特这类反爬严格的站点,yt-dlp 和 gallery-dl 都支持以多种方式导入用户自己的登录信息。在桌面端,最方便的方法是使用 --cookies-from-browser <BROWSER>
选项,即可自动从选定的浏览器配置文件中获取 cookies。
但对于本文讨论的移动端场景,则需要手动将 cookies 导出为文件,然后使用 -C, --cookies <FILE>
选项加载。为此,需要先在桌面端浏览器中完成登录,然后使用诸如 Get cookies.txt LOCALLY (Chrome), Export Cookies (Firefox) 等插件将该站点的 cookies 导出,供后续步骤使用。
由此,我们就得到了适合移动端使用的 yt-dlp 和 gallery-dl 命令模板: