`
JerryWang_SAP
  • 浏览: 1003525 次
  • 性别: Icon_minigender_1
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

批量导出某个简书用户的所有文章列表和文章超链接

阅读更多

简书改版后,根据文章标题搜索文章的功能就不见了。

 

虽然简书提供了批量下载文章的功能,但是下载到本地的文章都是markdown格式的,不包含文章的链接,这不满足我的需求。

既然我是程序员,没有这个功能我就自己实现一个。

打开简书首页,发现默认只显示8篇文章,用鼠标滑动到屏幕底部后,会触发一个懒加载事件,到后台读取更多的文章列表,所以文章读取在服务器端是采取的分页实现。

 

打开Chrome开发者工具,观察网络请求,请求url中99b8712e8850是我简书用户id,page=2,3,4这些是分页代码。

 

每页的文章内容以html格式包含在响应结构里:

 

我关心的只是文章标题和文章链接,如上图高亮字段所示。

最开始我写了一个nodejs应用,代码如下:

var request = require('request');
var jsdom = require("jsdom");
var JSDOM = jsdom.JSDOM;
const PREFIX = "https://www.jianshu.com";
const PAGE = "https://www.jianshu.com/u/99b8712e8850?order_by=shared_at&page=";
const MAX = 2;

var mArticleResult = new Map();
var pageNumber;
/* a given article: https://www.jianshu.com/p/963cd23fb092
  value got from API: /p/5c1d0319dc42
*/
var lastPageReached = false;
var url = "";

var aHandlers = [];

// use limited for loop to ease testing
for(var i = 0; i < MAX; i++){
  pageNumber = i + 1;
  var url = PAGE + pageNumber;
  // console.log("current page: " + url);
  var pageOptions = {
        url: url,
        method: "GET",
        headers: {
            "Accept": "text/html"
        }
  };
  aHandlers.push(getArticles(pageOptions, pageNumber));
  if( lastPageReached)
    break;
}

console.log("promise handler size: " + aHandlers.length);

Promise.all(aHandlers).then(function(){
  var articleIndex = 0;
  for (var [key, value] of mArticleResult) {
    console.log("Article[" + articleIndex++ + "]: " + key + " = " + value);
  }
  console.log("done");
}
  );

function getArticles(pageOptions, pageNumber) {
  return new Promise(function(resolve,reject){
      var requestC = request.defaults({jar: true});

      requestC(pageOptions,function(error,response,body){
        if( error){
          console.log("error: " + error);
          resolve(error);
        }
        var document = new JSDOM(body).window.document;
        var content = document.getElementsByTagName("li");

        for( var i =0; i < content.length; i++){
          var li = content[i];
          var children = li.childNodes;
          for( var j = 0; j < children.length; j++){
              var eachChild = children[j];
              if( eachChild.nodeName == "DIV"){
                var grandChild = eachChild.childNodes;
                for( var k = 0; k < grandChild.length; k++){
                  var grand = grandChild[k];
                  if( grand.nodeName == "A"){
                    var fragment = grand.getAttribute("href");
                    if( fragment.indexOf("/p") < 0)
                      continue;
                    console.log("title: " + grand.text);
                    var wholeURL = PREFIX + fragment;
                    console.log("url: " + wholeURL);
                    if( mArticleResult.has(grand.text)){
                      lastPageReached = true;
                      console.log("article size: " + mArticleResult.size);
                      resolve(pageNumber);
                    }
                    mArticleResult.set(grand.text, wholeURL);
                  }
                }
              }
          }
        }// end of outer loop
        resolve(pageNumber);
      }); 
     });
}

原理就是使用nodejs的request module,向简书网站同时发起多个请求,每个请求读取一页的简书文章。

后来发现这种方法在并发请求数大于10个的时候就无法工作,简书网站会拒绝该类请求,返回HTTP 429状态码。

所以最后我采用了最简单的同步请求实现,使用了nodejs提供的sync-request在循环里发起请求。

var request = require("sync-request");
var jsdom = require("jsdom");
var JSDOM = jsdom.JSDOM;
var textEncoding = require('text-encoding'); 
var textDecoder = textEncoding.TextDecoder;

const PREFIX = "https://www.jianshu.com";
const PAGE = "https://www.jianshu.com/u/99b8712e8850?order_by=shared_at&page=";
const MAX = 100;

var mArticleResult = new Map();
var lastPageReached = false;
var pageNumber;
/* a given article: https://www.jianshu.com/p/963cd23fb092
  value got from API: /p/5c1d0319dc42
*/

try {
    // use limited for loop to ease testing
    for (var i = 0; i < MAX; i++) {
        if( lastPageReached)
          break;
        pageNumber = i + 1;
        var url = PAGE + pageNumber;
        console.log("current page: " + url);
        var response = request('GET', url);
        var html = new textDecoder("utf-8").decode(response.body);
        handleResponseHTML(html);
    }
} 
catch (e) {

}

var articleIndex = 0;
var resultHTML = "<html>";

const fs = require('fs');

/*
<HTML>
<p>
<a href="https://www.baidu.com">eee</a>
</p>

<p><a>22</a></p>
<p><a>33</a></p>
</HTML>
*/

var index = 1;
for (var [key, value] of mArticleResult) {
    var article = "<p><a href=\"" + key + "\">" + 
    index++ + ". " + value + "</a></p>" + "\n";
    resultHTML = resultHTML + article;
    console.log("Article[" + articleIndex++ + "]: " + value + " = " + key);
}

resultHTML = resultHTML + "</html>";

var pwd = process.cwd() + "/jianshu.html";

fs.appendFileSync(pwd, resultHTML);

console.log("done");



function handleResponseHTML(html) {
    var document = new JSDOM(html).window.document;
    var content = document.getElementsByTagName("li");

    for (var i = 0; i < content.length; i++) {
        var li = content[i];
        var children = li.childNodes;
        for (var j = 0; j < children.length; j++) {
            var eachChild = children[j];
            if (eachChild.nodeName == "DIV") {
                var grandChild = eachChild.childNodes;
                for (var k = 0; k < grandChild.length; k++) {
                    var grand = grandChild[k];
                    if (grand.nodeName == "A") {
                        var fragment = grand.getAttribute("href");
                        if (fragment.indexOf("/p") < 0)
                            continue;
                        // console.log("title: " + grand.text);
                        var wholeURL = PREFIX + fragment;
                        // console.log("url: " + wholeURL);
                        if (mArticleResult.has(wholeURL)) {
                            lastPageReached = true;
                            console.log("article size: " + mArticleResult.size);
                            return;
                        }
                        mArticleResult.set(wholeURL, grand.text);
                    }
                }
            }
        }
    }
}

这个nodejs应用执行后,会在本地生成一个html文件,包含每篇文章的标题和超链接。

 

要获取更多Jerry的原创文章,请关注公众号"汪子熙":

 

 
0
0
分享到:
评论

相关推荐

    爬取简书文章.zip

    使用Scrapy+ Selenium+ChromeDriver爬取简书所有文章,然后存储到mysql数据库中,作为爬虫的练习。当然也可以用这些文章进行数据分析等等。 该压缩包有项目的完整源码和数据库的sql文件。读者可以直接使用。

    Python-简书网的用户抓取分析

    2. 数据分析:可以使用Pandas进行数据整理,对用户活跃度、文章数量、粉丝数等进行统计分析,了解简书用户群体特征。 3. 可视化展示:通过Matplotlib或Seaborn等库绘制图表,如用户数量分布、活跃度趋势图等,辅助...

    简书个人文章备份,图片批量导出小工具-附件资源

    简书个人文章备份,图片批量导出小工具-附件资源

    抓取CSDN博客以及简书博客的文章合并成kindle观看的Mobi格式

    1. **网络爬虫**:为了从CSDN和简书博客抓取文章,首先我们需要使用网络爬虫技术。Python提供了一些强大的库,如BeautifulSoup、Scrapy和Requests,可以方便地实现网页内容的抓取。通过发送HTTP请求到博主的主页,...

    简书项目及命令文档

    为了方便用户管理和寻找感兴趣的内容,简书允许你在文章中添加分类和标签。分类通常是固定的,如小说、科技、旅行等;而标签则更具灵活性,可以根据文章主题自定义,有助于提高文章的可搜索性。 二、命令行操作 ...

    仿简书使用nuxtVuenode完成简书所有功能

    【描述】:“仿简书,使用nuxt Vue node完成简书所有功能”意味着这个项目是基于Nuxt.js、Vue.js和Node.js技术栈,目的是创建一个类似于简书的在线写作和阅读平台。Nuxt.js是一个用于构建Vue.js应用程序的框架,它...

    爬虫脚本项目源码-爬取简书个人文章

    在这个名为“爬虫脚本项目源码-爬取简书个人文章”的压缩包中,包含了一个用Python编写的爬虫程序,旨在从简书平台上抓取特定用户的文章数据。简书是一个流行的在线创作社区,拥有丰富的用户生成内容,尤其是各种...

    Python-scrapy实现爬取简书首页热门文章

    在这个项目中,我们将探讨如何使用scrapy库来实现爬取简书首页的热门文章。简书作为一个流行的在线写作平台,其首页经常更新各类热门文章,通过爬虫获取这些数据,可以进行数据分析、内容挖掘或其他自定义用途。 ...

    仿简书Wordpress主题

    【仿简书WordPress主题】是一种针对WordPress平台设计的网站主题,旨在模仿国内知名写作社区“简书”的界面风格和用户体验。这款主题由Pete开发,它不仅在视觉上与简书相似,还试图在功能和易用性上接近原版应用,为...

    简书文章书写怎么操作.docx

    文章书写区域是简书平台用于创建和编辑文章的核心部分。在这一区域内,用户可以通过简书提供的编辑工具进行文字输入、格式调整等操作。 #### 三、设置文章标题 新创建的文章,默认标题为“无标题笔记”。用户可以...

    简书常见问题.docx

    2. **查看文章列表**:在我的主页中,可以看到已发布的所有文章和文集。 #### Q10:如何备份我的文章? 为了保护用户的创作成果,简书提供了文章导出功能,方便用户备份自己的文章: 1. **设置菜单**:在设置菜单...

    那个打包的不对用这个可以:简书动态爬取用户文章分析用户的写作习惯

    标题中的“那个打包的不对用这个可以:简书动态爬取用户文章分析用户的写作习惯”表明这是一个关于数据爬取和用户行为分析的项目。在这个项目中,开发者使用了爬虫技术来获取简书平台上的用户文章数据,通过对这些...

    仿简书的源码源码源码

    简书的首页通常包含各种文章的列表,分类展示,推荐系统,以及用户互动元素如评论和点赞。开发者可能通过这个项目学习到如何设计和实现一个动态、响应式的网页布局,以及如何通过API获取和展示数据。 【标签】"web...

    Python爬虫练习:bilibili用户信息爬取、下载工具、房天下新房二手房爬虫、简书全站文章爬取等.zip

    Python爬虫练习:bilibili用户信息爬取、下载工具、房天下新房二手房爬虫、简书全站文章爬取等.zip 申明 个人的一些python 爬虫练习 仅限于学习交流 目录 Scrapy-Redis 房天下新房二手房redis分布式爬虫 Scrapy ...

    仿简书APP源码

    简书作为一个流行的创作社区,其应用包含了许多功能,如用户注册登录、文章发布、阅读、评论、点赞、收藏等。因此,这个仿制源码可能涵盖了这些核心功能的实现,开发者可以通过研究源码学习如何实现这些功能。 在...

    Android仿简书长按文章生成图片效果

    这个功能在简书APP中尤其受到用户喜爱,它允许用户将文章内容转化为一张长图片,便于保存或分享。以下我们将详细讨论如何实现这一功能。 首先,我们要处理文章页的内容展示。在Android中,通常会使用`WebView`来...

    swift-仿简书网页网络加载过渡动画

    这包括但不限于使用SwiftUI或UIKit来构建页面结构,如文章列表、文章详情页、评论区域等。SwiftUI是Apple推出的新一代界面构建框架,它可以提供声明式编程方式,使界面构建更为直观。而UIKit则是传统的界面构建工具...

    Python爬取简书个人文章.rar

    在爬取简书文章时,我们首先需要获取用户的文章链接,这通常通过访问用户的个人主页并抓取页面上的链接实现。requests库的`get()`方法可以获取网页内容,`headers`参数可以设置请求头,模拟浏览器行为以避免被服务器...

    提升简书app用户忠诚度的策略研究.pdf

    【摘要】中的文章主要探讨了如何提升简书APP的用户忠诚度,通过对服务质量、产品质量、用户关系信任、用户满意度和转换成本四个方面进行分析。研究发现简书APP的用户忠诚度较低,为此提出了改进措施和提升策略。 ...

Global site tag (gtag.js) - Google Analytics