用nodejs快速在Matataki发文

如何用nodejs快速在Matataki发文, 利用node爬虫来获取网页的内容然后转发到matataki上面

这里就自己的blog做一个简单的example 这是可能需要用的接口文档⬇️⬇️⬇️ (docsify真香)

image

开始

  1. 首先我们先初始一个项目

    mkdir matataki-post
    npm init -y
    touch index.js
    
  2. 理清思路

    就像怎么把大象🐘装进冰箱一样 1.... 2... 3... 首先我们需要在matataki上面注册一个账号, 我选择了邮箱 因为很简单也很方便 注册也挺快的, 然后去发布一篇文章 看看接口是如何调用的

    编辑

    image

    发布

    image

    分析Networ

    编辑: 我们在编辑文章的时候可以看出上传图片调用接口是 /post/uploadImage, 于是我们可以忽略其他接口调用

    发布: 发布的时候, 可以看出我们一共调用了两个核心的接口, 一个是ipfs上传, 一个是文章上传

思路

// 1、获取内容
 // 1、获取html
 // 2、解析dom获取内容
// 2、发布文章
 // 1、转存文章封面 因为文章的图片是外站的 我们需要转存到matataki上面才行
 // 2、上传ipfs
 // 3、上传文章
  1. 获取网页内容并解析dom

    因为我的blog是静态页面 所以用superagent就可以抓取到内容了, 如果是客户端渲染抓去内容可能有问题, 可以考虑用puppetter做爬虫, 然后用cheerio来解析dom 回味jq, 请求用axios因为做前端习惯了🍑

    npm i superagent cheerio axios
    
    const superagent = require("superagent");
    const cheerio = require("cheerio");
    const axios = require("axios");
    // ...
    // 获取内容
    const getHtml = async url => {
      try {
        // 根据url获取内容
        const res = await superagent.get(url);
        return res.text;
      } catch (err) {
        console.error(err);
        return false;
      }
    };
    
    // 拆dom 这块根据自己页面自定义
    const getDom = html => {
      if (!html) return false; // 没html返回
      const $ = cheerio.load(html);
      // 我的标题
      let title = $("#main #posts .post-header .post-title");
      // 描述
      let desc = $("#main #posts .post-body").text();
      // 内容
      let content = $("#main #posts .post-body").html();
      // 文章封面
      let cover = $("#main #posts .post-body img");
     
      // 如果有标题
      let titleRes = title.length >= 1 ? $(title[0]).text() : "";
      // 如果有图片
      let coverRes = cover.length >= 1 ? $(cover[0]).attr("src") : "";
     
      // 把数据返回出去
      return {
        title: titleRes,
        desc,
        content,
        cover: coverRes
      };
    };
    

    这块还是挺简单的233~~~

    # 然后我们可以调用方法 启动
    node index
    
    # 如果不出意外的话, 数据就能正常返回了 懒得截图了
    
  2. 发布文章

    首先我们需要一些平台需要的信息,

    • TOKEN, 可以去控制台的Cookies里面寻找, 找到一个keyACCESS_TOKEN 然后复制信息
    • URL 就是需要转发的文章
    • AUTHOR是你这个账号在平台的用户名
    • PLATFORM 是你这个账号的类型, 比如我是邮箱账号 我就是为 email
    const TOKEN = ""; // 身份证明
    const URL = ""; // 需要发的文章
    const AUTHOR = ""; // 用户名
    const PLATFORM = "email"; // 账号类型 邮箱账号
    

    然后我们需要一个config文件 我也这种做法对不对 反正能用🍑 如果你觉得直接写在index.js要方便 可以简化这步

    // config.js
    module.exports = {
      // 接口地址
      api: {
        development: "",
        production: "https://api.smartsignature.io"
      },
      // 页面地址
      webUrl: {
        development: "",
        production: "https://www.matataki.io"
      }
    }
    
    // index.js
    const config = require('./config') // config
    const mode = process.env.NODE_ENV || 'production'; // 模式
    const API = config.api[mode]; // 接口
    const webUrl = config.webUrl[mode]; // 页面地址
    

    增加两个命令 dev start 来区分 developmentproduction

      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "NODE_ENV=development node index",
        "start": "NODE_ENV=production node index"
      },
    

    把内容发布到ipfs

    const qs = require("qs");
    // ...
    
    console.log('开始获取Html...');
    let resHtml = await getHtml(URL);
    console.log('获取Dom...');
    let resDom = await getDom(resHtml);
    
    let data = {
        title: resDom.title.trim(),
        author: AUTHOR,
        desc: resDom.desc.trim(),
        content: resDom.content.trim()
      };
      data.desc = data.desc.replace(/[\r\n]/g, ""); // 去除回撤换行
      data.content = data.content.replace(/[\r\n]/g, ""); // 去除回撤换行
      let hash = await postIpfs(data);
      if (!hash) return console.log("not hash", hash);
    
    // 发布到ipfs
    const postIpfs = async ({ title, author, desc, content }) => {
      try {
        if (!TOKEN) throw new Error("没有token");
        const stringifyData = qs.stringify({
          "data[title]": title,
          "data[author]": author,
          "data[desc]": desc,
          "data[content]": content
        });
        let res = await axios({
          method: "post",
          url: `${API}/post/ipfs`,
          data: stringifyData,
          headers: { "x-access-token": TOKEN }
        });
        // console.log(res.data);
        if (res.status === 200 && res.data.code === 0) {
          return res.data.hash;
        } else return false;
      } catch (error) {
        console.log(error);
        return false;
      }
    };
    

    需要的 x-access-token 已经在前面定义过了, 成功请求后会返回hash地址

    然后转存图片

    下载图片这块, 按照search到的code没有修改, 使用request请求图片, 并且写入文件, 当然我也发现一个不错的第三方库, image-downloader 这个可以很轻松的下载图片

    const FormData = require('form-data');
    const fs = require('fs')
    const request = require('request')
    const path = require('path')
    // ...
    // 图片转存
    const downloadImage = async url => {
      if (!url) {
        console.log('没有url地址')
        return false
      }
      // https://github.com/Kerminate/douban-movies/blob/9119c276b2785b329f62cca684bc6d6459a7c57e/server/tasks/smms.js
    
      // 下载图片
      const downResources = (url, imgPath) => {
        return new Promise((resolve, reject) => {
          request
            .get(url)
            .pipe(fs.createWriteStream(imgPath))
            .on('finish', () => {
              resolve()
            })
        })
      }
    
      const fileName = 'photo.png'
      const imgPath = path.resolve(__dirname, './photo.jpg')
      try {
        await downResources(url, imgPath)
        // fix Callback must be a function
        const buffer = await fs.readFileSync(imgPath)
        const base64Image = Buffer.from(buffer).toString('base64')
    
        const form = new FormData()
        form.append('smfile', Buffer.from(base64Image, 'base64'), {
          filename: fileName
        })
        let headers = form.getHeaders()
        headers['x-access-token'] = TOKEN
        const res = await axios({
            method: 'POST',
            url: `${API}/post/uploadImage`,
            headers: headers,
            data: form
          })
        // console.log(res.data)
        if (res.status === 200 && res.data.code === 0) {
          return res.data.data.cover
        } else {
          console.log('fail, status: ', res.status)
          return false
        }
      } catch (err) {
        console.log('update error', err)
        return false
      }
    };
    

    图片上传的核心我是从github里面search

    // ...
    // 这里的一些转换我没有弄明白, 前端一般直接一个file或者一个blob就上去了
    // 在node里面这个Buffer我还没有理解 希望大佬们看到了能教我一手👋!!!
    const base64Image = Buffer.from(buffer).toString('base64')
    const form = new FormData()
    form.append('smfile', Buffer.from(base64Image, 'base64'), {
      filename: fileName
    })
    // ...
    

    上传成功后会返回一个url地址, 如果是smms之类的图床上传记得多写一些判断他会判断重复的图片

    图片也有了之后就是上传文章了

    // 发布文章
    const post = async data => {
      try {
        let res = await axios({
          method: "post",
          url: `${API}/post/publish`,
          data: data,
          headers: { "x-access-token": TOKEN }
        });
        // console.log(data, res.data);
        if (res.status === 200 && res.data.code === 0) {
          return res.data;
        } else {
          console.log('fail', res.data)
          return false;
        }
      } catch (error) {
        console.log('error', error)
        return false;
      }
    };
    
      console.log('发送到Matataki...');
     // 大部分的参数按照我这个默认就好了
      let resPost = await post({
        author: AUTHOR,
        cover,
        fissionFactor: 2000,
        hash: hash,
        platform: PLATFORM,
        publickey: null,
        sign: null,
        msgParams: null,
        signId: null,
        title: resDom.title,
        is_original: 0,
        tags: "",
        cc_license: null,
        commentPayPoint: 1,
        shortContent: ""
      });
      if (resPost) {
        console.log(`发送成功, 您的文章地址: ${webUrl}/p/${resPost.data}`)
      } else {
        console.log('发送失败!!!')
      }
    

    成功后会返回一个文章id然后我们去访问console.log(`发送成功, 您的文章地址: ${webUrl}/p/${resPost.data}`)

到此流程就完全结束了!!! 归纳调用

// 开始
const init = async () => {
  console.log('开始获取Html...');
  let resHtml = await getHtml(URL);
  console.log('获取Dom...');
  let resDom = await getDom(resHtml);

  console.log('开始发送到ipfs...');
  let data = {
    title: resDom.title.trim(),
    author: AUTHOR,
    desc: resDom.desc.trim(),
    content: resDom.content.trim()
  };
  data.desc = data.desc.replace(/[\r\n]/g, ""); // 去除回撤换行
  data.content = data.content.replace(/[\r\n]/g, ""); // 去除回撤换行
  let hash = await postIpfs(data);
  if (!hash) return console.log("not hash", hash);

  console.log('转存下载图片...');
  let cover = await downloadImage(resDom.cover);
  if (!cover) return console.log('下载图片失败')
  console.log('发送到Matataki...');
  let resPost = await post({
    author: AUTHOR,
    cover,
    fissionFactor: 2000,
    hash: hash,
    platform: PLATFORM,
    publickey: null,
    sign: null,
    msgParams: null,
    signId: null,
    title: resDom.title,
    is_original: 0,
    tags: "",
    cc_license: null,
    commentPayPoint: 1,
    shortContent: ""
  });
  if (resPost) {
    console.log(`发送成功, 您的文章地址: ${webUrl}/p/${resPost.data}`)
  } else {
    console.log('发送失败!!!')
  }
};

init()
image

调用结果 看起来还不错🍑

预览地址 1991

仓库地址

我的Github


由于这是一个简单的example 所以不会弄得太复杂 简单的爬虫加上调用接口即可。

因为不太会node 全完自己瞎鼓捣, 如果写的不对或者不好的地方希望大佬们多多指点 指点

也欢迎加入QQ Group ID:718639024 来吐槽我🤮🤮🤮

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容