用TypeScript开发爬虫程序

全局安装typescript:


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

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


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

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


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

创建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. go();  

执行命令:


  1. tsc 

然后:


  1. node out/app 

观察输出是否正确。

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

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


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

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

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


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

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

解决:

添加helper.ts文件:


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

修改api.ts文件为:


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

修改app.ts文件为:


  1. import api = require('./api'); 
  2. import helper = require('./helper'); 
  3. import cheerio = require('cheerio'); 
  4.  
  5. const go = async () => { 
  6.     let urls = await api.get_index_urls(); 
  7.     for (let i = 0; i < urls.length; i++) { 
  8.         await helper.wait_seconds(1); 
  9.         let text = await api.get_content(urls[i]); 
  10.         console.log(text); 
  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.  
  3. mongoose.connect('mongodb://127.0.0.1/cnodejs_data', { 
  4.     server: { poolSize: 20 } 
  5. }, function (err) { 
  6.     if (err) { 
  7.         process.exit(1); 
  8.     } 
  9. }); 
  10.  
  11. // models 
  12. export const Article = require('./article');  

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


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

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


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

修改app.ts为:


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

执行


  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<string>(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; 
  48. }  

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


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

作者:sagacite

来源:51CTO

时间: 2024-11-03 17:07:07

用TypeScript开发爬虫程序的相关文章

使用TypeScript开发微信小程序的方法_C#教程

TypeScript简介: TypeScript是一种由微软开发的自由和开源的编程语言.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程.安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发. TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作.TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性.

Python开发实例分享bt种子爬虫程序和种子解析_python

看到网上也有开源的代码,这不,我拿来进行了二次重写,呵呵,上代码: 复制代码 代码如下:     #encoding: utf-8      import socket      from hashlib import sha1      from random import randint      from struct import unpack, pack      from socket import inet_aton, inet_ntoa      from bisect impo

ASP.NET 5系列教程 (五):在Visual Studio 2015中使用Grunt、Bower开发Web程序

基于Visual Studio 2015,你可以: 方便的管理前端包,如jQuery, Bootstrap, 或Angular. 自动运行任务,如LESS.JavaScript压缩.JSLint.JavaScript单元测试等. 方便的获得Web开发者生态圈的工具包. 为了实现这些场景,Visual Studio 2015已经内置了一些流行的第三方工具包: Bower:Web包管理器,Bower可以帮你安装前端包,包括JavaScript.CSS类库.对于服务器端包,请通过NuGet包管理. G

Eclipse开发Android程序如何在手机上运行

1.设置android手机为USB调试模式 打开手机里面的开发者选项->USB调试 2.通过eclipse上真机测试 没用真机时,用eclipse开发android程序都是点run,然后选择模拟器的.安装好手机的usb驱动后,还是点run,程序就会神奇的在真机上运行了,效果与在模拟器中的一样,而且速度比用模拟器块很多. 如果还是在模拟器上运行,右键->run as->run configurations->target 选项1:总是提示选择设备 选项2:在所有的设备上登陆 选项3:

c c++-求用c编写的一个简单的爬虫程序,高手赐教,不胜感激

问题描述 求用c编写的一个简单的爬虫程序,高手赐教,不胜感激 本人是初学者,要编写一爬虫程序,抓取60多万个网页上的信息,实在是无从下手,请高手给一个能看得懂的简单的爬虫程序学习用,多谢 解决方案 我也要写一个C爬虫,不过遇到了一些问题,比如58这样的网站,用getaddrinfo返回的ip无法连接,已经耽误了我好几天了,别的问题到还没遇到

从根本上改变我们开发Java程序的方式:Lambda

当今世界主流编程语言无不吸纳强大的闭包概念,但有个例外,它就是Java.数年来,Java语言中增加闭包特征的工作看起来毫无进展. 早在15年之前,Scala语言和TypeSafe框架的作者Martin Odersky和Phillip Wadler发布了实验性的"Pizza"项目,由此,人们开始试图将闭包纳入编程语言的基本特征之一.尽管这看起来有点过于复杂,Java社区大概在2008年就有了接纳闭包概念的想法.但由于Oracle对Sun微系统公司的匆忙收购,Java被冷落,Java语言新

使用JBuilder开发J2ME程序

程序 使用JBuilder开发J2ME程序 作者:陈跃峰 出自:http://blog.csdn.net/mailbomb          现在实际应用大部分都是使用JBuilder在进行开发,所以简单介绍一下如何使用.        JBuiler 9(包括JB9)以上都包含了MobileSet和WTK,可以直接用来开发.        使用JB9以上开发J2ME应用时,和使用JB进行一般的开发类似,步骤如下: 1.  新建project,根据需要选择合适的project类型.以下以新建空项

用Struts开发国际化程序思路

程序 对于使用者来说,一个支持国际化的WEB程序具有下面几种形式 1. 根据用户浏览器自动设置显示的语言(无需用户干预)2. 提供用户选择,用户根据自己的需要决定使用何种语言显示3. 结合前两种.系统自动选择一种语言,但同时提供用户根据需要选择 使用Struts开发国际化程序是一件非常便利的事情,我们来看前两种怎么来实现 1. 这是最简单的方式,你不需要修改任何程序,只需要把资源文件按照各个语言翻译一遍并把这些文件按照国际化程序的要求命名好放置同一个目录即可. 例如 ApplicationRes

蜘蛛/爬虫程序的多线程控制(C#语言)

程序|多线程|控制 在<爬虫/蜘蛛程序的制作(C#语言)>一文中,已经介绍了爬虫程序实现的基本方法,可以说,已经实现了爬虫的功能.只是它存在一个效率问题,下载速度可能很慢.这是两方面的原因造成的: 1.       分析和下载不能同步进行.在<爬虫/蜘蛛程序的制作(C#语言)>中已经介绍了爬虫程序的两个步骤:分析和下载.在单线程的程序中,两者是无法同时进行的.也就是说,分析时会造成网络空闲,分析的时间越长,下载的效率越低.反之也是一样,下载时无法同时进行分析,只有停下下载后才能进行