DIY技术资讯抓取工具的实践与研究

爷,独闯天下 提交于 2020-04-06 00:23:09

前言

相信每一个技术人员都有周期性获取技术资讯的诉求,而获取的方式也多种多样。例如,用资讯类APP,进行RSS订阅,参加行业大会,深入技术社区,订阅期刊杂志、公众号,等等,都是可选的方式。这些方式看到信息的成本都很低,有“开箱即得”的感觉。但缺点也很明显,有点像“大班课”,可以满足一类人的需求,但难较好地满足每个参与者的个性化诉求。通过这些方式,要想真正拿到自己所需要的信息的成本并不低(虽然智能推荐在往满足个性化诉求方面迭代,但离期待仍有较大的差距)。对于个性化诉求,最简单的方式就是你感兴趣哪方面的内容就去逐一主动检索或者浏览,但这种方式的成本显然太高。

核心的问题是,上面的两大类路径,都不是很懂你(了解你的意图和诉求)。而你需要一个既懂你,成本又不是太高的方式。

一、对于技术资讯获取DIY的框架性思考

相信在当前相当一段时期内,最适合的个性化资讯获取方式仍然是工具+人工相组合的方式。相比纯工具的算法推荐,一些付费资讯渠道已经在(智能)工具的基础上,对信息进行了人工的筛选、加工处理,质量会更好。如果你是程序员,自己编写一些小爬虫,在其中注入自己的喜好与智慧,不失为一种懂你且成本不高的方式。而且通过这种方式,你将获得很好的自我掌控感。本文中,笔者就着重介绍这种方式。值得提醒的是,本文所涉内容,仅为学习讨论技术,切勿用作非法用途。

 

具体来说,分为四部分(如图1.1所示):

 

  

  

 

图1.1

 

第一,自己控制消息来源

 

你可以根据自己的经验积累,在合法合规的前提下来选取消息来源。这个选择的维度可以很多样,包括质量可靠性、信息的前瞻性、兴趣匹配度、研究方向匹配度、信息生产频率、信息的新颖度,等等。

 

第二,自己编写采集和筛选算法

 

选定了一些采集渠道,你就可以自己编写采集和筛选算法了。采集周期、筛选规则、所需内容项,等等,都可以自己控制。如果你对数据处理、人工智能等很了解,相信还有更多的发挥空间。

 

第三,自己控制阅读和交互体验

 

由于阅读是一个长期的过程,对于优质的体验其实有着很强的需求。难受的阅读体验是非常不利于信息的快速获取的,甚至会打消获取信息的兴趣。比如,下面这两张图,图1.2左边是某头条的资讯界面,右边是微信读书的阅读界面。

 

        

 

图1.2

 

相形之下,作为阅读者,我个人更喜欢微信阅读的简洁,而不太喜欢某头条那些次要元素的干扰。

 

第四,自己控制迭代优化

 

自己既是消费者也一定程度是信息流通控制者的好处就是:自己可以站在结果环节对信息获取全流程进行自主评价,回溯作用到前面的环节,从而形成正向作用闭环。

 

这么做有什么收益呢?

 

首先,是获得有价值的信息。

 

这一点无需多言。

 

其次,有助于信息获取能力的提升。

 

就拿技术人员来说,这么做可以更高效地、持续地获取满足个性化诉求的高价值信息,在对外部技术世界持续保持关注中获得持续性地成长与提升。

1)关于信息来源:你将自己总结出一份最有价值的信息的来源渠道列表,提高信息的获取效率,能以较快的速度接触到相对可靠的信息。

2)关于信息处理:你将沉淀出自己的一份或简单或复杂的信息采集和筛选算法,提升信息的鉴别能力,增强信息处理的能力。

3)关于信息体验:你将获得适合你自己的信息获取、阅读、交互体验,增强阅读兴趣和减少疲劳。

 

第三,有助于进行技术探索,提升技术应用能力。

 

在这个过程中,实际上也是自己在运用技术解决实际问题的探索过程,可以作为技术甚至产品建构探索的实验田。比如说,Flutter这一技术有很多公司在进行尝试和应用,但是你所做的项目暂时还是用的Electron做的,目前并没有迁移到Flutter的打算。那么如果你对Flutter感兴趣的话,完全可以把采集到的技术资讯尝试用Flutter做成一个APP,先试水一下怎么用(只是举个“栗子”,如果你恰好真感兴趣的话,后面有彩蛋一枚,继续往下看准能找到😂)。这样就相当于是先期业余做了一些储备和实践。

 

二、对于技术资讯获取DIY的实践探索

 

上面啰嗦了这么多,还是讲点实在的吧。咱们来真实地爬取点技术资讯。要抓取的内容存在形式是多种多样的,有的被内容服务端直接渲染到了HTML页面上,有的则是在页面中通过JavaScript请求数据,然后再渲染出来的。

 

首先来看第一种。

 

1、HTML页面中内容的抓取

 

第一步,信息来源的选择。

 

要不我们就比较有代表性的互联网公司BAT里随便找一家吧,看看他们都有些什么高价值的技术资讯。不如就选那个比较高调(非常乐于向业界分享自己技术)的阿里巴巴,因为高调的可能比较好找。他们有个云栖社区,里面有个栏目叫阿里技术(https://yq.aliyun.com/articles/721143,这是一个一直在有规律更新,而且文章质量不错的栏目,界面如下所示。

 

 

图2.1

 

第二步,信息的采集和筛选。

 

假设我们准备爬取最近一周阿里技术这个栏目下都有些什么新的文章发布。我们主要获取其标题、文章链接地址、发布时间和文章简介,希望只抓取最近7天内发布的文章。即期望爬取出来的结果如图2.2所示。

 

 图2.2

 

目标清楚了,下一步就是怎么实现,笔者选择使用Node.js。这里需要介绍用到的两个工具:request-promise(https://www.npmjs.com/package/request-promise)和cheerio(https://www.npmjs.com/package/cheerio)。所以首先你需要用 yarn init 命令创建一个项目,再用 yarn add request request-promise cheerio 命令安装上这几个依赖模块。 

 

关于request-promise,官方介绍是:

 

The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.

 

通过request-promise,可以很轻易地抓到页面的HTML,如下所示:

 

const rp = require('request-promise');
rp(' // 略去了地址 
  .then(function (htmlString) {
   // Process html...    
 })
 .catch(function (err) {
  // Crawling failed...    
 });

 

抓到HTML后,我们还是希望对其进行处理,把其中的我们所需要的标题、文章链接地址和文章简介等信息提取出来。这时需要用到另一个工具——cheerio。用它与request-promise结合,可以让对于抓取到的HTML的处理基本上像用jQuery那样进行。因为cheerio实现了jQuery的核心子集。两者结合后的用法如下:

 

const rp = require('request-promise');
const cheerio = require('cheerio');
const targetURL = ' // 略去了地址  
const options = {
  uri: targetURL,
  transform: (body) => {
    return cheerio.load(body);
  }
};

function getArticles() {
  rp(options)
    .then(($) => {
      // Process html like you would with jQuery... 
      console.log($('title').text());
    })
    .catch((err) => {
      // Crawling failed or Cheerio choked...
    });
}

// 入口
getArticles();

 

上面代码中,

 

console.log($('title').text())

 

会log出来页面title标签内部的文字,就像使用jQuery操作页面DOM一样。

 

接着我们就可以用Chrome打开阿里技术(https://yq.aliyun.com/articles/721143)页面,借助Chrome DevTools轻而易举找到文章的标题所对应的HTML元素(如图2.3所示)。进而通过将上述代码中的

 

console.log($('title').text())

 

这一行替换为:

 

console.log console.log($('.yq-new-item h3 a').eq(1).text())($('.yq-new-item h3 a').eq(1).text())

 

从而log出来其中一篇技术资讯文章的标题。

 

 

图2.3

 

举一反三,用同样的方法可以获取到文章链接地址和文章简介。但是,我们还想获取到每篇文章的发布时间,但是当前页面中并没有,怎么办呢?点进去每篇文章的链接,我们发现文章内部是有这个信息的(如图2.4)。于是,实现思路就有了。每抓取到一篇文章的链接之后,再针对抓到的链接地址再进行一次抓取,把该篇文章中的发布时间也抓取出来。

 

 

 图2.4

 

另外,因为Promise在代码中用多了之后,看起有点丑陋,所以我们将之改成用async和await的方式实现。并且把抓取到的信息写入到一个JSON文件(result.json)中。最终实现的演示代码如下:

/**
 * 爬取技术资讯学习举例1
 */
const fs = require('fs');
const rp = require('request-promise');
const cheerio = require('cheerio');
const  targetURL = 'https://xxxxxxxxxxxxxx'; // 略去了地址
const maxDeltaDay = 7;

/**
 * 抓取目标网页中的技术资讯
 * @param {string} url - 抓取的目标网页的网址 
 * @param {number} maxDeltaDay - 抓取距离当前时间多少天以内的资讯 
 */
 async function getArticles(url, maxDeltaDay) {  
   const options = generateOptions(url);
   const $ = await rp(options);  
   const elements = $('.yq-new-item h3 a');
   
   // 拿到包含文章标题、链接等的标签  
   const result = [];  
   const promises = [];
   elements.map((index, el) => {    
     const $el = $(el);    
     const linkObj = {};  
       
     // 获取标题和链接    
     linkObj.title = $el.text();    
     const link = $el.attr('href');    
     linkObj.link = `https://yq.aliyun.com${link}`;    
     
     // 处理文章简介
     let brief = $el.parent().parent().find('.new-desc-two').text();    
     brief = brief.replace(/\s*/g, '');    
     linkObj.brief = brief;    
     promises.push(   
       getDeltaDay(linkObj.link).then((deltaDay) => {        
         if (deltaDay < maxDeltaDay) {          
           linkObj.deltaDay = deltaDay;          
   &nbs
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!