用TypeScript開發爬蟲程序

全局安裝typescript:
npm install -g typescript
目前版本2.0.3,這個版本再也不須要使用typings命令了。可是vscode捆綁的版本是1.8的,須要一些配置工做,看本文的處理辦法。
 
測試tsc命令:
tsc
建立要寫的程序項目文件夾:
mkdir test-typescript-spider
進入該文件夾:
cd test-typescript-spider
初始化項目:
npm init
安裝superagent和cheerio模塊:
npm i --save superagent cheerio
安裝對應的類型聲明模塊:
npm i -s @types/superagent --save
npm i -s @types/cheerio --save
安裝項目內的typescript(必須走這一步):
npm i --save typescript
用vscode打開項目文件夾。在該文件夾下建立tsconfig.json文件,並複製如下配置代碼進去:
{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "noEmitOnError": true,
        "noImplicitAny": true,
        "experimentalDecorators": true,
        "sourceMap": false,
     // "sourceRoot": "./",
        "outDir": "./out"
    },
    "exclude": [
        "node_modules"
    ]
}
在vscode打開「文件」-「首選項」-「工做區設置」
在settings.json中加入(若是不作這個配置,vscode會在打開項目的時候提示選擇哪一個版本的typescript):
{
"typescript.tsdk": "node_modules/typescript/lib"
}
 
建立api.ts文件,複製如下代碼進去:
import superagent = require('superagent');
import cheerio = require('cheerio');

export const remote_get = function(url: string) {

    const promise = new Promise<superagent.Response>(function (resolve, reject) {
        superagent.get(url)
            .end(function (err, res) {
                if (!err) {
                    resolve(res);
                } else {
                    console.log(err)
                    reject(err);
                }
            });
    });
    return promise;
}
建立app.ts文件,書寫測試代碼:
import api = require('./api');
const go = async () => {
    let res = await api.remote_get('http://www.baidu.com/');
    console.log(res.text);
}
go();
執行命令:
tsc
而後:
node out/app
觀察輸出是否正確。
 
如今嘗試抓取 http://cnodejs.org/的第一頁文章連接。
修改app.ts文件,代碼以下:
import api = require('./api');
import cheerio = require('cheerio');

const go = async () => {
    const res = await api.remote_get('http://cnodejs.org/');
    const $ = cheerio.load(res.text);
    let urls: string[] = [];
    let titles: string[] = [];
    $('.topic_title_wrapper').each((index, element) => {
        titles.push($(element).find('.topic_title').first().text().trim());
        urls.push('http://cnodejs.org/' + $(element).find('.topic_title').first().attr('href'));
    })
    console.log(titles, urls);
}
go();
觀察輸出,文章的標題和連接都已獲取到了。
 
如今嘗試深刻抓取文章內容
import api = require('./api');
import cheerio = require('cheerio');

const go = async () => {
    const res = await api.remote_get('http://cnodejs.org/');
    const $ = cheerio.load(res.text);
    $('.topic_title_wrapper').each(async (index, element) => {
        let url = ('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
        const res_content = await api.remote_get(url);
        const $_content = cheerio.load(res_content.text);
        console.log($_content('.topic_content').first().text());
    })

}
go();
能夠發現由於訪問服務器太迅猛,致使出現不少次503錯誤。
解決:
添加helper.ts文件:
export const wait_seconds = function (senconds: number) {
    return new Promise(resolve => setTimeout(resolve, senconds * 1000));
}
修改api.ts文件爲:
import superagent = require('superagent');
import cheerio = require('cheerio');

export const get_index_urls = function () {
    const res = await remote_get('http://cnodejs.org/');
    const $ = cheerio.load(res.text);
    let urls: string[] = [];
    $('.topic_title_wrapper').each(async (index, element) => {
        urls.push('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
    });
    return urls;
}
export const get_content = async function (url: string) {
    const res = await remote_get(url);
    const $ = cheerio.load(res.text);
    return $('.topic_content').first().text();
}

export const remote_get = function (url: string) {

    const promise = new Promise<superagent.Response>(function (resolve, reject) {

        superagent.get(url)
            .end(function (err, res) {
                if (!err) {
                    resolve(res);
                } else {
                    console.log(err)
                    reject(err);
                }
            });
    });
    return promise;
}
修改app.ts文件爲:
import api = require('./api');
import helper = require('./helper');
import cheerio = require('cheerio');

const go = async () => {
    const res = await api.remote_get('http://cnodejs.org/');
    const $ = cheerio.load(res.text);
    let urls = await api.get_index_urls();
    for (let i = 0; i < urls.length; i++) {
        await helper.wait_seconds(1);
        let text = await api.get_content(urls[i]);
        console.log(text);
    }
}
go();
觀察輸出能夠看到,程序實現了隔一秒再請求下一個內容頁。
 
如今嘗試把抓取到的東西存到數據庫中。
安裝mongoose模塊:
npm i mongoose --save
npm i -s @types/mongoose --save
而後創建Scheme。先建立models文件夾:
mkdir models
在models文件夾下建立index.ts:
import * as mongoose from 'mongoose';

mongoose.connect('mongodb://127.0.0.1/cnodejs_data', {
    server: { poolSize: 20 }
}, function (err) {
    if (err) {
        process.exit(1);
    }
});

// models
export const Article = require('./article');
在models文件夾下建立IArticle.ts:
interface IArticle {
    title: String;
    url: String;
    text: String;
}
export = IArticle;
在models文件夾下建立Article.ts:
import mongoose = require('mongoose');
import IArticle = require('./IArticle');
interface IArticleModel extends IArticle, mongoose.Document { }

const ArticleSchema = new mongoose.Schema({
    title: { type: String },
    url: { type: String },
    text: { type: String },
});

const Article = mongoose.model<IArticleModel>("Article", ArticleSchema);
export = Article;
修改api.ts爲:
import superagent = require('superagent');
import cheerio = require('cheerio');
import models = require('./models');
const Article = models.Article;

export const get_index_urls = async function () {
    const res = await remote_get('http://cnodejs.org/');

    const $ = cheerio.load(res.text);
    let urls: string[] = [];
    $('.topic_title_wrapper').each((index, element) => {
        urls.push('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
    });
    return urls;

}
export const fetch_content = async function (url: string) {
    const res = await remote_get(url);

    const $ = cheerio.load(res.text);
    let article = new Article();
    article.text = $('.topic_content').first().text();
    article.title = $('.topic_full_title').first().text().replace('置頂', '').replace('精華', '').trim();
    article.url = url;
    console.log('獲取成功:' + article.title);
    article.save();

}
export const remote_get = function (url: string) {

    return new Promise<superagent.Response>((resolve, reject) => {
        superagent.get(url)
            .end(function (err, res) {
                if (!err) {
                    resolve(res);
                } else {
                    reject(err);
                }
            });
    });
}
修改app.ts爲:
import api = require('./api');
import helper = require('./helper');
import cheerio = require('cheerio');

(async () => {

    try {
        let urls = await api.get_index_urls();
        for (let i = 0; i < urls.length; i++) {
            await helper.wait_seconds(1);
            await api.fetch_content(urls[i]);
        }
    } catch (err) {
        console.log(err);
    }

    console.log('完畢!');

})();
執行tsc
node out/app
觀察輸出,並去數據庫檢查一下
能夠發現入庫成功了!
相關文章
相關標籤/搜索