一只搬运文章的小毛贼
发布于 6 年前 作者 18820227745 2983 次浏览 来自 分享

不要问我为啥要写这样的代码,嘿嘿嘿~~~~~

1.文章自动上传到steemit

本来想分析网站的api进行上传的,没想到steemit做了众多限制,感觉有些麻烦所以采用直接控制浏览器的方法上传文章。 node.js使用selenium-webdriver模块可以轻松控制浏览器。


  /**
   * 登录
   */
  async login() {
    let url = `https://steemit.com/login.html`;
    let _this = this;
    let returnJson = {
      result: false,
      value: ""
    };
    return new Promise(async (resolve, reject) => {
      await _this.web.get(url);
      await _this.web.findElements(By.css("input")).then(async res => {
        await res[1].sendKeys(_this.__username);//设置登录用户名
        await res[2].sendKeys(_this.__password);//设置登录密码
        await _this.web
          .findElement(By.css(".login-modal-buttons>button"))
          .click();
        await _this.web.wait(until.urlIs("https://steemit.com/welcome"), 4000);
        await _this.web
          .findElement(By.css(".show-for-medium.submit-story"))
          .click();
        returnJson.result = true;
        returnJson.value = "login success";
        console.log("login success");
        resolve(returnJson);
        return returnJson;
      });
    });
  }

  /**
   * 提交文章
   * @param {*} title 文章标题
   * @param {*} post 文章体
   * @param {*} tags 文章标签
   */
  async submit(title, post, tags) {
    let url = `https://steemit.com/submit.html`;
    let _this = this;
    let returnJson = {
      result: false,
      value: ""
    };
    return new Promise(async (resolve, reject) => {
      await _this.web.get(url);
      //设定延时,确保进入提交页面
      await _this.web.wait(
        until.urlIs("https://steemit.com/submit.html"),
        4000
      );
      //标签定位,打开网页分析一下就oK了
      await _this.web.findElements(By.css("input")).then(async res => {
        await res[1].sendKeys(title);
        await res[3].sendKeys(tags);
        await _this.web.wait(until.elementLocated(By.css("textarea")), 3000);
        await _this.web.findElement(By.css("textarea")).sendKeys(post);
        await _this.web.findElement(By.css(".button")).click();
        returnJson.result = true;
        returnJson.value = "submit success";
        resolve(returnJson);
        return returnJson;
      });
    });
  }

标签定位大家只用打开浏览器看一下就知道了 先调用登录接口,登录成功后调用上传文章接口

2.文章来源-从medium批量下载文章到redis队列

根据一位道友提供的api接口可以根据作者名获取到该作者的文章列表。

  /**
   * 获取Medium文章列表
   * @param {*} user
   * @param {*} limit
   */
  async getPostByUsername(username, limit) {
    let returnJson = {
      result: false,
      value: ""
    };
    let _this = this;
    return new Promise((resolve, reject) => {
      if (
        typeof limit != "number" ||
        limit <= 0 ||
        limit > 100 ||
        typeof username != "string" ||
        username === ""
      ) {
        returnJson.result = false;
        returnJson.value = "参数不正确";
        resolve(returnJson);
        return returnJson;
      } else {
        let JSONDate = {
          query: `query PostQuery($username: String!, $limit: Int!){
                posts(username: $username, limit: $limit) {
                  title
                  firstPublishedAt
                  url
                  content {
                    subtitle
                  }
                }
                user(username: $username) {
                  username
                  name
                  bio
                }
              }`,
          variables: `{
                "username": "${username}",
                "limit": ${limit}
              }`,
          operationName: "PostQuery"
        };

        superagent
          .post("https://micro-medium-api.now.sh/graphql")
          .send(JSONDate) // sends a JSON post body
          .set("accept", "json")
          .end((err, res) => {
            if (err) {
              console.log("获取文章列表出错: ", err);
              returnJson.result = false;
              returnJson.value = "获取文章列表出错: " + err;
              resolve(returnJson);
              return returnJson;
            } else {
              returnJson.result = true;
              returnJson.value = res.text;
              resolve(returnJson);
              return returnJson;
            }
          });
      }
    });
  }

又是一个神器h2m,(根据url将html转化成md)

npm install h2m -g
h2m https://baidu.com

于是我有封装了一个接口

  /**
   * 根据url下载Medium文章到本地
   * @param {*} url
   * @param {*} savePath
   * @param {*} saveName
   */
  async downloadPostByUrl(url, saveName,author,category) {
    let returnJson = {
      result: false,
      value: ""
    };
    let _this = this;
    url = encodeURI(url);
    return new Promise((resolve, reject) => {
      if (url === "" || saveName === "" ) {
        returnJson.result = false;
        returnJson.value = "参数不正确";
        resolve(returnJson);
        return returnJson;
      } else {
        let command = `h2m ${url}`;
        console.log(command);
        exec(command, {timeout: 1000*60*3,maxBuffer: 20*1024*1024},(error, stdout, stderr) => {
          console.log(`stderr: ${stderr}`);
          if (error) {
            console.log(`exec error: ${error}`);
            returnJson.result = false;
            returnJson.value = `exec error: ${error}`;
            resolve(returnJson);
          } else {
            returnJson.result = true;
            returnJson.value = `根据url下载Medium文章到本地成功`;
            console.log('根据url下载Medium文章到本地成功');
            resolve(returnJson);
            let saveStr = stdout.slice(stdout.indexOf('\n\n---\n'),stdout.indexOf('One clap, two clap, three clap, forty?'))
            let title = saveName;
            let postJson = {
              title: title,
              author: author,
              category: category,
              content: saveStr
            }
			//存储到redis队列中
            _this.queueJson[category].publish(postJson)
          }
          return returnJson;
        });
      }
    });
  }

小坑:exec使用时stdout, stderr默认大小是200K,要把maxBuffer设置大一点才行。

3.如何保证上传的文章不重复呢?

真心感谢无所不能的npm 使用redis-message-queue可以轻松创建出值唯一的redis队列。

this.client = new rmq.UniqueQueue(this.name, port, host);

4.运行

代码位置:https://gitee.com/null_639_7345/steemit 1.git代码到本地 2.下载firefox驱动文件到本地,安装firefox浏览器 3.npm i h2m -g 4.修改配置文件config/default.js

module.exports = {
    cwd : 'F:\\test1\\steemit\\',//项目根目录
    redisHost: '192.168.10.6',//redis服务器ip
    redisPost: 6379,
    promulgatorName: '***',//steemit用户名
    promulgatorPassword: "***",//steemit密码
    categories:[
        {
            name: 'popular',//steemit上传文章的分类(tag)
            //Medium的作者列表
            origin: ['joshrose','JessicaLexicus','ThunderPuff','usemuzli','black_metallic']
        }
    ]
};

5.node index.js

最后可以告诉大家一个激动的好消息。 image.png

4 回复

medium 的文章质量很高, 我自己用爬虫去爬. 主要是翻页的时候比较麻烦,我是滚动很长一段距离,延时十几秒,然后抓取页面列表 有没有通过关键词获取文章的api? 我做的太复杂了 下载文章里面的gist代码块和图片是如何处理的呢?

@phpsmarter 我也没找到通过关键字查找的api,你通过h2m下载下来的md文档里面的图片是正常的,代码也应该是正常的

@phpsmarter 如果你非要用关键词的方法做,可以先试试分析网站有没有办法调用现成的api,如果没找到api可以试试控制浏览器爬取搜索后的网页,爬取出文章路径再通过h2m工具下载下来后,做一下格式化就可以了

@18820227745 好, 我去试试, 但是文章, 我是觉得是使用惰性加载比较好, 我先只获取文章列表, 如果点击了列表项在去获取详细信息.

回到顶部