自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于TypeScript從0到1搭建一款爬蟲(chóng)工具

系統(tǒng)
今天,我們將使用TS這門語(yǔ)言搭建一款爬蟲(chóng)工具。

[[378604]]

前言

今天,我們將使用TS這門語(yǔ)言搭建一款爬蟲(chóng)工具。目標(biāo)網(wǎng)址是什么呢?我們?nèi)ド暇W(wǎng)一搜,經(jīng)過(guò)幾番排查之后,我們選定了這一個(gè)網(wǎng)站。

https://www.hanju.run/

一個(gè)視頻網(wǎng)站,我們的目的主要是爬取這個(gè)網(wǎng)站上視頻的播放鏈接。下面,我們就開(kāi)始進(jìn)行第一步。

第一步

俗話說(shuō),萬(wàn)事開(kāi)頭難。不過(guò)對(duì)于這個(gè)項(xiàng)目而言,恰恰相反。你需要做以下幾個(gè)事情:

1.我們需要?jiǎng)?chuàng)建一個(gè)項(xiàng)目文件夾

2.鍵入命令,初始化項(xiàng)目

  1. npm init -y 

3.局部安裝typescript

  1. npm install typescript -D 

4.接著鍵入命令,生成ts配置文件

  1. tsc --init 

5.局部安裝ts-node,用于命令行輸出命令

  1. npm install -D ts-node 

6.在項(xiàng)目文件夾中創(chuàng)建一個(gè)src文件夾

然后我們?cè)趕rc文件夾中創(chuàng)建一個(gè)crawler.ts文件。

7.在package.json文件中修改快捷啟動(dòng)命令

  1. "scripts": { 
  2.     "dev-t""ts-node ./src/crawler.ts" 
  3.   } 

第二步

接下來(lái),我們將進(jìn)行實(shí)戰(zhàn)操作,也就是上文中crawler.ts文件是我們的主戰(zhàn)場(chǎng)。

我們首先需要引用的這幾個(gè)依賴,分別是

  1. import superagent from "superagent"
  2. import cheerio from "cheerio"
  3. import fs from "fs"
  4. import path from "path"

所以,我們會(huì)這樣安裝依賴:

superagent作用是獲取遠(yuǎn)程網(wǎng)址html的內(nèi)容。

  1. npm install superagent 

cheerio作用是可以通過(guò)jQ語(yǔ)法獲取頁(yè)面節(jié)點(diǎn)的內(nèi)容。

  1. npm install cheerio 

剩余兩個(gè)依賴fs,path。它們是node內(nèi)置依賴,直接引入即可。

我們完成了安裝依賴,但是會(huì)發(fā)現(xiàn)你安裝的依賴上會(huì)有紅色報(bào)錯(cuò)。原因是這樣的,superagent和cheerio內(nèi)部都是用JS寫的,并不是TS寫的,而我們現(xiàn)在的環(huán)境是TS。所以我們需要翻譯一下,我們將這種翻譯文件又稱類型定義文件(以.d.ts為后綴)。我們可以使用以下命令安裝類型定義文件。

  1. npm install -D @types/superagent 

  1. npm install -D @types/cheerio 

接下來(lái),我們就認(rèn)認(rèn)真真看源碼了。

1.安裝完兩個(gè)依賴后,我們需要?jiǎng)?chuàng)建一個(gè)Crawler類,并且將其實(shí)例化。

  1. import superagent from "superagent"
  2. import cheerio from "cheerio"
  3. import fs from "fs"
  4. import path from "path"
  5.  
  6. class Crawler { 
  7.   constructor() { 
  8.      
  9.   } 
  10.  
  11. const crawler = new Crawler(); 

2.我們確定下要爬取的網(wǎng)址,然后賦給一個(gè)私有變量。最后我們會(huì)封裝一個(gè)getRawHtml方法來(lái)獲取對(duì)應(yīng)網(wǎng)址的內(nèi)容。

getRawHtml方法中我們使用了async/await關(guān)鍵字,主要用于異步獲取頁(yè)面內(nèi)容,然后返回值。

  1. import superagent from "superagent"
  2. import cheerio from "cheerio"
  3. import fs from "fs"
  4. import path from "path"
  5.  
  6. class Crawler { 
  7.   private url = "https://www.hanju.run/play/39221-4-0.html"
  8.  
  9.   async getRawHtml() { 
  10.     const result = await superagent.get(this.url); 
  11.     return result.text; 
  12.   } 
  13.  
  14.   async initSpiderProcess() { 
  15.     const html = await this.getRawHtml(); 
  16.   } 
  17.  
  18.   constructor() { 
  19.     this.initSpiderProcess(); 
  20.   } 
  21.  
  22. const crawler = new Crawler(); 

3.使用cheerio依賴內(nèi)置的方法獲取對(duì)應(yīng)的節(jié)點(diǎn)內(nèi)容。

我們通過(guò)getRawHtml方法異步獲取網(wǎng)頁(yè)的內(nèi)容,然后我們傳給getJsonInfo這個(gè)方法,注意是string類型。我們這里通過(guò)cheerio.load(html)這條語(yǔ)句處理,就可以通過(guò)jQ語(yǔ)法來(lái)獲取對(duì)應(yīng)的節(jié)點(diǎn)內(nèi)容。我們獲取到了網(wǎng)頁(yè)中視頻的標(biāo)題以及鏈接,通過(guò)鍵值對(duì)的方式添加到一個(gè)對(duì)象中。注:我們?cè)谶@里定義了一個(gè)接口,定義鍵值對(duì)的類型。

  1. import superagent from "superagent"
  2. import cheerio from "cheerio"
  3. import fs from "fs"
  4. import path from "path"
  5.  
  6. interface Info { 
  7.   name: string; 
  8.   url: string; 
  9.  
  10. class Crawler { 
  11.   private url = "https://www.hanju.run/play/39221-4-0.html"
  12.  
  13.   getJsonInfo(html: string) { 
  14.     const $ = cheerio.load(html); 
  15.     const info: Info[] = []; 
  16.     const scpt: string = String($(".play>script:nth-child(1)").html()); 
  17.     const url = unescape( 
  18.       scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "") 
  19.     ); 
  20.     const name: string = String($("title").html()); 
  21.     info.push({ 
  22.       name
  23.       url, 
  24.     }); 
  25.     const result = { 
  26.       time: new Date().getTime(), 
  27.       data: info, 
  28.     }; 
  29.     return result; 
  30.   } 
  31.  
  32.   async getRawHtml() { 
  33.     const result = await superagent.get(this.url); 
  34.     return result.text; 
  35.   } 
  36.  
  37.   async initSpiderProcess() { 
  38.     const html = await this.getRawHtml(); 
  39.     const info = this.getJsonInfo(html); 
  40.   } 
  41.  
  42.   constructor() { 
  43.     this.initSpiderProcess(); 
  44.   } 
  45.  
  46. const crawler = new Crawler(); 

4.我們首先要在項(xiàng)目根目錄下創(chuàng)建一個(gè)data文件夾。然后我們將獲取的內(nèi)容我們存入文件夾內(nèi)的url.json文件(文件自動(dòng)生成)中。

我們將其封裝成getJsonContent方法,在這里我們使用了path.resolve來(lái)獲取文件的路徑。fs.readFileSync來(lái)讀取文件內(nèi)容,fs.writeFileSync來(lái)將內(nèi)容寫入文件。注:我們分別定義了兩個(gè)接口objJson與InfoResult。

  1. import superagent from "superagent"
  2. import cheerio from "cheerio"
  3. import fs from "fs"
  4. import path from "path"
  5.  
  6. interface objJson { 
  7.   [propName: number]: Info[]; 
  8.  
  9. interface Info { 
  10.   name: string; 
  11.   url: string; 
  12.  
  13. interface InfoResult { 
  14.   time: number; 
  15.   data: Info[]; 
  16.  
  17. class Crawler { 
  18.   private url = "https://www.hanju.run/play/39221-4-0.html"
  19.  
  20.   getJsonInfo(html: string) { 
  21.     const $ = cheerio.load(html); 
  22.     const info: Info[] = []; 
  23.     const scpt: string = String($(".play>script:nth-child(1)").html()); 
  24.     const url = unescape( 
  25.       scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "") 
  26.     ); 
  27.     const name: string = String($("title").html()); 
  28.     info.push({ 
  29.       name
  30.       url, 
  31.     }); 
  32.     const result = { 
  33.       time: new Date().getTime(), 
  34.       data: info, 
  35.     }; 
  36.     return result; 
  37.   } 
  38.  
  39.   async getRawHtml() { 
  40.     const result = await superagent.get(this.url); 
  41.     return result.text; 
  42.   } 
  43.  
  44.   getJsonContent(info: InfoResult) { 
  45.     const filePath = path.resolve(__dirname, "../data/url.json"); 
  46.     let fileContent: objJson = {}; 
  47.     if (fs.existsSync(filePath)) { 
  48.       fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8")); 
  49.     } 
  50.     fileContent[info.time] = info.data; 
  51.     fs.writeFileSync(filePath, JSON.stringify(fileContent)); 
  52.   } 
  53.  
  54.   async initSpiderProcess() { 
  55.     const html = await this.getRawHtml(); 
  56.     const info = this.getJsonInfo(html); 
  57.     this.getJsonContent(info); 
  58.   } 
  59.  
  60.   constructor() { 
  61.     this.initSpiderProcess(); 
  62.   } 
  63.  
  64. const crawler = new Crawler(); 

5.運(yùn)行命令

  1. npm run dev-t 

6.查看生成文件的效果

  1.   "1610738046569": [ 
  2.     { 
  3.       "name""《復(fù)仇者聯(lián)盟4:終局之戰(zhàn)》HD1080P中字m3u8在線觀看-韓劇網(wǎng)"
  4.       "url""https://wuxian.xueyou-kuyun.com/20190728/16820_302c7858/index.m3u8" 
  5.     } 
  6.   ], 
  7.   "1610738872042": [ 
  8.     { 
  9.       "name""《鋼鐵俠2》HD高清m3u8在線觀看-韓劇網(wǎng)"
  10.       "url""https://www.yxlmbbs.com:65/20190920/54uIR9hI/index.m3u8" 
  11.     } 
  12.   ], 
  13.   "1610739069969": [ 
  14.     { 
  15.       "name""《鋼鐵俠2》中英特效m3u8在線觀看-韓劇網(wǎng)"
  16.       "url""https://tv.youkutv.cc/2019/11/12/mjkHyHycfh0LyS4r/playlist.m3u8" 
  17.     } 
  18.   ] 

準(zhǔn)結(jié)語(yǔ)

到這里真的結(jié)束了嗎?

不!不!不!

真的沒(méi)有結(jié)束。

我們會(huì)看到上面一坨代碼,真的很臭~(yú)

我們將分別使用組合模式與單例模式將其優(yōu)化。

優(yōu)化一:組合模式

組合模式(Composite Pattern),又叫部分整體模式,是用于把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象。組合模式依據(jù)樹(shù)形結(jié)構(gòu)來(lái)組合對(duì)象,用來(lái)表示部分以及整體層次。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它創(chuàng)建了對(duì)象組的樹(shù)形結(jié)構(gòu)。

這種模式創(chuàng)建了一個(gè)包含自己對(duì)象組的類。該類提供了修改相同對(duì)象組的方式。

簡(jiǎn)言之,就是可以像處理簡(jiǎn)單元素一樣來(lái)處理復(fù)雜元素。

首先,我們?cè)趕rc文件夾下創(chuàng)建一個(gè)combination文件夾,然后在其文件夾下分別在創(chuàng)建兩個(gè)文件crawler.ts和urlAnalyzer.ts。

crawler.ts

crawler.ts文件的作用主要是處理獲取頁(yè)面內(nèi)容以及存入文件內(nèi)。

  1. import superagent from "superagent"
  2. import fs from "fs"
  3. import path from "path"
  4. import UrlAnalyzer from "./urlAnalyzer.ts"
  5.  
  6. export interface Analyzer { 
  7.   analyze: (html: string, filePath: string) => string; 
  8.  
  9. class Crowller { 
  10.   private filePath = path.resolve(__dirname, "../../data/url.json"); 
  11.  
  12.   async getRawHtml() { 
  13.     const result = await superagent.get(this.url); 
  14.     return result.text; 
  15.   } 
  16.  
  17.   writeFile(content: string) { 
  18.     fs.writeFileSync(this.filePath, content); 
  19.   } 
  20.  
  21.   async initSpiderProcess() { 
  22.     const html = await this.getRawHtml(); 
  23.     const fileContent = this.analyzer.analyze(html, this.filePath); 
  24.     this.writeFile(fileContent); 
  25.   } 
  26.  
  27.   constructor(private analyzer: Analyzer, private url: string) { 
  28.     this.initSpiderProcess(); 
  29.   } 
  30. const url = "https://www.hanju.run/play/39257-1-1.html"
  31.  
  32. const analyzer = new UrlAnalyzer(); 
  33. new Crowller(analyzer, url); 

urlAnalyzer.ts

urlAnalyzer.ts文件的作用主要是處理獲取頁(yè)面節(jié)點(diǎn)內(nèi)容的具體邏輯。

  1. import cheerio from "cheerio"
  2. import fs from "fs"
  3. import { Analyzer } from "./crawler.ts"
  4.  
  5. interface objJson { 
  6.   [propName: number]: Info[]; 
  7. interface InfoResult { 
  8.   time: number; 
  9.   data: Info[]; 
  10. interface Info { 
  11.   name: string; 
  12.   url: string; 
  13.  
  14. export default class UrlAnalyzer implements Analyzer { 
  15.   private getJsonInfo(html: string) { 
  16.     const $ = cheerio.load(html); 
  17.     const info: Info[] = []; 
  18.     const scpt: string = String($(".play>script:nth-child(1)").html()); 
  19.     const url = unescape( 
  20.       scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "") 
  21.     ); 
  22.     const name: string = String($("title").html()); 
  23.     info.push({ 
  24.       name
  25.       url, 
  26.     }); 
  27.     const result = { 
  28.       time: new Date().getTime(), 
  29.       data: info, 
  30.     }; 
  31.     return result; 
  32.   } 
  33.  
  34.   private getJsonContent(info: InfoResult, filePath: string) { 
  35.     let fileContent: objJson = {}; 
  36.     if (fs.existsSync(filePath)) { 
  37.       fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8")); 
  38.     } 
  39.     fileContent[info.time] = info.data; 
  40.     return fileContent; 
  41.   } 
  42.  
  43.   public analyze(html: string, filePath: string) { 
  44.     const info = this.getJsonInfo(html); 
  45.     console.log(info); 
  46.     const fileContent = this.getJsonContent(info, filePath); 
  47.     return JSON.stringify(fileContent); 
  48.   } 

可以在package.json文件中定義快捷啟動(dòng)命令。

  1. "scripts": { 
  2.   "dev-c""ts-node ./src/combination/crawler.ts" 
  3. }, 

然后使用npm run dev-c啟動(dòng)即可。

優(yōu)化二:?jiǎn)卫J?/span>

**單例模式(Singleton Pattern)**是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪問(wèn),不需要實(shí)例化該類的對(duì)象。

應(yīng)用實(shí)例:

  • 1、一個(gè)班級(jí)只有一個(gè)班主任。
  • 2、Windows 是多進(jìn)程多線程的,在操作一個(gè)文件的時(shí)候,就不可避免地出現(xiàn)多個(gè)進(jìn)程或線程同時(shí)操作一個(gè)文件的現(xiàn)象,所以所有文件的處理必須通過(guò)唯一的實(shí)例來(lái)進(jìn)行。
  • 3、一些設(shè)備管理器常常設(shè)計(jì)為單例模式,比如一個(gè)電腦有兩臺(tái)打印機(jī),在輸出的時(shí)候就要處理不能兩臺(tái)打印機(jī)打印同一個(gè)文件。

同樣,我們?cè)趕rc文件夾下創(chuàng)建一個(gè)singleton文件夾,然后在其文件夾下分別在創(chuàng)建兩個(gè)文件crawler1.ts和urlAnalyzer.ts。

這兩個(gè)文件的作用與上文同樣,只不過(guò)代碼書(shū)寫不一樣。

crawler1.ts

  1. import superagent from "superagent"
  2. import fs from "fs"
  3. import path from "path"
  4. import UrlAnalyzer from "./urlAnalyzer.ts"
  5.  
  6. export interface Analyzer { 
  7.   analyze: (html: string, filePath: string) => string; 
  8.  
  9. class Crowller { 
  10.   private filePath = path.resolve(__dirname, "../../data/url.json"); 
  11.  
  12.   async getRawHtml() { 
  13.     const result = await superagent.get(this.url); 
  14.     return result.text; 
  15.   } 
  16.  
  17.   private writeFile(content: string) { 
  18.     fs.writeFileSync(this.filePath, content); 
  19.   } 
  20.  
  21.   private async initSpiderProcess() { 
  22.     const html = await this.getRawHtml(); 
  23.     const fileContent = this.analyzer.analyze(html, this.filePath); 
  24.     this.writeFile(JSON.stringify(fileContent)); 
  25.   } 
  26.  
  27.   constructor(private analyzer: Analyzer, private url: string) { 
  28.     this.initSpiderProcess(); 
  29.   } 
  30. const url = "https://www.hanju.run/play/39257-1-1.html"
  31.  
  32. const analyzer = UrlAnalyzer.getInstance(); 
  33. new Crowller(analyzer, url); 

urlAnalyzer.ts

  1. import cheerio from "cheerio"
  2. import fs from "fs"
  3. import { Analyzer } from "./crawler1.ts"
  4.  
  5. interface objJson { 
  6.   [propName: number]: Info[]; 
  7. interface InfoResult { 
  8.   time: number; 
  9.   data: Info[]; 
  10. interface Info { 
  11.   name: string; 
  12.   url: string; 
  13. export default class UrlAnalyzer implements Analyzer { 
  14.   static instance: UrlAnalyzer; 
  15.  
  16.   static getInstance() { 
  17.     if (!UrlAnalyzer.instance) { 
  18.       UrlAnalyzer.instance = new UrlAnalyzer(); 
  19.     } 
  20.     return UrlAnalyzer.instance; 
  21.   } 
  22.  
  23.   private getJsonInfo(html: string) { 
  24.     const $ = cheerio.load(html); 
  25.     const info: Info[] = []; 
  26.     const scpt: string = String($(".play>script:nth-child(1)").html()); 
  27.     const url = unescape( 
  28.       scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "") 
  29.     ); 
  30.     const name: string = String($("title").html()); 
  31.     info.push({ 
  32.       name
  33.       url, 
  34.     }); 
  35.     const result = { 
  36.       time: new Date().getTime(), 
  37.       data: info, 
  38.     }; 
  39.     return result; 
  40.   } 
  41.  
  42.   private getJsonContent(info: InfoResult, filePath: string) { 
  43.     let fileContent: objJson = {}; 
  44.     if (fs.existsSync(filePath)) { 
  45.       fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8")); 
  46.     } 
  47.     fileContent[info.time] = info.data; 
  48.     return fileContent; 
  49.   } 
  50.  
  51.   public analyze(html: string, filePath: string) { 
  52.      const info = this.getJsonInfo(html); 
  53.      console.log(info); 
  54.     const fileContent = this.getJsonContent(info, filePath); 
  55.     return JSON.stringify(fileContent); 
  56.   } 
  57.  
  58.   private constructor() {} 

可以在package.json文件中定義快捷啟動(dòng)命令。

  1. "scripts": { 
  2.     "dev-s""ts-node ./src/singleton/crawler1.ts"
  3.  }, 

然后使用npm run dev-s啟動(dòng)即可。

結(jié)語(yǔ)

這下真的結(jié)束了,謝謝閱讀。希望可以幫到你。

完整源碼地址:

https://github.com/maomincoding/TsCrawler

 

責(zé)任編輯:姜華 來(lái)源: 前端歷劫之路
相關(guān)推薦

2022-01-27 13:02:46

前端爬蟲(chóng)工具

2021-02-20 07:02:24

Vue.js組件開(kāi)發(fā)技術(shù)

2021-09-26 05:00:11

Vscode插件

2020-12-29 05:26:27

視頻播放器Vuevideo

2021-03-30 07:11:22

Vue3parcel-vue-工具

2023-03-06 11:35:55

經(jīng)營(yíng)分析體系

2023-05-10 10:45:06

開(kāi)源工具庫(kù)項(xiàng)目

2018-01-16 12:31:33

Python爬蟲(chóng)數(shù)據(jù)

2022-03-15 11:51:00

決策分析模型

2019-07-31 10:18:17

Web 開(kāi)發(fā)Python

2017-05-27 09:23:10

IOS框架APP框架代碼

2022-06-13 07:02:02

Zadig平臺(tái)自動(dòng)化

2022-04-07 10:02:58

前端檢測(cè)工具

2022-05-26 17:40:51

Linux開(kāi)源

2011-08-31 10:18:09

Template St

2016-11-28 16:23:23

戴爾

2023-11-15 08:14:35

2022-05-09 08:35:43

面試產(chǎn)品互聯(lián)網(wǎng)

2022-08-25 14:41:51

集群搭建

2016-01-14 13:07:20

美團(tuán)壓測(cè)工具工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)