用TypeScript开发爬虫程序

全局安装typescript:

创新互联成立10年来,这条路我们正越走越好,积累了技术与客户资源,形成了良好的口碑。为客户提供成都网站建设、成都网站设计、网站策划、网页设计、域名申请、网络营销、VI设计、网站改版、漏洞修补等服务。网站是否美观、功能强大、用户体验好、性价比高、打开快等等,这些对于网站建设都非常重要,创新互联通过对建站技术性的掌握、对创意设计的研究为客户提供一站式互联网解决方案,携手广大客户,共同发展进步。

 
 
  1. npm install -g typescript

目前版本2.0.3,这个版本不再需要使用typings命令了。但是vscode捆绑的版本是1.8的,需要一些配置工作,看本文的处理办法。

测试tsc命令:

 
 
  1. tsc

创建要写的程序项目文件夹:

 
 
  1. mkdir test-typescript-spider

进入该文件夹:

 
 
  1. cd test-typescript-spider

初始化项目:

 
 
  1. npm init

安装superagent和cheerio模块:

 
 
  1. npm i --save superagent cheerio

安装对应的类型声明模块:

 
 
  1. npm i -s @types/superagent --save 
  2. npm i -s @types/cheerio --save 

安装项目内的typescript(必须走这一步):

 
 
  1. npm i --save typescript

用vscode打开项目文件夹。在该文件夹下创建tsconfig.json文件,并复制以下配置代码进去:

 
 
  1. {
  2.     "compilerOptions": {
  3.         "target": "ES6",
  4.         "module": "commonjs",
  5.         "noEmitOnError": true,
  6.         "noImplicitAny": true,
  7.         "experimentalDecorators": true,
  8.         "sourceMap": false,
  9.      // "sourceRoot": "./",
  10.         "outDir": "./out"
  11.     },
  12.     "exclude": [
  13.         "node_modules"
  14.     ]

在vscode打开“文件”-“***项”-“工作区设置”在settings.json中加入(如果不做这个配置,vscode会在打开项目的时候提示选择哪个版本的typescript):

 
 
  1. {
  2.    "typescript.tsdk": "node_modules/typescript/lib"

创建api.ts文件,复制以下代码进去:

 
 
  1. import superagent = require('superagent');
  2. import cheerio = require('cheerio');
  3. export const remote_get = function(url: string) {
  4.     const promise = new Promise(function (resolve, reject) {
  5.         superagent.get(url)
  6.             .end(function (err, res) {
  7.                 if (!err) {
  8.                     resolve(res);
  9.                 } else {
  10.                     console.log(err)
  11.                     reject(err);
  12.                 }
  13.             });
  14.     });
  15.     return promise;

创建app.ts文件,书写测试代码:

 
 
  1. import api = require('./api');
  2. const go = async () => {
  3.     let res = await api.remote_get('http://www.baidu.com/');
  4.     console.log(res.text);
  5. }
  6. go(); 

执行命令:

 
 
  1. tsc

然后:

 
 
  1. node out/app

观察输出是否正确。

现在尝试抓取http://cnodejs.org/的***页文章链接。

修改app.ts文件,代码如下:

 
 
  1. import api = require('./api');
  2. import cheerio = require('cheerio');
  3. const go = async () => {
  4.     const res = await api.remote_get('http://cnodejs.org/');
  5.     const $ = cheerio.load(res.text);
  6.     let urls: string[] = [];
  7.     let titles: string[] = [];
  8.     $('.topic_title_wrapper').each((index, element) => {
  9.         titles.push($(element).find('.topic_title').first().text().trim());
  10.         urls.push('http://cnodejs.org/' + $(element).find('.topic_title').first().attr('href'));
  11.     })
  12.     console.log(titles, urls);
  13. }
  14. go(); 

观察输出,文章的标题和链接都已获取到了。

现在尝试深入抓取文章内容

 
 
  1. import api = require('./api');
  2. import cheerio = require('cheerio');
  3. const go = async () => {
  4.     const res = await api.remote_get('http://cnodejs.org/');
  5.     const $ = cheerio.load(res.text);
  6.     $('.topic_title_wrapper').each(async (index, element) => {
  7.         let url = ('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
  8.         const res_content = await api.remote_get(url);
  9.         const $_content = cheerio.load(res_content.text);
  10.         console.log($_content('.topic_content').first().text());
  11.     })
  12. }
  13. go(); 

可以发现因为访问服务器太迅猛,导致出现很多次503错误。

解决:

添加helper.ts文件:

 
 
  1. export const wait_seconds = function (senconds: number) {
  2.     return new Promise(resolve => setTimeout(resolve, senconds * 1000));

修改api.ts文件为:

 
 
  1. import superagent = require('superagent');
  2. import cheerio = require('cheerio');
  3. export const get_index_urls = function () {
  4.     const res = await remote_get('http://cnodejs.org/');
  5.     const $ = cheerio.load(res.text);
  6.     let urls: string[] = [];
  7.     $('.topic_title_wrapper').each(async (index, element) => {
  8.         urls.push('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
  9.     });
  10.     return urls;
  11. }
  12. export const get_content = async function (url: string) {
  13.     const res = await remote_get(url);
  14.     const $ = cheerio.load(res.text);
  15.     return $('.topic_content').first().text();
  16. }
  17. export const remote_get = function (url: string) {
  18.     const promise = new Promise(function (resolve, reject) {
  19.         superagent.get(url)
  20.             .end(function (err, res) {
  21.                 if (!err) {
  22.                     resolve(res);
  23.                 } else {
  24.                     console.log(err)
  25.                     reject(err);
  26.                 }
  27.             });
  28.     });
  29.     return promise;
  30. }

修改app.ts文件为:

 
 
  1. import api = require('./api');
  2. import helper = require('./helper');
  3. import cheerio = require('cheerio');
  4. const go = async () => {
  5.     let urls = await api.get_index_urls();
  6.     for (let i = 0; i < urls.length; i++) {
  7.         await helper.wait_seconds(1);
  8.         let text = await api.get_content(urls[i]);
  9.         console.log(text);
  10.     }
  11. }
  12. go();

观察输出可以看到,程序实现了隔一秒再请求下一个内容页。

现在尝试把抓取到的东西存到数据库中。安装mongoose模块:

 
 
  1. npm i mongoose --save
  2. npm i -s @types/mongoose --save 

然后建立Scheme。先创建models文件夹:

 
 
  1. mkdir models

在models文件夹下创建index.ts:

 
 
  1. import * as mongoose from 'mongoose';
  2. mongoose.connect('mongodb://127.0.0.1/cnodejs_data', {
  3.     server: { poolSize: 20 }
  4. }, function (err) {
  5.     if (err) {
  6.         process.exit(1);
  7.     }
  8. });
  9. // models
  10. export const Article = require('./article'); 

在models文件夹下创建IArticle.ts:

 
 
  1. interface IArticle {
  2.     title: String;
  3.     url: String;
  4.     text: String;
  5. }
  6. export = IArticle;

在models文件夹下创建Article.ts:

 
 
  1. import mongoose = require('mongoose');
  2. import IArticle = require('./IArticle');
  3. interface IArticleModel extends IArticle, mongoose.Document { }
  4. const ArticleSchema = new mongoose.Schema({
  5.     title: { type: String },
  6.     url: { type: String },
  7.     text: { type: String },
  8. });
  9. const Article = mongoose.model("Article", ArticleSchema);
  10. export = Article; 

修改api.ts为:

 
 
  1. import superagent = require('superagent');
  2. import cheerio = require('cheerio');
  3. import models = require('./models');
  4. const Article = models.Article;
  5. export const get_index_urls = async function () {
  6.     const res = await remote_get('http://cnodejs.org/');
  7.     const $ = cheerio.load(res.text);
  8.     let urls: string[] = [];
  9.     $('.topic_title_wrapper').each((index, element) => {
  10.         urls.push('http://cnodejs.org' + $(element).find('.topic_title').first().attr('href'));
  11.     });
  12.     return urls;
  13. }
  14. export const fetch_content = async function (url: string) {
  15.     const res = await remote_get(url);
  16.     const $ = cheerio.load(res.text);
  17.     let article = new Article();
  18.     article.text = $('.topic_content').first().text();
  19.     article.title = $('.topic_full_title').first().text().replace('置顶', '').replace('精华', '').trim();
  20.     article.url = url;
  21.     console.log('获取成功:' + article.title);
  22.     article.save();
  23. }
  24. export const remote_get = function (url: string) {
  25.     return new Promise((resolve, reject) => {
  26.         superagent.get(url)
  27.             .end(function (err, res) {
  28.                 if (!err) {
  29.                     resolve(res);
  30.                 } else {
  31.                     reject(err);
  32.                 }
  33.             });
  34.     });

修改app.ts为:

 
 
  1. import api = require('./api');
  2. import helper = require('./helper');
  3. import cheerio = require('cheerio');
  4. (async () => {
  5.     try {
  6.         let urls = await api.get_index_urls();
  7.         for (let i = 0; i < urls.length; i++) {
  8.             await helper.wait_seconds(1);
  9.             await api.fetch_content(urls[i]);
  10.         }
  11.     } catch (err) {
  12.         console.log(err);
  13.     }
  14.     console.log('完毕!');
  15. })(); 

执行

 
 
  1. tsc
  2. node out/app 

观察输出,并去数据库检查一下可以发现入库成功了!

补充:remote_get方法的改进版,实现错误重试和加入代理服务器.放弃了superagent库,用的request库,仅供参考:

 
 
  1. //config.retries = 3;
  2. let current_retry = config.retries || 0;
  3. export const remote_get = async function (url: string, proxy?: string) {
  4.     //每次请求都先稍等一下
  5.     await wait_seconds(2);
  6.     if (!proxy) {
  7.         proxy = '';
  8.     }
  9.     const promise = new Promise(function (resolve, reject) {
  10.         console.log('get: ' + url + ',  using proxy: ' + proxy);
  11.         let options: request.CoreOptions = {
  12.             headers: {
  13.                 'Cookie': '',
  14.                 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
  15.                 'Referer': 'https://www.baidu.com/'
  16.             },
  17.             encoding: 'utf-8',
  18.             method: 'GET',
  19.             proxy: proxy,
  20.             timeout: 3000,
  21.         }
  22.         request(url, options, async function (err, response, body) {
  23.             console.log('got:' + url);
  24.             if (!err) {
  25.                 body = body.toString();
  26.                 current_retry = config.retries || 0;
  27.                 console.log('bytes:' + body.length);
  28.                 resolve(body);
  29.             } else {
  30.                 console.log(err);
  31.                 if (current_retry <= 0) {
  32.                     current_retry = config.retries || 0;
  33.                     reject(err);
  34.                 } else {
  35.                     console.log('retry...(' + current_retry + ')')
  36.                     current_retry--;
  37.                     try {
  38.                         let body = await remote_get(url, proxy);
  39.                         resolve(body);
  40.                     } catch (e) {
  41.                         reject(e);
  42.                     }
  43.                 }
  44.             }
  45.         });
  46.     });
  47.     return promise;

另外,IArticle.ts和Article.ts合并为一个文件,可能更好,可以参考我另一个model的写法:

 
 
  1. import mongoose = require('mongoose');
  2. interface IProxyModel {
  3.     uri: string;
  4.     ip: string;
  5.     port:string;
  6.     info:string;
  7. }
  8. export interface IProxy extends IProxyModel, mongoose.Document { }
  9. const ProxySchema = new mongoose.Schema({
  10.     uri: { type: String },//
  11.     ip: { type: String },//
  12.     port: { type: String },//
  13.     info: { type: String },//
  14. });
  15. export const Proxy = mongoose.model("Proxy", ProxySchema); 

导入的时候这么写就行了:

 
 
  1. import { IProxy, Proxy } from './models';

其中Proxy可以用来做new、find、where之类的操作:

 
 
  1. let x = new Proxy();
  2. let xx = await Proxy.find({});
  3. let xxx = await Proxy.where('aaa',123).exec(); 

而IProxy用于实体对象的传递,例如

 
 
  1. function xxx(p:IProxy){

网站题目:用TypeScript开发爬虫程序
本文地址:http://www.csdahua.cn/qtweb/news33/405433.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网