日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
基于TypeScript/Node從0到1搭建一款爬蟲工具

前言

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

??https://www.hanju.run/ ??

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

第一步

俗話說,萬事開頭難。不過對于這個項目而言,恰恰相反。你需要做以下幾個事情:

  1. 我們需要創(chuàng)建一個項目文件夾
  2. 鍵入命令,初始化項目
npm init -y
  1. 局部安裝typescript
npm install typescript -D
  1. 接著鍵入命令,生成ts配置文件
tsc --init
  1. 局部安裝ts-node,用于命令行輸出命令
npm install -D ts-node
  1. 在項目文件夾中創(chuàng)建一個src文件夾

然后我們在src文件夾中創(chuàng)建一個crawler.ts文件。

  1. 在package.json文件中修改快捷啟動命令
"scripts": {
"dev-t": "ts-node ./src/crawler.ts"
}

第二步

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

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

import superagent from "superagent";
import cheerio from "cheerio";
import fs from "fs";
import path from "path";

所以,我們會這樣安裝依賴:

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

npm install superagent

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

npm install cheerio

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

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

npm install -D @types/superagent
npm install -D @types/cheerio

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

  1. 安裝完兩個依賴后,我們需要創(chuàng)建一個Crawler類,并且將其實例化。
import superagent from "superagent";
import cheerio from "cheerio";
import fs from "fs";
import path from "path";
class Crawler {
constructor() {
}
}
const crawler = new Crawler();
  1. 我們確定下要爬取的網(wǎng)址,然后賦給一個私有變量。最后我們會封裝一個getRawHtml方法來獲取對應(yīng)網(wǎng)址的內(nèi)容。

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

import superagent from "superagent";
import cheerio from "cheerio";
import fs from "fs";
import path from "path";
class Crawler {
private url = "https://www.hanju.run/play/39221-4-0.html";
async getRawHtml() {
const result = await superagent.get(this.url);
return result.text;
}
async initSpiderProcess() {
const html = await this.getRawHtml();
}
constructor() {
this.initSpiderProcess();
}
}
const crawler = new Crawler();
  1. 使用cheerio依賴內(nèi)置的方法獲取對應(yīng)的節(jié)點內(nèi)容。

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

import superagent from "superagent";
import cheerio from "cheerio";
import fs from "fs";
import path from "path";
interface Info {
name: string;
url: string;
}
class Crawler {
private url = "https://www.hanju.run/play/39221-4-0.html";
getJsonInfo(html: string) {
const $ = cheerio.load(html);
const info: Info[] = [];
const scpt: string = String($(".play>script:nth-child(1)").html());
const url = unescape(
scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "")
);
const name: string = String($("title").html());
info.push({
name,
url,
});
const result = {
time: new Date().getTime(),
data: info,
};
return result;
}
async getRawHtml() {
const result = await superagent.get(this.url);
return result.text;
}
async initSpiderProcess() {
const html = await this.getRawHtml();
const info = this.getJsonInfo(html);
}
constructor() {
this.initSpiderProcess();
}
}
const crawler = new Crawler();
  1. 我們首先要在項目根目錄下創(chuàng)建一個data文件夾。然后我們將獲取的內(nèi)容我們存入文件夾內(nèi)的url.json文件(文件自動生成)中。

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

import superagent from "superagent";
import cheerio from "cheerio";
import fs from "fs";
import path from "path";
interface objJson {
[propName: number]: Info[];
}
interface Info {
name: string;
url: string;
}
interface InfoResult {
time: number;
data: Info[];
}
class Crawler {
private url = "https://www.hanju.run/play/39221-4-0.html";
getJsonInfo(html: string) {
const $ = cheerio.load(html);
const info: Info[] = [];
const scpt: string = String($(".play>script:nth-child(1)").html());
const url = unescape(
scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "")
);
const name: string = String($("title").html());
info.push({
name,
url,
});
const result = {
time: new Date().getTime(),
data: info,
};
return result;
}
async getRawHtml() {
const result = await superagent.get(this.url);
return result.text;
}
getJsonContent(info: InfoResult) {
const filePath = path.resolve(__dirname, "../data/url.json");
let fileContent: objJson = {};
if (fs.existsSync(filePath)) {
fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
fileContent[info.time] = info.data;
fs.writeFileSync(filePath, JSON.stringify(fileContent));
}
async initSpiderProcess() {
const html = await this.getRawHtml();
const info = this.getJsonInfo(html);
this.getJsonContent(info);
}
constructor() {
this.initSpiderProcess();
}
}
const crawler = new Crawler();
  1. 運(yùn)行命令   
npm run dev-t
  1. 查看生成文件的效果  
{
"1610738046569": [
{
"name": "《復(fù)仇者聯(lián)盟4:終局之戰(zhàn)》HD1080P中字m3u8在線觀看-韓劇網(wǎng)",

"url": "https://wuxian.xueyou-kuyun.com/20190728/16820_302c7858/index.m3u8"
}
],
"1610738872042": [
{
"name": "《鋼鐵俠2》HD高清m3u8在線觀看-韓劇網(wǎng)",
"url": "https://www.yxlmbbs.com:65/20190920/54uIR9hI/index.m3u8"
}
],
"1610739069969": [
{
"name": "《鋼鐵俠2》中英特效m3u8在線觀看-韓劇網(wǎng)",

"url": "https://tv.youkutv.cc/2019/11/12/mjkHyHycfh0LyS4r/playlist.m3u8"
}
]
}

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

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

不!

不!

不!

真的沒有結(jié)束。

我們會看到上面一坨代碼,真的很臭~

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

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

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

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

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

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

crawler.ts

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

import superagent from "superagent";
import fs from "fs";
import path from "path";
import UrlAnalyzer from "./urlAnalyzer.ts";
export interface Analyzer {
analyze: (html: string, filePath: string) => string;
}
class Crowller {
private filePath = path.resolve(__dirname, "../../data/url.json");
async getRawHtml() {
const result = await superagent.get(this.url);
return result.text;
}
writeFile(content: string) {
fs.writeFileSync(this.filePath, content);
}
async initSpiderProcess() {
const html = await this.getRawHtml();
const fileContent = this.analyzer.analyze(html, this.filePath);
this.writeFile(fileContent);
}
constructor(private analyzer: Analyzer, private url: string) {
this.initSpiderProcess();
}
}
const url = "https://www.hanju.run/play/39257-1-1.html";
const analyzer = new UrlAnalyzer();
new Crowller(analyzer, url);

urlAnalyzer.ts

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

import cheerio from "cheerio";
import fs from "fs";import { Analyzer } from "./crawler.ts";
interface objJson {
[propName: number]: Info[];
}
interface InfoResult {
time: number;
data: Info[];
}
interface Info {
name: string;
url: string;
}
export default class UrlAnalyzer implements Analyzer {
private getJsonInfo(html: string) {
const $ = cheerio.load(html);
const info: Info[] = [];
const scpt: string = String($(".play>script:nth-child(1)").html());
const url = unescape(
scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "")
);
const name: string = String($("title").html());
info.push({
name,
url,
});
const result = {
time: new Date().getTime(),
data: info,
};
return result;
}
private getJsonContent(info: InfoResult, filePath: string) {
let fileContent: objJson = {};
if (fs.existsSync(filePath)) {
fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
fileContent[info.time] = info.data;
return fileContent;
}
public analyze(html: string, filePath: string) {
const info = this.getJsonInfo(html);
console.log(info);
const fileContent = this.getJsonContent(info, filePath);
return JSON.stringify(fileContent);
}
}

可以在package.json文件中定義快捷啟動命令。

然后使用npm run dev-c啟動即可。

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

優(yōu)化二:單例模式

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

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

應(yīng)用實例:

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

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

這兩個文件的作用與上文同樣,只不過代碼書寫不一樣。

crawler1.ts

import superagent from "superagent";
import fs from "fs";
import path from "path";
import UrlAnalyzer from "./urlAnalyzer.ts";
export interface Analyzer {
analyze: (html: string, filePath: string) => string;
}
class Crowller {
private filePath = path.resolve(__dirname, "../../data/url.json");
async getRawHtml() {
const result = await superagent.get(this.url);
return result.text;
}
private writeFile(content: string) {
fs.writeFileSync(this.filePath, content);
}
private async initSpiderProcess() {
const html = await this.getRawHtml();
const fileContent = this.analyzer.analyze(html, this.filePath);
this.writeFile(JSON.stringify(fileContent));
}
constructor(private analyzer: Analyzer, private url: string) {
this.initSpiderProcess();
}
}
const url = "https://www.hanju.run/play/39257-1-1.html";
const analyzer = UrlAnalyzer.getInstance();
new Crowller(analyzer, url);

urlAnalyzer.ts

import cheerio from "cheerio";
import fs from "fs";
import { Analyzer } from "./crawler1.ts";
interface objJson {
[propName: number]: Info[];
}
interface InfoResult {
time: number;
data: Info[];
}
interface Info {
name: string;
url: string;
}
export default class UrlAnalyzer implements Analyzer {
static instance: UrlAnalyzer;
static getInstance() {
if (!UrlAnalyzer.instance) {
UrlAnalyzer.instance = new UrlAnalyzer();
}
return UrlAnalyzer.instance;
}
private getJsonInfo(html: string) {
const $ = cheerio.load(html);
const info: Info[] = [];
const scpt: string = String($(".play>script:nth-child(1)").html());
const url = unescape(
scpt.split(";")[3].split("(")[1].split(")")[0].replace(/\"/g, "")
);
const name: string = String($("title").html());
info.push({
name,
url,
});
const result = {
time: new Date().getTime(),
data: info,
};
return result;
}
private getJsonContent(info: InfoResult, filePath: string) {
let fileContent: objJson = {};
if (fs.existsSync(filePath)) {
fileContent = JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
fileContent[info.time] = info.data;
return fileContent;
}
public analyze(html: string, filePath: string) {
const info = this.getJsonInfo(html);
console.log(info);
const fileContent = this.getJsonContent(info, filePath);
return JSON.stringify(fileContent);
}
private constructor() {}
}

可以在package.json文件中定義快捷啟動命令。

然后使用npm run dev-s啟動即可。

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

結(jié)語

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

完整源碼地址:

??https://github.com/maomincoding/TsCrawler ??


分享名稱:基于TypeScript/Node從0到1搭建一款爬蟲工具
本文地址:http://www.5511xx.com/article/dhsojch.html