迎来阅读第三篇Windows 命令行系列文章。在这篇,我们开始深入Windows 控制台和命令行,它是什么,你可以用它可以做什么……和它不能做什么!
系列文章:
在开始开发Windows NT操作系统的那时候,大概是1989年,那时候还没有GUI(图形化用户界面),也没有桌面操作系统,只有最原始的全屏的命令行界面,类似于MS-DOS的可视化界面越来越重要!Windows GUI 开始开发的时候是在开发团队需要开发一个基于控制台的应用的背景下诞生的!Windows 控制台是第一个Windows NT的GUI应用,并且可以保证兼容运行继续使用已有的Windows应用。
Windows 控制台最初的代码到现在(2018年)已经有30年的历史……古老的东西,事实上,今天还有很多开发者在使用它!
控制台程序能做什么?
就像之前的文章说的,终端的工作其实很简单:
但是,Windows 控制台能做的事情有些不同:
深入Windows控制台内部
Windows控制台是一种传统的Win32可执行文件,虽然它最初是用“C”编写的,但随着团队现代化和模块化控制台的代码库,大部分代码都已正在迁移到现代C++了。
对于那些关心此类事物的人:许多人都在询问Windows是用C还是C++编写的。答案是 - 尽管NT是基于对象的设计 - 像大多数操作系统一样,Windows几乎完全用C语言编写!为什么? C++在内存占用和代码执行开销方面引入了开销。即使在今天,使用C++编写的代码的其所隐藏的开销也会令人大吃一惊,但早在1990年代后期,此时内存价格约为60$/MB(是的......每个MEGABYTE为60美元!)时,vtable等隐藏机制的内存开销非常高。此外,虚方法间接调用和对象解引用的开销可能导致当时的C++代码存在非常显着的性能和规模损耗。虽然你仍然需要当心,现代C++在现代计算机上的性能开销并不是一个值得关注的问题,同时考虑到其安全性、可读性和可维护性方面的优势,这通常是一种可接受的折衷...这就是为什么我们将Console的代码稳步升级到现代C++这样做的原因!
那么,Windows 控制台内部是什么样?
在 Windows 7 之前,Windows 控制台实例托管于核心的客户-服务器运行子系统(Client Server Runtime Subsystem,CSRSS)!然而,在 Windows 7 中,考虑到安全性和可靠性因素,控制台从CSRSS 中剥离出来,组件了一个包含如下二进制文件的新家庭:
控制台当前的内部结构总体结构图就像这样:
控制台的核心组件包含如下内容(自下而上):
Windows控制台API
从上述的控制台架构图中可以看出,与NIX终端不同的是,控制台发送/接收API调用和/或数据序列化为IO控制(IOCTL)消息,而不是序列化后的文本! 甚至从(主要是Linux)命令行应用程序接收的文本中所嵌入的ANSI/VT序列也被提取、解析并转换为API调用!
这种差异揭示了*NIX和Windows之间关键的基本哲学差异:在*NIX中,“一切都是文件”,然而在Windows中,“一切都是对象”!
两种方法都有利有弊,我们将概括之,但避免在这里进行长篇大论。请记住,哲学中的这一关键差异是Windows和* NIX之间诸多差异的基础!
在 *NIX系统中,一切都是文件
在60年代末和70年代初Unix被第一次实现的时候,其中一个核心原则就是任何东西都可以被抽象成文件流,一个关键目标是简化对设备和外设的访问处理:如果所有的设备都在系统中以文件系统的形式存在,那么现存的代码就可以不做修改地直接访问这些设备。
这个原则影响深远:你可以通过伪文件系统或虚拟文件系统来浏览和查询大量的基于*NIX的系统和机器配置,它们仅仅是”表现得“像是“文件”或“文件夹”,实际可能是机器配置或硬件。
例如,在Linux中,你可以通过访问 /proc/cpuinfo 虚拟文件节点来查看CPU的一些信息:
这个模型是如此简单和一致,但它也存在一些额外开销:从这些伪文件中提取或查询特殊的文本信息并从执行命令中返回,经常需要一些工具的辅助,比如:sed,awk,perl,python等。这些工具经常被用来写脚本和命令来解析文本内容、查找特殊模式、区域和值。这些脚本可以变得非常复杂,难以维护和碎片化。如果文本的结构、布局或格式发生变更,那么许多脚本也需要随之更新。
在Windows中,任何事物都是对象
当Windows NT被设计和构建时,“对象”被视为软件设计的未来:“面向对象”的语言比洞穴里的兔子更快出现 - Simula和Smalltalk已经建立起来,而C ++正变得越来越流行。其他面向对象的语言,如Python,Eiffel,Objective-C,ObjectPascal / Delphi,Java,C#等许多其他语言都在快速发展紧随其后。
不可避免的是,它成型于面向对象大好时期(大约1989年)中,Windows NT的设计理念是“一切都是对象”。事实上,NT内核最重要的部分之一是“对象管理器”!
Windows NT公开了一组丰富的Win32 API,可以调用这些API来从操作系统获取和/或操作对象。开发人员使用Win32 API来收集和呈现* NIX伪文件和工具提供的类似信息,但是通过对象和结构。并且因为解析器,编译器和分析器理解对象的结构,所以通常可以更早地捕获许多编码错误,从而帮助验证程序员的意图在语法和逻辑上是否正确。随着时间的推移,这也可以减少系统破损,波动和“搅动”。
所以,回到我们关于Windows控制台的中心讨论:NT团队决定构建一个“控制台”,它在几个关键领域区别于传统的* NIX终端:
Windows控制台的问题
虽然Console的API已经证明在Windows命令行工具和服务领域非常流行,但以API为中心的模型对命令行方案提出了一些挑战:
只有Windows实现了Console API
许多Windows命令行工具和应用程序广泛使用Console API。
问题呢?这些API仅适用于Windows。
因此,结合其他差异化因素(例如过程生命周期差异等),Windows命令行应用程序并不总是易于移植到* NIX,反之亦然。
因此,Windows生态系统开发了自己的,通常类似但通常不同的命令行工具和应用程序。这意味着用户在使用Windows时必须学习一组命令行应用程序和工具,shell,脚本语言等,而在使用* NIX时则需要学习另一组。
这个问题没有简单的快速解决方案:Windows控制台和命令行不能简单地丢弃并被bash和iTerm2取代 - 有数以亿计的应用程序和脚本依赖于Windows控制台和Cmd / PowerShell shells。
像Cygwin这样的第三方工具可以很好地将许多核心GNU工具和兼容性库移植到Windows,但是它们无法运行未移植的,未经修改的Linux二进制文件。这非常重要,因为许多Ruby,Python,Node包和模块依赖于或包装Linux二进制文件,或者依赖于* NIX运转状态。
这些原因促使微软通过在 Windows的子系统Linux(WSL)上本地运行真正的,未经修改的Linux二进制文件和工具来扩展Windows的兼容性。使用WSL的用户现在可以在同一台机器上并行下载和安装一个或多个Linux发行版,并使用apt / zypper / npm / gem / etc.安装和运行绝大多数Linux命令行工具以及他们喜欢的Windows应用程序和工具。
但是,还有一些控制台提供的东西尚未被非Microsoft终端采用:具体来说,Windows控制台提供命令历史记录和命令别名服务,从而无需每个命令行shell(特别是)重新实现相同的功能。
把 Windows 命令行远程化是困难的
正如我们在 Command-Line Backgrounder 一文中所讨论的那样,终端最初与它们所连接的计算机是分开的。快进到今天,这种设计仍然存在:大多数现代终端和命令行应用程序/shell 等等是由进程或机器边界分隔的。
在基于 *NIX 的平台上,终端和命令行应用程序的分离并通过简单的字符进行通信的概念导致 *NIX 命令行易于从远程计算机/设备访问和操作:只要终端和命令行应用程序可以通过某种类型的有序串行通信基础架构(TTY/PTY 等)传输字符流,远程操作 *NIX 机器的命令行是非常简单的。
但是在 Windows 上,许多命令行应用程序依赖于调用 Console API,并假设它们与控制台本身在同一台机器上运行。这使得远程操作 Windows 命令行 shell/工具等变得很困难:在远程计算机上运行的命令行应用程序如何调用在用户本地计算机的控制台上的 API 呢?更糟糕的是,如果远程命令行应用程序通过 Mac 或 Linux 机器上的终端访问,它如何调用 Console API 呢?!
很抱歉开个玩笑,但我们将在以后的文章中更详细地阐释这个主题!
启动控制台或者不!
通常,在基于 *NIX 的系统上,当用户想要启动一个命令行工具时,他们首先会启动一个终端。然后终端启动一个默认的 shell ,或者可以配置为启动一个特定的应用程序/工具。终端和命令行应用程序通过伪终端(PTY)交换字符流进行通信,直到一个或两个字符终止。
然而,在 Windows 系统上,事情就不一样了:Windows 用户永远不会启动控制台(conhost.exe)——然而他们会启动像是 Cmd.exe,PowerShell.exe,wsl.exe 等等这样的命令行 shell 和应用程序。Windows 系统将新启动的应用程序连接到当前控制台(如果是从命令行启动的话),或者连接到新创建的控制台实例。
# 现在要说的?
是的,在 Windows 系统中,用户启动命令行应用程序,而不是控制台本身。
如果用户从现有的命令行 shell 启动命令行应用程序,Windows 通常会将新启动的 .exe(可执行文件) 附加到当前控制台。否则,Windows 会将一个新的控制台实例与新推出的应用程序绑定在一起。
小白说:很多人说“命令行程序在控制台运行”。这不是真的,而且导致很多关于控制台和命令行应用程序如何工作的困惑!命令行应用程序和它们的控制台都在各自独立的 Win32 进程中运行。请通过指出“命令行工具/应用程序连接到控制台运行”(或类似的)来帮助纠正这种误解。谢谢!
听起来不错,对吧?嗯…不;这里有一些问题:
1.控制台和命令行应用程序通过经由驱动程序的 IOCTL 消息进行通信,而不是通过文本流进行通信
2.windows 要求 ConHost.exe 必须是连接到命令行应用程序的控制台程序
3.Windows 控制了控制台和命令行应用程序通信之间通信“管道”的创建
这些都是明显的限制:如果你想为 Windows 创建一个替代控制台的应用程序,该怎么办?你将如何发送键盘、鼠标、笔等等外设的信息?如果你无法访问连接你新控制台和命令行应用程序的通信“管道”,用户将怎么对命令行应用程序进行操作?
遗憾的是,这些情况并不好:有一些很棒的用于 Windows 的第三方控制台(和服务器应用程序)(例如 ConEmu/Cmder, Console2/ConsoleZ, Hyper, Visual Studio Code, OpenSSH 等),他们必须通过离奇的跳转才能像正常的控制台一样运行!
举例来说,第三方控制台必须在屏幕外启动一个命令行应用程序,例如(-32000,-32000)。然后,他们必须向屏幕外控制台发送击键信息,然后收集屏幕外控制台的文本内容并在自己的 UI 上重新绘制它们!
我知道,这很疯狂,对吧? !这证明了这些应用程序创造者们的独创性和决心,这些程序甚至还在有效的运行!
这显然是我们急于补救的一种情况。请继续关注这部分内容的更多信息——在这方面有一些好消息!
Windows 控制台 & VT
如上所述,Windows 控制台提供了大量 API。使用控制台 API,命令行应用程序和工具可写入文本,更改文本颜色,移动光标等。并且,由于控制台 API 的存在,Windows 控制台几乎不需要支持 ANSI/VT 序列,这些序列在其他平台上提供非常类似的功能。
实际上,在 Windows 10 之前,Windows 控制台仅实现了对 ANSI/VT 序列的最低限度支持:
从2014年开始,微软组建了一个新的 Windows 控制台团队,使得这一切都发生了变化。控制台团队的最高优先级事项之一是实现对 ANSI/VT 序列的全面支持,以便渲染在 Windows 子系统之Linux(WSL)和远程 *NIX 机器上运行的 *NIX 应用程序的输出。您可以在本系列的上一篇文章中阅读更多关于这个故事的内容。
控制台团队迅速为 Windows 10 的控制台添加了对 ANSI/VT 序列的全面支持,使用户能够使用和享用大量 Windows 和 Linux 命令行工具和应用程序。
该团队继续改进和完善每个操作系统发布版本上的控制台对 VT 的支持,并对您在我们的 GitHub 问题跟踪器上提交的任何问题表示感谢。
处理Unicode
一个快速的Unicode回顾:
Unicode或ISO/IEC 10646是一个国际标准,定义了地球上几乎每个书写系统中所使用的每个字符/字形,以及当今使用的许多非脚本符号和字符大小的图像(例如表情符号)。目前(2018年7月),Unicode 11定义了137439个字符,包含146个现代和历史文字系统!
Unicode还定义了几种字符编码,包括UTF-8, UTF-16, 和UTF-32:
由于UTF-8的高效的存储要求以及在HTML页面中的广泛使用,它是目前最流行的编码。
UTF-16/UCS-2都是常见的,尽管在已存储文档(例如网页、代码等)中其使用比例正在降低。UTF-32是很少使用的,因为它的效率低且存储需要相当大的空间。
很好,所以我们有有效并且高效的方式来表示和存储Unicode字符了!
所以?
哎呀,Windows控制台及其API是在创建Unicode之前创建的!
Windows控制台将文本(随后在屏幕上绘制)存储为每个单元需要2个字节的UCS-2字符。
命令行应用程序使用控制台API将文本写入到控制台中。处理文本的控制台API有两种形式 - 带有A后缀处理的单字节/字符串的函数,带有W后缀处理双字节(wchar)/字符串的函数:
例如,WriteConsoleOutputCharacter()函数编译为ASCII项目的WriteConsoleOutputCharacterA(),或Unicode项目的WriteConsoleOutputCharacterW()。如果需要指定处理方式,代码中可以直接调用... A或...W后缀的函数。
注意:每个W API至少支持UCS-2,因为这是在进行A/W拆分时就存在的事情,我们认为这样做会很棒。但许多W API已更新为在同一渠道上也支持UTF-16
。并非所有W API都可以支持UTF-16,但所有W API至少可以支持UCS-2。
此外,控制台不支持一些较新的Unicode功能,包括零宽度连接符(ZWJ),该符号被用于连接阿拉伯语和印度语中的其他单独字符,并将表情符号字符组合成一个可视字形!
那么如果你想在控制台上输出一个ninjacat表情符号或复杂的多字节中文/阿拉伯字符会怎样呢? 糟糕的是,你做不到!
Console API不仅不支持长度超过2字节/字形的Unicode字符(NinjaCat表情符号需要8个字节!),但Console内部的UCS-2缓冲区不能存储该数据的额外字节,更糟糕的是 ,Console当前的基于GDI的渲染器甚至无法绘制字形,即使缓冲区可以存储它!
可叹! 这就是遗留代码的乐趣。
但是,我也会希望你们到此打住 - 我们将在本系列的新一篇文章中回到这个主题。 敬请关注!
再一次,亲爱的读者,如果你读过以上的所有内容,谢谢你,也祝贺你 —— 你现在比你的大多数朋友都更了解 Windows 控制台,甚至可能比你想知道的还要多!祝你幸运!
在这篇文章中,我们涵盖了很多内容:
Windows控制台的主要构建模块:
控制台做什么?
控制台与 *NIX 终端有什么不同
控制台存在的问题
在本系列的后续文章中,我们将深入探讨控制台,并讨论如何处理这些问题……和更多其他内容!
像往常一样,请继续关注我们。
本文由oschina作者参与翻译,如有侵权,请联系删除。
着网络技术的发展,越来越多的应用基于互联网发布,再好的应用,如果打开速度慢,10个用户会有9个用户选择离开,相关统计数据显示,每增加0.1秒的加载延迟,将会导致客户活跃度下降1%。在目前获客成本较高,用户面临众多可选项的情况下,如何提高用户访问的体验,给用户留下良好的第一印象,提高ROI,是所有开发互联网应用的企业都关注的核心问题。
影响应用资源加载的因素很多,服务器性能、网络传输质量、网站出口带宽状况、DNS解析时间、网页内容大小、终端用户网络质量等,在云计算技术高度发达的今天,并非每一个环节的优化都需要企业自己造轮子,更为便捷可行的选择是:借助云服务商提供的相应加速服务来优化企业的应用,可以实现更低的成本、更敏捷快速的建设、更强壮和高性能的服务,用来取代传统的用昂贵的成本购买大量服务器、带宽做自建的模式。
标准CDN服务所擅长加速的内容是静态内容,如文件、图片、视频等,通过CDN的缓存策略来缓存并实现加速。但互联网上的应用复杂,源站往往也会有很多经常变化的内容---动态内容,如用户登录、内容搜索、视频弹幕、直播评论、购物交易、股票行情、体育实况等,很多时候这些应用没有做动静分离设计和动静内容分别处理,这也造成很多应用即使采用了标准CDN服务,却没有达到很好的加速效果,因为动态部分内容的加速性能并没有得到很好的优化。
对应上叙的情况,我们推荐使用阿里云全站加速产品,阿里云全站加速产品是阿里云自主研发的融合了动态加速技术和静态加速技术的CDN产品,可以很好的解决页面动静态资源混合、跨运营商、网络不稳定、单线源站、突发流量、网络拥塞等诸多因素导致的响应慢、丢包、服务不稳定的问题,全面提升全站性能和用户体验。
阿里云全站加速产品可以有效提升静态、动态内容的加速效果。下面的图我们可以具体看下,使用全站加速产品前后的效果对比。从实际的测试结果图中可以看到使用全站加速前,无论访问效率,还是节点加速效果,都较未使用前有了明显的提升。
那阿里云全站加速怎么样才能够实现以上的效果呢?主要通过以下四个方面完成:
第一、全球覆盖的加速节点阿里云在全球有2800+节点,这些节点涵盖了中国国内、欧洲、美洲、亚洲、非洲等全世界的大部分区域,同时这些节点都是互联了当地最核心的运营商网络,阿里云全站加速可以保证全球用户都能够找到离他最近的、访问质量最好的和用户接入网络运营商相对应的加速节点。
第二、全球智能调度系统在上面我们讲到了覆盖,只有覆盖还是不够的,还需要把用户调度到对应的最合理的节点,这个环节就非常取决于调度的IP库的完善性和准确性。阿里云全球智能调度系统结合阿里整体庞大的用户基础(淘宝、天猫、优酷等),基于这些用户基础可以打造非常详尽、精准的用户IP库,可以有效保证用户接入匹配的高准确度。
第三、智能自适应缓存业界一般对于全站加速的场景,需要客户手动配置动静态内容,来让CDN平台执行动态和静态两种加速模式。但是很多网站特别是中小客户,动静态内容区分不是很清晰,不便于做动静态的区分。全站加速推出的动静态智能自适应功能,可以让客户不再需要繁琐配置动静态内容区分,全站加速会自动的分析和识别请求和响应特征,智能的对访问的内容进行动静态分类,让可以缓存的静态内容避免了动态化访问源站,从而降低了回源带宽、回源时间而提升了性能。当然,阿里云全站加速平台也支持客户通过自定义的方案,很方便的自行定义实动静态内容加速规则。
此外,针对静态内容,阿里云全站加速还可以通过智能压缩功,自动对静态文件进行Gzip压缩,以及通过页面优化,对当前域名下所有HTML页面中冗余的注释和重复的空白符进行优化,以减小传输文件大小,减少流量支出和提升加速分发效率。
第四、智能路由对于无法缓存或者不允许缓存的内容,最核心的处理逻辑是实现路径加速,阿里云的路径加速是通过智能路由来实现的,在广泛覆盖的节点之间,通过实时探测通信网络质量,并根据探测的质量,进行路径的有效分析,同时结合阿里达摩院的最佳数据计算模型,提供一条从用户的接入点到源站之间一个最优的路径,实现最好的加速效果。
阿里云全站加速产品除了通过以上的策略提供了优秀的加速效果之外,还提供了下面的相关的功能模块让客户的服务可以变得更灵活和更健壮,以及还提供了更广泛和新颖的加速模式:
第一、源站策略1、智能回源策略全站加速除了提供最优链路回源来保证最好的服务质量的同时,还提供了丰富的回源策略管理。很多客户的场景处于安全和自身业务的需求,往往会有一些复杂的策略需要CDN来适配。目前全站加速在回源策略方面具有丰富的功能。
2、灵活回源配置和重试容灾策略阿里云全站加速提供了灵活的回源配置策略,可以根据需求配置多主源(可设置不同的回源权重)、主备源,源站可以使用IP和域名。
网络情况瞬息万变,连接抖动和拥堵时常发生,在长链路传输时,情况会更加严峻。通常情况下在回源阶段,因为链路的加长,整体的网络可控性降低。经常会遇到回源节点的机房网络有问题,回源的某条运营商链路断了等等相关的问题。
结合阿里云全站加速的提供了多种回源配置以及回源重试容灾策略,可以避免单源站问题、源站单IP问题、源站偶发不通等问题,给业务提供更健壮的一个支撑。
3、 WaitingRoom回源方案在回源的时候,有的时候会面临一种场景,就是某次突发活动请求的压力非常大,举个例子,比如在春运火车票购票的时候,请求的压力可能是平时压力的上百倍,短时间之内没有办法扩容这么大倍数能力的源站来解决请求问题的(短时间内扩容源站上百倍的能力,会面临到很大的成本压力以及很长的时间周期问题)。针对上面这种场景,全站加速提供了WaitingRoom解决方案,可以灵活根据请求的URL、配置的回源比例、排队时长,实现突发情况下有序的回源,保证源站服务稳定性。
第二、全链路https加速我们知道HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTPS协议是以安全为目标的HTTP通道, HTTPS提供了身份验证与加密通讯方法,被广泛用于网上安全敏感的通讯,例如交易支付、金融应用、API接口、政务信息等。
通过阿里云全站加速的控制台,可快速开启HTTPS协议,实现客户端和全站加速之间请求的HTTPS加密,保障数据传输的安全性,防止HTTP明文传输中的被窃听、篡改、冒充和劫持风险。
目前主流浏览器已将HTTP协议标识为不安全,若坚持使用HTTP协议,除了安全会埋下隐患外,终端客户在访问网站时出现的不安全标识,也将影响访问。
第三、WebSocket加速WebSocket协议是基于TCP的一种新的网络协议。实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端。在WebSocket中,浏览器和服务器只需要完成一次握手,两者之间创建持久性的连接,进行双向数据传输,客户端和服务器之间的数据交换变得更加简单高效。
阿里云的全站加速产品也提供了对应的Websocket加速模式,通过阿里云全站加速的Websocket, 可在视频弹幕、在线教育笔记大纲等信息推送、股票、金融产品实时报价、体育实况更新、视频会议和聊天、基于位置的应用等场景中使用,能偶更好的节省服务器资源和带宽,并且能够更实时高效地进行通讯。
第四、IP应用加速IP应用加速旨在提供非标准HTTP协议用户,特别是四层私有协议服务场景下,如金融类、游戏类、语音交互类等客户提供网络传输加速,降低服务的延迟和提升访问的可用性。
阿里云IP应用加速可以提供:私有协议做传输控制、智能选路优化网络层、源站透传、业务透明转发无任何侵入,可以通过IP应用加速灵活的使用TCP、UDP等相关协议做业务的传输。
通过以上的了解,我们可以看到通过阿里云全站加速产品,可以有效的提升网站(APP)加速性能和用户体验。在更多的业务搬到线上的时代,全站加速为游戏、在线教育、互联网媒体、金融、商等行业中的数字化应用提供了更优的加速方案。如果您的业务中有全站加速的需求,可以通过阿里云官网、工单、服务群等方式进行了解和反馈。
比Python,JavaScript才是更适合写爬虫的语言。原因有如下三个方面:
一、任务:爬取用户在Github上的repo信息
通过实例的方式学习爬虫是最好的方法,先定一个小目标:爬取github repo信息。入口URL如下,我们只需要一直点击next按钮就能够遍历到用户的所有repo。
https://github.com/{{username}}?tab=repositories
获取repo之后,可以做什么?
二、爬虫双股剑:axios和jQuery
axios是JavaScript中很常用的异步网络请求库,相比jQuery,它更轻量、更专业。既能够用于浏览器端,也可以用于Node。它的语法风格是promise形式的。在本任务中,只需要了解如下用法就足够了:
axios.get(url).then((resp) => { 请求成功,处理resp.data中的html数据 }).catch((err) => { 请求失败,错误处理 })
请求之后需要处理回复结果,处理回复结果的库当然是用jQuery。实际上,我们有更好的选择:cheerio。
在node下,使用jQuery,需要使用jsdom库模拟一个window对象,这种方法效率较低,四个字形容就是:笨重稳妥。
如下代码使用jQuery解析haha.html文件
fs = require("fs") jquery=require('jquery') jsdom=require('jsdom') //fs.readFileSync()返回结果是一个buffer,相当于byte[] html = fs.readFileSync('haha.html').toString('utf8') dom= new jsdom.JSDOM(html) $=jquery(dom.window) console.log($('h1'))
cheerio只实现了jQuery中的DOM部分,相当于jQuery的一个子集。cheerio的语法和jQuery完全一致,在使用cheerio时,几乎感觉不到它和jQuery的差异。在解析HTML方面,毫无疑问,cheerio是更好的选择。如下代码使用cheerio解析haha.html文件。
cheerio=require('cheerio') html=require('fs').readFileSync("haha.html").toString('utf8') $=cheerio.load(html) console.log($('h1'))
只需20余行,便可实现简单的github爬虫,此爬虫只爬取了一页repo列表。
var axios = require("axios") var cheerio = require("cheerio") axios.get("https://github.com/weiyinfu?tab=repositories").then(resp => { var $ = cheerio.load(resp.data) var lis = $("#user-repositories-list li") var repos = [] for (var i = 0; i < lis.length; i++) { var li = lis.eq(i) var repo = { repoName: li.find("h3").text().trim(), repoUrl: li.find("h3 a").attr("href").trim(), repoDesc: li.find("p").text().trim(), language: li.find("[itemprop=programmingLanguage]").text().trim(), star: li.find(".muted-link.mr-3").eq(0).text().trim(), fork: li.find(".muted-link.mr-3").eq(1).text().trim(), forkedFrom: li.find(".f6.text-gray.mb-1 a").text().trim() } repos.push(repo) } console.log(repos) })
三、更丰富的功能
爬虫不是目的,而是达成目的的一种手段。获取数据也不是目的,从数据中提取统计信息并呈现给人才是最终目的。
在github爬虫的基础上,我们可以扩展出更加丰富的功能:使用echarts等图表展示结果。
要想让更多人使用此爬虫工具获取自己的github统计信息,就需要将做成一个网站的形式,通过搜索页面输入用户名,启动爬虫立即爬取github信息,然后使用echarts进行统计展示。网站肯定也要用js作为后端,这样才能和js爬虫无缝衔接,不然还要考虑跨语言调用。js后端有两大web框架express和koa,二者API非常相似,并无优劣之分,但express更加流行。
如上设计有一处用户体验不佳的地方:当启动爬虫爬取github信息时,用户可能需要等待好几秒,这个过程不能让用户干等着。一种解决思路是:让用户看到爬虫爬取的进度或者爬取过程。可以通过websocket向用户推送爬取过程信息并在前端进行展示。展示时,使用类似控制台的界面进行展示。
如何存储爬取到的数据呢?使用MongoDB或者文件都可以,最好实现两种存储方式,让系统的存储方式变得可配置。使用MongoDB时,用到js中的连接池框架generic-pool。
整个项目用到的库包括:
试用地址:
https://weiyinfu.cn/githubstatistic/search.html
案例地址:https://github.com/weiyinfu/GithubStatistic
原文链接:https://zhuanlan.zhihu.com/p/53763115
*请认真填写需求信息,我们会在24小时内与您取得联系。