nodejs爬虫--抓取CSDN某用户全部文章

前端开发 作者: 2024-08-23 09:45:01
最近正在学习node.js,就像搞一些东西来玩玩,于是这个简单的爬虫就诞生了。
  1. node.js爬虫肯定要先安装node.js环境
  2. 创建一个文件夹
  3. 在该文件夹打开命令行,执行npm init初始化项目

安装依赖

  • express 用来搭建一个简单http服务器,也可以使用node原生api
  • cheerio 相当于node版的jQuery,用来解析页面
  • superagent 用来请求目标页面
  • eventproxy 解决同时处理多个页面的问题

创建建好目录

node-spider-csdn
├─ .gitignore 
├─ node_modules 
├─ README.md 
├─ index.js 			项目入口
├─ package-lock.json
├─ package.json
└─ routes
  └─ csdn.js			爬虫主要代码

创建一个Http服务器

const express = require('express');

const app = express();

app.listen(3000,function() {
    console.log('running in http://127.0.0.1:3000');
});

编写csdn.js模块

const express = require('express');
const csdn = require('./routes/csdn.js');

const app = express();

app.use(csdn);

app.listen(3000,function() {
    console.log('running in http://127.0.0.1:3000');
});

整体结构

// 引入需要的第三方包
const cheerio = require('cheerio');
const superagent = require('superagent');
const express = require('express');
const eventproxy = require('eventproxy');

const router = express.Router(); // 挂载路由
const ep = new eventproxy();

router.get('/csdn/:name',function(req,res) {
    const name = req.params.name; // 用户id
    // 具体实现...
});

// 将router暴露出去
module.exports = router;

分析页面

  • 原创文章的完整url:https://blog.csdn.net/l1028386804/article/list/2?t=1
  • CSDN的文章列表是40篇一页
  • 分页控件是动态生成的,所以无法直接通过HTML解析获得
  • 文章信息都在类名为article-item-box的盒子中
  • id信息在该盒子的data-articleid属性中

获取所有文章页面

/**
 * 获取总文章数目
 * @param {String} url 页面路径
 * @param {Function} callback 回调
 */
let getArticleNum = function (url,callback) {
    superagent.get(url).end(function (err,html) {
        if (err) {
            console.log(`err = ${err}`);
        }
        let $ = cheerio.load(html.text);
        let num = parseInt($('.data-info dl').first().attr('title'));

        callback(num);
    });
};
// ...
router.get('/csdn/:name',res) {
    const name = req.params.name;
    getArticleNum(`https://blog.csdn.net/${name}`,function (num) {
        let pages = []; // 保存要抓取的页面

        let pageNum = Math.ceil(num / 40); // 计算一共有多少页面

        for (let i = 1; i <= pageNum; i++) {
            pages.push(`https://blog.csdn.net/${name}/article/list/${i}?t=1`);
        }
        // ...
    });
});
// ...

遍历获取所有页面的HTML

// ...
router.get('/csdn/:name',function (req,res) {
    const name = req.params.name;

    getArticleNum(`https://blog.csdn.net/${name}`,function (num) {
        let pages = [];
        let articleData = []; // 保存所有文章数据
        
        let pageNum = Math.ceil(num / 40); // 计算一共有多少页面

        for (let i = 1; i <= pageNum; i++) {
            pages.push(`https://blog.csdn.net/${name}/article/list/${i}?t=1`);
        }

        // 获取所有页面的文章信息
        pages.forEach(function (targetUrl) {
            superagent.get(targetUrl).end(function (err,html) {
                if (err) {
                    console.log(`err ${err}`);
                }
                let $ = cheerio.load(html.text);
                
				// 当前页面的文章列表
                let articlesHtml = $('.article-list .article-item-box');

                // 遍历当前页的文章列表
                for (let i = 0; i < articlesHtml.length; i++) {
                    // 解析获取文章信息
                    // push到articleData中
                    // ...
                }
            });
        });
    });
});
// ...

解析文章信息

/**
 * 解析html字符串,获取文章信息
 * @param {String} html 包含文章信息的html
 * @param {Number} index 文章索引
 */
let analysisHtml = function (html,index) {
    return {
        id: html.eq(index).attr('data-articleid'),title: html.eq(index).find('h4 a').text().replace(/\s+/g,'').slice(2),link: html.eq(index).find('a').attr('href'),abstract: html.eq(index).find('.content a').text().replace(/\s+/g,''),shared_time: html.eq(index).find('.info-box .date').text().replace(/\s+/,read_count: html.eq(index).find('.info-box .read-num .num').first().text().replace(/\s+/,comment_count: html.eq(index).find('.info-box .read-num .num').last().text().replace(/\s+/,'')
    };
};
// ...
// 遍历当前页的文章列表
for (let i = 0; i < articlesHtml.length; i++) {
    let article = analysisHtml(articlesHtml,i);
    articleData.push(article);
    // ...
}
// ...

处理并发异步操作

// ...
pages.forEach(function (targetUrl) {
    superagent.get(targetUrl).end(function (err,html) {
        if (err) {
            console.log(`err ${err}`);
        }
        let $ = cheerio.load(html.text);

        let articlesHtml = $('.article-list .article-item-box');

        for (let i = 0; i < articlesHtml.length; i++) {
            let article = analysisHtml(articlesHtml,i);
            articleData.push(article);

            ep.emit('blogArtc',article); // 计数器
        }
    });
});

// 当所有'blogArtc'完成后,触发回调
ep.after('blogArtc',num,function (data) {
    res.json({
        status_code: 0,data: data
    });
});
// ...
原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_67175.html