基本思路
思路一(origin:master):从维基百科的某个分类(比如:航空母舰(key))页面开始,找出链接的title属性中包含key(航空母舰)的所有目标,加入到待抓取队列中。这样,抓一个页面的代码及其图片的同时,也获取这个网页上所有与key相关的其它网页的地址,采取一个类广度优先遍历的算法来完成此任务。
思路二(origin:cat):按分类进行抓取。注意到,维基百科上,分类都以Category:开头,由于维基百科有很好的文档结构,很容易从任一个分类,开始,一直把其下的所有分类全都抓取下来。这个算法对分类页面,提取子分类,且并行抓取其下所有页面,速度快,可以把分类结构保存下来,但其实有很多的重复页面,不过这个可以后期写个脚本就能很容易的处理。

库的选择
开始想用jsdom,虽然感觉它功能强大,但也比较“重”,最要命的是说明文档不够好,只说了它的优势,没一个全面的说明。因此,换成cheerio,轻量级,功能比较全,至少文档一看就能有一个整体概念。其实做到后来,才发现根本不需要库,用正则表达式就能搞定一切!用库只是少写了一点正则而矣。

关键点
全局变量设定:

var regKey = ['航空母舰','航空母艦','航母'];  //链接中若包含此中关键词,即为目标
var allKeys = [];              //链接的title,也是页面标识,避免重复抓取
var keys = ['Category:航空母舰'];  //等待队列,起始页

图片下载
使用request库的流式操作,让每一个下载操作形成闭包。注意异步操作可能带来的副作用。另外,图片名字要重新设定,开始我取原名,不知道为什么,有的图明明存在,就是显示不出来;并且要把srcset属性清理掉,不然本面显示不出来。

$ = cheer.load(downHtml);
 var rsHtml = $.html();
 var imgs = $('#bodyContent .image');    //图片都由这个样式修饰
 for(img in imgs){
  if(typeof imgs[img].attribs === 'undefined' || typeof imgs[img].attribs.href === 'undefined')
   {continue;}  //结构为链接下的图片,链接不存在,跳过
  else
   {
    var picUrl = imgs[img].children[0].attribs.src;  //图片地址
    var dirs = picUrl.split('.');
    var filename = baseDir uuid.v1() '.' dirs[dirs.length -1];  //重新命名

    request("https:" picUrl).pipe(fs.createWriteStream('pages/' filename));  //下载

    rsHtml = rsHtml.replace(picUrl,filename);  //换成本地路径
    // console.log(picUrl);
   }
 }

广度优先遍历
开始没能完全理解异步的概念,以循环方式来做,以为使用了Promise,就已经全转化为同步了,但其实只是能保证交给promise的操作会有序进行,并不能让这些操作与其它的操作有序化!如,下面的代码就是不正确的。

var keys = ['航空母舰'];
var key = keys.shift();
while(key){
 data.get({
  url:encodeURI(key),
  qs:null
 }).then(function(downHtml){
    ...
    keys.push(key);        //(1)
  }
 });
key = keys.shift();          //(2)
}

上面的操作看试很正常,但其实(2)会在(1)之间被运行!哪怎么办?
我使用递归来解决这个问题。如下示例代码:

var key = keys.shift();
(function doNext(key){
 data.get({
  url:key,
  qs:null
 }).then(function(downHtml){
  ...
  keys.push(href);
  ...
  key = keys.shift();
  if(key){
   doNext(key);
  }else{
   console.log('抓取任务顺利完成。')
  }
 })
})(key);

正则清理
使用正则表达式清理无用的页面代码,因为有很多模式需要处理,写了一个循环统一处理。

var regs = [/<link rel=\"stylesheet\" href=\"?[^\"]*\">/g,
  /<script>?[^<]*<\/script>/g,
 /<style>?[^<]*<\/style>/g,
 /<a ?[^>]*>/g,
 /<\/a>/g,
 /srcset=(\"?[^\"]*\")/g
 ]
 regs.forEach(function(rs){
  var mactches = rsHtml.match(rs);
  for (var i=0;i < mactches.length ; i  )
  {
   rsHtml = rsHtml.replace(mactches[i],mactches[i].indexOf('stylesheet')>-1?'<link rel="stylesheet" href="wiki' (i 1) '.css"':'');
  }
 })

运行效果
上维基中文是需要FQ的,试运行了一下,抓取 航空母舰 分类,运行过程中,发现了三百左右的相关链接(包括分类页面,这些页面我是只取有效链接,不下载),最终正确的下载了209个,手工测试了一些出错链接,发现都为无效链接,显示该词条还未建立,整个过程大概花了不到十五分钟,压缩后近三十M,感觉效果还不错。

源代码
https://github.com/zhoutk/wikiSpider
小结
到昨晚基本完成任务,思路一能够抓取内容比较准确的页面,而且页面不重复,但抓取效率不高,分类信息无法准确获得;思路二能够按维基百科的分类,自动抓取并分门别类的把文件存储到本地,效率高(实测,抓取【军舰】类,共抓取页面近六千个,费时五十来分钟,每分钟能抓取超过一百个页面),能准确的保存分类信息。
最大的收获在于深刻的理解了异步编程的整体流程控制。

Node.js环境下编写爬虫爬取维基百科内容的实例分享的更多相关文章

  1. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. ios – 使用带有NodeJs HTTPS的certificates.cer

    我为IOS推送通知生成了一个.cer文件,我希望将它与NodeJSHTTPS模块一起使用.我发现HTTPS模块的唯一例子是使用.pem和.sfx文件,而不是.cer:有解决方案吗解决方法.cer文件可以使用两种不同的格式进行编码:PEM和DER.如果您的文件使用PEM格式编码,您可以像使用任何其他.pem文件一样使用它(有关详细信息,请参见Node.jsdocumentation):如果您的文件使

  3. 如何在XCode IDE中构建NodeJS?

    如何在XCodeIDE中将NodeJS构建为项目?NodeJS构建指令说它应该用以下内容构建:但是我希望在XCodeIDE中构建.我真正想要做的是在我的应用程序中嵌入NodeJS,所以我想如果我可以在XCode中构建NodeJS,那么我可以调整它以在我建立和运行NodeJS后添加我的应用程序.我想通过让V8在XCode中编译来取得一些进展,现在我正在尝试将NodeJS添加到V8项目中.解决方法在节点存储库根目录中运行./configure–xcode,您将获得所需的node.xcodeproj文件.

  4. 深入云存储系统Swift核心组件:Ring实现原理剖析

    它的目的是用于托管Rackspace的CloudFilesservice,原始项目代号是swift,所以沿用至今。Ring是Swift中最重要的组件,用于记录存储对象与物理位置间映射关系。先来看一下Swift文档中关于Ring的描述:Ring用来确定数据驻留在集群中的位置。有单独对应于Account数据库、container数据库和单个object的ring。Ring使用zone的概念来保证数据的隔离。每个partition的replica都确保放在了不同的zone中。本文逐步深入探讨了Swift如何通过

  5. Swift开发:创建XML文件,包含节点,属性值

    .append;//3创建第二个节点数据letitem2:Item=Item;for{letnode=Node;node.id=i+1;node.attributes=["ID":"\","Name":"N-\","disp":"1","Appliance":"1","Icon":"ic_switch_4"]item2.addNode;}xml.items?

  6. 泛型 – 符合Swift中Comparable的泛型类

    我正在尝试创建一个符合Comparable协议的简单通用节点类,以便我可以轻松地比较节点而无需访问其密钥.当我试图写

  7. swift3 – 将SceneKit对象放在SCNCamera当前方向的前面

    >生成SCNVector4,它定向节点,使其“面向”相机?但是让我有点失落.我看到了许多类似的问题,比如thisone,但没有答案.嘿,如果要将对象放在相对于另一个节点的某个位置,并且与参考节点的方向相同,则可以使用这个更简单的函数:如果您想将’node’2m放在某个’cameraNode’前面,你可以这样称呼:

  8. 如何在Swift中继承NSOperation以将SKAction对象排队以进行串行执行?

    Rob为子类化NSOperation提供了agreatObjective-Csolution,以实现SKAction对象的串行排队机制.我在自己的Swift项目中成功实现了这一点.要使用Actionoperation,请在客户端类中实例化NSOperationQueue类成员:在init方法中添加以下重要行:然后当您准备好向其添加SKActions时,它们会连续运行:您是否需要在任何时候终止操作:希望有所帮助!

  9. 核心数据 – 如何在Swift中定义CoreData关系?

    在CoreData中,我已经从Node到Tag定义了一个无序的多对多关系.我创建了一个这样的Swift实体:现在我想添加一个Tag到Node的一个实例,像这样:但是,这会失败,并显示以下错误:Terminatingappduetouncaughtexception‘NSinvalidargumentexception’,reason:‘Unacceptabletypeofvalueforto-ma

  10. 将“nil”值赋给Swift中的一般类型变量

    您需要将变量声明为可选项:不幸的是,这似乎触发了一个未实现的编译器功能:您可以通过使用NSObject的类型约束声明T来解决它:

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部