当前位置: 首页 > 科技观察

基于TypeScript从0到1搭建一个爬虫工具

时间:2023-03-20 13:42:31 科技观察

前言今天我们将使用TS语言搭建一个爬虫工具。目标网址是什么?我们在网上搜索,几经考察,选择了这个网站。https://www.hanju.run/是一个视频网站,我们的主要目的是抓取该网站视频的播放链接。接下来,我们开始第一步。第一步俗话说,万事开头难。但是对于这个项目,情况恰恰相反。您需要做以下事情:1.我们需要创建一个项目文件夹2.输入命令来初始化项目npminit-y3。部分安装typescriptnpminstalltypescript-D4。然后输入命令生成ts配置文件tsc--init5。部分安装ts-node,用于命令行输出命令npminstall-Dts-node6。在项目文件夹中创建一个src文件夹,然后我们在src文件夹中创建一个crawler.ts文件。7.修改package.json文件中的快捷启动命令"scripts":{"dev-t":"ts-node./src/crawler.ts"}Step2接下来我们进行实际操作,即上面的crawler.ts文件是我们的主战场。我们需要引用的前几个依赖项是importsuperagentfrom"superagent";从“cheerio”导入cheerio;从“fs”导入fs;从“路径”导入路径;所以,我们会这样安装依赖:superagent的作用是获取远程urlhtml的内容。npminstallsuperagentcheerio的作用是通过jQ语法获取页面节点的内容。npminstallcheerio剩下的两个依赖是fs和path。它们是node内置的依赖,可以直接导入。我们已经完成了依赖的安装,但是我们会发现你安装的依赖会出现红色错误。原因是这样的,superagent和cheerio都是用JS写的,不是TS,我们现在的环境是TS。所以我们需要对其进行翻译,我们称这个翻译文件也称为类型定义文件(后缀为.d.ts)。我们可以使用以下命令安装类型定义文件。npminstall-D@types/superagentnpminstall-D@types/cheerio接下来我们就认真的看一下源码。1、安装好两个依赖后,我们需要创建一个Crawler类并实例化。importsuperagentfrom“superagent”;importcheeriofrom“cheerio”;importfsfrom“fs”;importpathfrom“path”;classCrawler{constructor(){}}constcrawler=newCrawler();2。我们确定要抓取的URL,然后将其赋值给一个私有变量。最后我们会封装一个getRawHtml方法来获取对应URL的内容。在getRawHtml方法中,我们使用了async/await关键字,主要用于异步获取页面内容,然后返回值。importsuperagentfrom“superagent”;importcheeriofrom“cheerio”;importfsfrom“fs”;importpathfrom“path”;classCrawler{privateurl="https://www.hanju.run/play/39221-4-0.html";asyncgetRawHtml(){constresult=awaitsuperagent.get(this.url);returnresult.text;}asyncinitSpiderProcess(){consthtml=awaitthis.getRawHtml();}constructor(){this.initSpiderProcess();}}constcrawler=newCrawler();3.使用cheerio依赖内置方法获取对应的节点内容。我们通过getRawHtml方法异步获取网页的内容,然后我们将其传递给getJsonInfo方法,注意是字符串类型。这里我们使用语句cheerio.load(html)通过jQ语法获取对应的节点内容。我们拿到了网页上视频的标题和链接,通过键值对的方式添加到一个对象中。注意:我们这里定义一个接口来定义键值对的类型。importsuperagentfrom“superagent”;importcheeriofrom“cheerio”;importfsfrom“fs”;importpathfrom“path”;interfaceInfo{name:string;url:string;}classCrawler{privateurl="https://www.hanju.run/play/39221-4-0.html";getJsonInfo(html:string){const$=cheerio.load(html);constinfo:Info[]=[];constscpt:string=String($(".play>script:nth-child(1)").html());consturl=unescape(scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g,""));constname:string=String($("title").html());info.push({name,url,});constresult={time:newDate().getTime(),data:info,};returnresult;}asyncgetRawHtml(){constresult=awaitsuperagent.get(this.url);returnresult.text;}asyncinitSpiderProcess(){consthtml=awaitthis.getRawHtml();constinfo=this.getJsonInfo(html);}constructor(){this.initSpiderProcess();}}constcrawler=newCrawler();4.我们首先要在项目根目录下创建一个数据文件夹。然后我们将获取到的内容存放在文件夹中的url.json文件中(该文件是自动生成的)。我们将其封装到getJsonContent方法中,这里使用path.resolve获取文件的路径。fs.readFileSync读取文件内容,fs.writeFileSync将内容写入文件。注意:我们分别定义了两个接口objJson和InfoResult。importsuperagentfrom“superagent”;importcheeriofrom“cheerio”;importfsfrom“fs”;importpathfrom“path”;interfaceobjJson{[propName:number]:Info[];}interfaceInfo{name:string;url:string;}interfaceInfoResult{time:number;数据:信息[];}classCrawler{privateurl="https://www.hanju.run/play/39221-4-0.html";getJsonInfo(html:string){const$=cheerio.load(html);constinfo:Info[]=[];constscpt:string=String($(".play>script:nth-child(1)").html());consturl=unescape(scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g,""));constname:string=String($("title").html());info.push({name,url,});constresult={time:newDate().getTime(),data:info,};returnresult;}asyncgetRawHtml(){constresult=awaitsuperagent.get(this.url);returnresult.text;}getJsonContent(info:InfoResult){constfilePath=path.resolve(__dirname,"../data/url.json");letfileContent:objJson={};if(fs.existsSync(filePath)){fileContent=JSON.parse(fs.readFileSync(filePath,"utf-8"));}fileContent[info.time]=info.data;fs.writeFileSync(filePath,JSON.stringify(fileContent));}asyncinitSpiderProcess(){consthtml=awaitthis.getRawHtml();constinfo=this.getJsonInfo(html);this.getJsonContent(info);}constructor(){this.initSpiderProcess();}}constcrawler=newCrawler();5.运行命令npmrundev-t6。查看生成文件效果{"1610738046569":[{"name":"《复仇者联盟4:终局之战》HD1080P中文m3u8在线观看-韩剧网","url":"https://wuxian.xueyou-kuyun.com/20190728/16820_302c7858/index.m3u8"}],"1610738872042":[{"name":"《钢铁侠2》HD高清m3u8在线观看-韩剧网","url":"https://www.yxlmbbs.com:65/20190920/54uIR9hI/index.m3u8"}],"1610739069969":[{"name":"《钢铁侠2》中英特效m3u8在线观看-韩剧网","url":"https://tv.youkutv.cc/2019/11/12/mjkHyHycfh0LyS4r/playlist.m3u8"}]}准尾声真的到这里了吗?不!不!不!真的没完我们会看到上面一堆代码,真的很臭~我们会使用组合模式和单例模式来优化它。优化一:复合模式复合模式也称为整体模式的一部分,用于将一组相似的对象当作一个单独的对象来对待。Composite模式根据树结构组合对象,用于表示部分和整个层次结构。这种类型的设计模式是一种结构模式,它创建对象组的树结构。此模式创建一个类,该类包含自己的一组对象。此类提供了一种修改相同对象组的方法。简而言之,可以用与简单元素相同的方式处理复杂元素。首先我们在src文件夹下创建一个combination文件夹,然后在该文件夹下创建两个文件crawler.ts和urlAnalyzer.ts。crawler.tscrawler.ts文件的作用主要是对页面内容进行处理,并存储在文件中。importsuperagentfrom"superagent";importfsfrom"fs";importpathfrom"path";importUrlAnalyzerfrom./urlAnalyzer.ts";exportinterfaceAnalyzer{analyze:(html:string,filePath:string)=>string;}classCrowller{privatefilePath=path.resolve(__dirname,"../../data/url.json");asyncgetRawHtml(){constresult=awaitsuperagent.get(this.url);returnresult.text;}writeFile(content:string){fs.writeFileSync(this.filePath,content);}asyncinitSpiderProcess(){consthtml=awaitthis.getRawHtml();constfileContent=this.analyzer.analyze(html,this.filePath);this.writeFile(fileContent);}构造函数(privateanalyzer:Analyzer,privateurl:string){this.initSpiderProcess();}}consturl="https://www.hanju.run/play/39257-1-1.html";constanalyzer=newUrlAnalyzer();newCrowller(analyzer,url);urlAnalyzer.tsurlAnalyzer.ts文件的作用主要是处理获取页面节点内容的附件。importcheeriofrom"cheerio";importfsfrom"fs";import{Analyzer}from./crawler.ts";interfaceobjJson{[propName:number]:Info[];}interfaceInfoResult{time:number;data:Info[];}interfaceInfo{name:string;url:string;}exportdefaultclassUrlAnalyzerimplementsAnalyzer{privategetJsonInfo(html:string){const$=cheerio.load(html);constinfo:Info[]=[];constscpt:string=String($(".play>脚本:nth-??child(1)").html());consturl=unescape(scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g,""));constname:string=String($("title").html());info.push({name,url,});constresult={time:newDate().getTime(),data:info,};returnresult;}privategetJsonContent(info:InfoResult,filePath:string){letfileContent:objJson={};if(fs.existsSync(filePath)){fileContent=JSON.parse(fs.readFileSync(filePath,"utf-8"));}fileContent[info.time]=info.data;returnfileContent;}publicanalyze(html:string,filePath:string){constinfo=this.getJsonInfo(html);console.log(info);constfileContent=this.getJsonContent(info,filePath);returnJSON.stringify(fileContent);}}可以在package.json中文件定义快捷启动命令"scripts":{"dev-c":"ts-node./src/combination/crawler.ts"},然后使用npmrundev-c启动。优化二:单例模式**单例模式(SingletonPattern)**是Java中最简单的设计模式之一。这种类型的设计模式是一种创建模式,它提供了一种创建对象的最佳方式。此模式涉及一个类,该类负责创建自己的对象,同时确保只创建一个对象。此类提供了一种直接访问其唯一对象的方法,而无需实例化此类的对象。应用实例:1.一个班只有一个班主任。2.Windows是多进程多线程的。在操作一个文件时,难免会有多个进程或线程同时操作一个文件,所以所有的文件处理都必须通过一个唯一的实例来进行。3.一些设备管理器往往设计成单例模式。例如,一台计算机有两台打印机。输出时,必须处理两台打印机不能打印同一个文件。同样,我们在src文件夹下创建一个singleton文件夹,然后在该文件夹下创建两个文件crawler1.ts和urlAnalyzer.ts。这两个文件的作用和上面一样,只是代码的写法不同。crawler1.tsimportsuperagentfrom"superagent";importfsfrom"fs";importpathfrom"path";importUrlAnalyzerfrom"./urlAnalyzer.ts";exportinterfaceAnalyzer{analyze:(html:string,filePath:string)=>string;}classCrowller{privatefilePath=path。resolve(__dirname,"../../data/url.json");asyncgetRawHtml(){constresult=awaitsuperagent.get(this.url);returnresult.text;}privatewriteFile(content:string){fs.writeFileSync(this.filePath,content);}privateasyncinitSpiderProcess(){consthtml=awaitthis.getRawHtml();constfileContent=this.analyzer.analyze(html,this.filePath);this.writeFile(JSON.stringify(fileContent));}构造函数(privateanalyzer:Analyzer,privateurl:string){this.initSpiderProcess();}}consturl="https://www.hanju.run/play/39257-1-1.html";constanalyzer=UrlAnalyzer.getInstance();newCrowller(分析器,url);urlAnalyzer.tsimportcheeriofrom“cheerio”;importfsfrom“fs”;导入{Analyzer}from“./crawler1.ts";interfaceobjJson{[propName:number]:Info[];}interfaceInfoResult{time:number;data:Info[];}interfaceInfo{name:string;url:string;}exportdefaultclassUrlAnalyzerimplementsAnalyzer{staticinstance:UrlAnalyzer;staticgetInstance(){if(!UrlAnalyzer.instance){UrlAnalyzer.instance=newUrlAnalyzer();}returnUrlAnalyzer.instance;}privategetJsonInfo(html:string){const$=cheerio.load(html);constinfo:Info[]=[];constscpt:string=String($(".play>script:nth-child(1)").html());consturl=unescape(scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g,""));constname:string=String($("title").html());info.push({name,url,});constresult={time:newDate().getTime(),data:info,};returnresult;}privategetJsonContent(info:InfoResult,filePath:string){letfileContent:objJson={};if(fs.existsSync(filePath)){fileContent=JSON.parse(fs.readFileSync(filePath,"utf-8"));}fileContent[info.time]=info.data;returnfileContent;}publicanalyze(html:string,filePath:string){constinfo=this.getJsonInfo(html);console.log(info);constfileContent=this.getJsonContent(info,filePath);returnJSON.stringify(fileContent);}privateconstructor(){}}可以在package.json文件中定义快捷启动命令"scripts":{"dev-s":"ts-node./src/singleton/crawler1.ts",},然后使用npmrundev-s即可启动。结语到这里就真的结束了,感谢阅读。我希望我能帮助你。完整源码地址:https://github.com/maomincoding/TsCrawler