新手爬虫,教你如何爬掘金(一)
发布于 7 年前 作者 fruit-memory 6740 次浏览 来自 分享

前言,此文章仅作教学用途,如果有人拿去干别的事情,我概不负责,如果该文章侵害到了掘金社区的利益,请膜法小编立刻联系我删除.

这是我在掘金的第一篇文章,遂想写个爬虫教程吧,目标就是掘金,嘿嘿

本文用到的三个工具为

  • cheerio:jQuery语法,帮助你在非浏览器环境下解析网页用的
  • qs 序列化成url的查询字符串,(不知道说没说对…)例:{a:1,b2} => a=1&b=2
  • request 一个封装好的好用的请求库,我自己把它promise化了一下

全部代码见github

开始我是尝试直接请求掘金首页,然后用cheerio解析,然后拿到网页继续干活的。。可是事情并没有这么简单,通过这个方法爬取的网页跟我们正常浏览的首页不一样(有可能是我哪姿势不对) 没办法,只能从接口出发了

首先打开网页版掘金, 然后打开chrome的network,查看相关请求

咦!recommend?推荐?好了,进去一看,果然是首页热门文章,但是。。。 请求参数suid是什么?查看请求调用堆栈,,再看源码,emmmm 源码已经被混淆压缩了

这可怎么办?我没有登陆 查看完所有请求响应都没看到跟suid有关的,这可咋整?

直接进入请求网址,再更改suid,发现随便更改都可以得到相应 但是。。。这并没有什么用啊!就10条信息还需要爬吗?

没办法了,只能老套路了。先登陆再说

为了防止页面跳转后登陆请求消失,需要先勾选Preserve log,使页面跳转后前面的请求不会消失

差点忘打码了,qwq

模拟登陆

我是使用邮箱注册的,可能使用其它账号注册的接口会不一样

  let data = await request.create({
    url: 'https://juejin.im/auth/type/email',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({email: '155com', password: 'cfdsd.'}) //账号密码
  });

直接一个请求搞定,得到如下相应,问题的关键就在于cookie

好了,接下来该找接口了,点击最新,发现network多了下面这个请求,其响应数据就是最新板块的文章

相关参数有来源,设备id,用户id,token等,其中最重要的就是token,id什么的随便改两个字符好像也没问题,但是token错了它会报illegal token,请求方法错了,就算参数对了也会报missing src。

当你看到token的时候,你会发现,哪都找不到这个数据

但是你仔细看第5张图的时候,你会发现这auth后面这串字符很像base64编码

获取token

打开相关网站,尝试解码

答案呼之欲出啊,最重要的三个参数全在这了,那么问题来了,node如何解析base64编码呢?

一行代码解决,buffer对象本身提供了base64的解码功能,最后调用toString方法,转成字符串,最后parse得到对象

buffer用法浅析nodejs的buffer类

  const cookie = data.headers['set-cookie'];
  const encodeToken = cookie[0]
    .split(';')[0]
    .split('=')[1];
  const decodeToken = JSON.parse(new Buffer(encodeToken, 'base64').toString())

有了token,你就可以随心所欲的爬了,爬图片?主题?标题?文章内容?完全o98k

爬图片

async function getPictureUrl(request, url) {
  let data = await request.get({ url });
  console.log(data.body.match(/(https:\/\/user-gold-cdn).+?\/ignore-error\/1/g)); //匹配出图片url,这里掘金使用了cdn来存储图片
}

爬最新文章

async function getTopics(request, typeKey) {
  const { token, clientId, userId } = require('./user.json');
  const querystring = qs.stringify({
    src: 'web',
    uid: userId,
    device_id: clientId,
    token: token,
    limit: 20,
    category: 'all',
    recomment: 1
  });
  const types = {
    timeline: 'get_entry_by_timeline', //最新
    comment: 'get_entry_by_comment',   //评论
    rank: 'get_entry_by_rank'          //热门
  };
  const data = await request.get({
    url: `https://timeline-merger-ms.juejin.im/v1/${types[typeKey]}?${querystring}`,
    headers: {
      host: 'timeline-merger-ms.juejin.im',
      referer: 'https://juejin.im/timeline?sort=comment'
    }
  });
  const body = data.body;
  if (body.s !== 1) {     //出错时清空信息
    fs.writeFileSync('./user.json', JSON.stringify({}));
    throw { type: 'token', message: body.m };
  } else {
    return body.d.entrylist;
  }
}

简单的处理错误

try {
  (async function() {
    const request = new Riven();
    request.setDefaultOptions({
      headers: {
        Cookie:
          'gr_user_id=44868117-2a80-49e8-ba2b-2acd2a77a887; ab={}; _ga=GA1.2.1234597644.150' +
          '6904166; MEIQIA_EXTRA_TRACK_ID=0uMtBISQ3EoiMICJMjpaZedfTBz; _gid=GA1.2.100579701' +
          '2.1523672771; Hm_lvt_93bbd335a208870aa1f296bcd6842e5e=1521573516,1521573752,1522' +
          '270605,1523672771; gr_session_id_89669d96c88aefbc=d54a635e-cece-4f16-aca4-808ae9' +
          '2ee559; gr_cs1_d54a635e-cece-4f16-aca4-808ae92ee559=objectId%3A5a974f6ef265da4e8' +
          '53d8d52; auth=; auth.sig=25Jg_aucc6SpX1VH8RlCoh6azLU; Hm_lpvt_93bbd335a208870aa1' +
          'f296bcd6842e5e=1523675329; QINGCLOUDELB=165e4274d6090771b096025ed82d52a1ab7e48fb' +
          '3972913efd95d72fe838c4fb|WtFwy|WtFwr'
      }
    }); //设不设置cookie都OK的
    if (!isLogin()) {
      login(request);
    }
    const topics = await getTopics(request, 'commnet');
     for (let i = 0; i < topics.length; ++i) {
      //await getDetailData(request, topics[i].objectId);
      getPictureUrl(request, topics[i].originalUrl);
      await sleep(2000); //伪线程挂起
    }
  })();
 process.on('unhandledRejection', error => {
    if (error.type === 'token') {
      login();
    }
    console.log(error.message);
  });
} catch (error) {
  console.log(error.message);
}

当然,我没有使用数据库来保存数据,这只是教大家爬取原理,到这里,一个超级简单的爬虫就完成了

到最后好像也没用到cheerio了 ◔ ‸◔? 不过会在下一篇文章中使用

以上代码或言论如有错误,还望大家指出

8 回复

学习了~

和《Node.js 包教不包会》里面的很类似哈

@fairyly 之前听说《Node.js 包教不包会》是个很棒的教程,但是一直没有看,哈哈,这是我自己摸索出来的

不错,很好的爬虫经历,支持

来自酷炫的 CNodeMD

你好,我是掘金社区负责人,请跟我走一趟:)

@yikejiucai 真的假的,不会封号吧,我现在还在准备下一部分教程呢…

回到顶部