less than 1 minute read

本篇的主要内容是新闻爬虫的实现

在完成了前期的部署之后,接下去就是愉快地开发过程了。node.js 的语法就是 js 的语法,没有什么介绍的必要,基础语法看教程即可。node.js 的特色是函数回调,如果不把这个问题处理好的话,就会像我一样陷入回调地狱之中(跑。

回到正题,爬虫的实现主要分成两个部分。一个是请求网页数据,一个是解析网页。

配置环境

请求网页和解析网页的包都有不止一个,但大家都是大同小异的。在本文中,请求网页数据用的是 request包,解析网页用的是 cheerio包,所以在使用之前先导入相应的包。

var myRequest = require("request");
var myCheerio = require("cheerio");

假如没有就用 npm 下载。

请求网页数据

请求网页数据的本质就是把 js 的访问包装成一个浏览器访问,让网站正常地给你返回一个完整的网页。本来,因为大量的爬虫会拖慢服务器,所以爬虫和网站的反爬要不断地斗智斗勇,但据我实验,似乎因为我爬的数量太少了,根本没有触发任何机制,直接拿一个裸的访问请求抓到了需要的数据……

首先对 request包再包装一下。

function request(url, callback) {
  var options = {
    url: url,
    encoding: null,
    headers: null,
  };
  myRequest(options, callback);
}

正常来说,options里的参数不可能只填一个 url,应该要把 headers补充完整,以伪装成一个浏览器请求。但既然爬虫没被反制,也不费这个心思处理了。

需要注意的是函数的第二个参数,callback即回调函数,顾名思义,callback会在访问请求返回之后被调用,而在这个时间段内,程序并没有等着回调函数的结束,而是自顾自的继续运行了。这种异步的操作减少了大量阻塞的时间,但也给初学者造成了一些上手的困难,因为回调函数和后续内容的执行顺序是不好确定的。举个例子:

var a = 2;
request(url, function () {
  a = 1;
});
console.log(a);

此时输出的结果并不能很好地确定,因为拿不准回调内的赋值语句是否会在输出之后再执行(一般是会的,因为网络 IO 的延时比本地执行大得多)。所以为了保证回调得到的数据能够被正确地处理,只能把对这个数据的处理逻辑全部放在回调函数里面。层层套娃,就变成了回调地狱。

但总之,我们可以在回调函数里拿到 request请求的数据,并在函数里对数据做处理。

request(url, function (err, res, body) {
  var html = body;
  /*
    处理逻辑
    */
});

解析网页数据

做新闻爬虫大概需要爬取两个类型的网页,一种是目录网页,也就是导航页;一种是新闻的主体网页。首先从目录网页上获取主体网页的链接,再逐个访问主体网页,从网页里获取新闻的具体信息。

解析目录网页

目录网页的网址一般需要自己确定,比如这里以新浪新闻的首页为例,在爬取了新浪新闻的首页之后。使用 cheerio解析网页的内容,这个包的基本使用方法和 jQuery的解析方法是类似的,只要读取所有链接标签 <a>的链接就能获取主体网页的链接。

var $ = myCheerio.load(html, { decodeEntities: false });
hrefArr = $("a");

当然,这些链接中有许多都不是新闻的链接,可以通过正则表达式对链接做一个初步的筛选。这里的正则表达式筛选出的是 https://news开头的 html 文件。

var exr = /https:\/\/news.*html/;
for (var i = 0; i < hrefArr.length; i++) {
  hrefURL = hrefArr[i].attribs.href;
  if (exr.test(hrefURL)) {
    urlArr.push(hrefURL);
  }
}

解析主体网页

在获取了主体网页的地址之后,仿照上面的方法,可以得到主体网页的内容。但要从中分析出需要的内容,相对会比较困难。一个比较好的办法是随便用浏览器打开一个主体网页,F12,然后拿元素检查在网页上戳,一般用标签加上 CSS 类加上 id 足够确定对应的元素了。多试几遍就能正确地锁定目标文本。

var ans = {};
ans["title"] = $("title").text();
// 对中文日期格式做处理
ans["date"] = new Date(
  Date.parse(
    $("span.date")
      .text()
      .replace("", "-")
      .replace("", "-")
      .replace("", "")
  )
);
ans["content"] = $("#article p").text();

总结

爬取网页的重点在于对请求的伪装和回调函数,解析网页的重点在于正确地分析出所需要的数据的位置然后提取。至于提取出来的数据怎么保存,那又是另一个故事了。如果只是想用来提取自己想看的新闻,直接输出就可以了。

Updated: