前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >node.js写爬虫程序抓取维基百科(wikiSpider)

node.js写爬虫程序抓取维基百科(wikiSpider)

作者头像
全栈程序员站长
发布2022-07-05 14:24:40
6600
发布2022-07-05 14:24:40
举报
文章被收录于专栏:全栈程序员必看

任务说明

抓取维基百科中文站某几个分类到本地,包括图片资源,能在单机直接浏览。

基本思路

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

库的选择

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

关键点

全局变量设定

代码语言:javascript
复制
var regKey = ['航空母舰','航空母艦','航母'];    //链接中若包含此中关键词,即为目标
var allKeys = [];                            //链接的title,也是页面标识,避免重复抓取
var keys = ['Category:%E8%88%AA%E7%A9%BA%E6%AF%8D%E8%88%B0'];    //等待队列,起始页

图片下载

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

代码语言:javascript
复制
$ = 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的操作会有序进行,并不能让这些操作与其它的操作有序化!如,下面的代码就是不正确的。

代码语言:javascript
复制
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)之间被运行!哪怎么办? 我使用递归来解决这个问题。如下示例代码:

代码语言:javascript
复制
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);

正则清理

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

代码语言:javascript
复制
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,感觉效果还不错。

源代码

代码语言:javascript
复制
https://github.com/zhoutk/wikiSpider

小结

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

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/109207.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年9月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 任务说明
  • 基本思路
  • 库的选择
  • 关键点
    • 全局变量设定
      • 图片下载
        • 广度优先遍历
          • 正则清理
            • 运行效果
              • 源代码
                • 小结
                相关产品与服务
                文件存储
                文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档