nodejs爬蟲--抓取CSDN某用戶全部文章

来源:https://www.cnblogs.com/xueyubao/archive/2020/02/11/node-spider-csdn.html
-Advertisement-
Play Games

最近正在學習node.js,就像搞一些東西來玩玩,於是這個簡單的爬蟲就誕生了。 ...


最近正在學習node.js,就像搞一些東西來玩玩,於是這個簡單的爬蟲就誕生了。

準備工作

  1. node.js爬蟲肯定要先安裝node.js環境
  2. 創建一個文件夾
  3. 在該文件夾打開命令行,執行npm init初始化項目

正式開始

安裝依賴

  • express 用來搭建一個簡單http伺服器,也可以使用node原生api
  • cheerio 相當於node版的jQuery,用來解析頁面
  • superagent 用來請求目標頁面
  • eventproxy 解決同時處理多個頁面的問題

直接使用npm install express cheerio superagent eventproxy來安裝依賴包,當然你也可以用別的方法。

創建建好目錄

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

創建一個Http伺服器

index.js文件中,實例化一個express對象,啟動一個Http服務

const express = require('express');

const app = express();

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

這樣就啟動了一個簡單的Http本地服務,執行node index.js後通過http://127.0.0.1:3000就可以訪問到這個伺服器。有關Express的更多內容可以參考官方文檔

編寫csdn.js模塊

先引入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');
});

然後開始編寫csdn.js

整體結構

// 引入需要的第三方包
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;

分析頁面

整體結構寫好後就要開始分析CSDN用戶文章頁面的HTML了。

隨便找一個人的博客,經過觀察發現:

  • 原創文章的完整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', function(req, 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`);
        }
        // ...
    });
});
// ...

我們可以通過console.log()或者res.send()來查看獲取的網址是否正確

遍歷獲取所有頁面的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中
                    // ...
                }
            });
        });
    });
});
// ...

解析文章信息

因為獲取到的有些文本中空格太多,所以需要用到正則表達式來去除多餘的空格。

cheerio對於Document的操作和jQuery基本一樣,所以有前端基礎的可以很輕鬆上手。

/**
 * 解析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);
    // ...
}
// ...

我們已經獲取到所有文章的信息數據,但是因為獲取各個頁面的文章時是併發非同步進行的,所以要同時利用這些數據特殊的方法。

處理併發非同步操作

這裡我使用的是“計數器”eventproxy,還有很多其他的方法都可以解決這個問題。

// ...
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
    });
});
// ...

這樣,一個簡單的node爬蟲就寫好了,執行node index.js啟動服務後,在瀏覽器中輸入http://127.0.0.1:3000/csdn/xxxx就可以獲得xxxx(這是id)的全部文章了。

完整代碼

參考文章


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ./mongo1.查看所有資料庫show dbs2.切換資料庫use 資料庫名3.查詢所有集合show collections4.查詢所有文檔db.文檔名.find()db.文檔名.find().pretty() //格式化顯示5.查詢一條,並且增加上查詢條件,例如:db.model.findOne ...
  • 使用jdbc將mysql資料庫中的內容封裝為指定對象的list集合 ...
  • 一、問題來由 ViewPager控制項很大程度上滿足了開發者開發頁面左右移動切換的功能,使用非常方便。但是使用中發現,在刪除或者修改數據的時候,PagerAdapter無法像BaseAdapter那樣僅通過notifyDataSetChanged方法通知刷新View。有人提出一種解決方案:給Viewp ...
  • 一、手風琴效果 <style> *{ margin:0; padding:0; } ul{ width: 960px; height: 300px; margin:100px auto; border:1px solid red; ​ } ul li { list-style: none; widt ...
  • 1.HTML基礎標簽圖片常見代碼形式<img src="圖片路徑地址" alt="屬性名" title="占位符">常見的圖片格式為以下三種:.jpg(圖片有損壓縮,影響畫質)、.png(圖片無損壓縮、容積大、具有透明通道)、.gif(動圖)。圖片路徑地址分為本地圖片和網路圖片,本地圖片中分為絕對路 ...
  • 由於JavaScript 的 replace 只能替換一次,因此另外編寫一個能現替換全部匹配內容方法,代碼如下: /* 把 content 中所有的 searchValue 替換為 replaceValue */ function replaceAll(content,searchValue,rep ...
  • 不是 HTML 標簽。它為瀏覽器提供一項信息(聲明),即 HTML 是用什麼版本編寫的。 HTML是一門超文本標記語言,瀏覽器解析html標記後,展示頁面內容,目前流行HTML5,俗稱H5。靜態網頁的拓展名htm或html新建網頁的步驟:1、新建文本文檔,更改拓展名為htm或html2、編寫網頁的基... ...
  • 一、定寬高 1、絕對定位和負margin值: <section class="absolute"> <div></div> </section> <style> section{ display:block; } section.absolute { width: 100px; height: 10 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...