nodejs实现定时爬取微博热搜
程序員導航站:https://iiter.cn
The summer is coming
我知道,那些夏天,就像青春一樣回不來。 - 宋冬野
青春是回不來了,倒是要準備渡過在西安的第三個夏天了。
廢話
我發(fā)現(xiàn),自己對 coding 這件事的稱呼,從敲代碼 改為 寫代碼 了。
emmm…敲代碼,自我感覺,就像是,習慣了用 const 定義常量的我看到別人用 var 定義的常量。
對,優(yōu)雅!
寫代碼 這三個字,顯得更為優(yōu)雅一些,更像是在創(chuàng)作,打磨一件精致的作品。
改編自 掘金站長 的一句話:
子非猿,安之 coding 之樂也。
看完本文的收獲
- ctrl + c
- ctrl + v
- nodejs 入門級爬蟲
為何寫爬蟲相關的文章
最近訪問 艾特網(wǎng) 的時候發(fā)現(xiàn)請求有點慢。
后來經(jīng)過一番檢查,發(fā)現(xiàn)首頁中搜索熱點需要每次去爬取百度熱搜的數(shù)據(jù)并當做接口返回給前端,由于是服務端渲染,接口堵塞就容易出現(xiàn)訪問較慢的情況。
就想著對這個接口進行一次重構。
解決方案
- 設置定時任務,每隔1分鐘/3分鐘/5分鐘爬取新浪微博實時熱搜(新浪微博熱搜點擊率更高一些)
- 爬取到數(shù)據(jù)后不直接返回給前端,先寫入一個.json格式的文件。
- 服務端渲染的后臺接口請求并返回給前端json文件的內(nèi)容
需求捋清楚以后就可以開干了。
創(chuàng)建工程
初始化
首先得找到目標站點,如下:(微博實時熱搜)
https://s.weibo.com/top/summary?cate=realtimehot
創(chuàng)建文件夾 weibo
進入文件夾根目錄
使用 npm init -y 快速初始化一個項目
安裝依賴
創(chuàng)建app.js文件
安裝以下依賴
npm i cheerio superagent -D關于superagent和cheerio的介紹
superagent 是一個輕量級、漸進式的請求庫,內(nèi)部依賴 nodejs 原生的請求 api,適用于 nodejs 環(huán)境。
cheerio 是 nodejs 的抓取頁面模塊,為服務器特別定制的,快速、靈活、實施的 jQuery 核心實現(xiàn)。適合各種 Web 爬蟲程序。node.js 版的 jQuery。
代碼編寫
打開 app.js ,開始完成主要功能
首先在頂部引入cheerio 、superagent 以及 nodejs 中的 fs 模塊
const cheerio = require("cheerio"); const superagent = require("superagent"); const fs = require("fs");通過變量的方式聲明熱搜的url,便于后面 復用
const weiboURL = "https://s.weibo.com"; const hotSearchURL = weiboURL + "/top/summary?cate=realtimehot";superagent
使用 superagent 發(fā)送get請求
superagent 的 get 方法接收兩個參數(shù)。第一個是請求的 url 地址,第二個是請求成功后的回調(diào)函數(shù)。
回調(diào)函數(shù)有倆參數(shù),第一個參數(shù)為 error ,如果請求成功,則返回 null,反之則拋出錯誤。第二個參數(shù)是請求成功后的 響應體
網(wǎng)頁元素分析
打開目標站對網(wǎng)頁中的 DOM 元素進行一波分析。
對 jQuery 比較熟的小老弟,看到下圖如此簡潔清晰明了的 DOM 結構,是不是有 N 種取出它每個 tr 中的數(shù)據(jù)并 push 到一個 Array 里的方法呢?
對!我們最終的目的就是要通過 jQuery 的語法,遍歷每個 tr ,并將其每一項的 熱搜地址 、熱搜內(nèi)容 、 熱度值 、序號 、表情等信息 push 進一個空數(shù)組中
再將它通過 nodejs 的 fs 模塊,寫入一個 json 文件中。
jQuery 遍歷拿出數(shù)據(jù)
使用 jQuery 的 each 方法,對 tbody 中的每一項 tr 進行遍歷,回調(diào)參數(shù)中第一個參數(shù)為遍歷的下標 index,第二個參數(shù)為當前遍歷的元素,一般 $(this) 指向的就是當前遍歷的元素。
let hotList = []; $("#pl_top_realtimehot table tbody tr").each(function (index) {if (index !== 0) {const $td = $(this).children().eq(1);const link = weiboURL + $td.find("a").attr("href");const text = $td.find("a").text();const hotValue = $td.find("span").text();const icon = $td.find("img").attr("src")? "https:" + $td.find("img").attr("src"): "";hotList.push({index,link,text,hotValue,icon,});} });cheerio 包裝請求后的響應體
在 nodejs 中,要想向上面那樣愉快的寫 jQuery 語法,還得將請求成功后返回的響應體,用 cheerio 的 load 方法進行包裝。
const $ = cheerio.load(res.text);寫入 json 文件
接著使用 nodejs 的 fs 模塊,將創(chuàng)建好的數(shù)組轉成 json字符串,最后寫入當前文件目錄下的 hotSearch.json 文件中(無此文件則會自動創(chuàng)建)。
fs.writeFileSync(`${__dirname}/hotSearch.json`,JSON.stringify(hotList),"utf-8" );完整代碼如下:
const cheerio = require("cheerio"); const superagent = require("superagent"); const fs = require("fs"); const weiboURL = "https://s.weibo.com"; const hotSearchURL = weiboURL + "/top/summary?cate=realtimehot"; superagent.get(hotSearchURL, (err, res) => {if (err) console.error(err);const $ = cheerio.load(res.text);let hotList = [];$("#pl_top_realtimehot table tbody tr").each(function (index) {if (index !== 0) {const $td = $(this).children().eq(1);const link = weiboURL + $td.find("a").attr("href");const text = $td.find("a").text();const hotValue = $td.find("span").text();const icon = $td.find("img").attr("src")? "https:" + $td.find("img").attr("src"): "";hotList.push({index,link,text,hotValue,icon,});}});fs.writeFileSync(`${__dirname}/hotSearch.json`,JSON.stringify(hotList),"utf-8"); });打開終端,輸入 node app,可看到根目錄下多了個 hotSearch.json 文件。
定時爬取
雖然代碼可以運行,也能爬取到數(shù)據(jù)并存入 json 文件。
但是,每次都要手動運行,才能爬取到當前時間段的熱搜數(shù)據(jù),這一點都 不人性化!
最近微博熱搜瓜這么多,咱可是一秒鐘可都不能耽擱。我們最開始期望的是每隔多長時間 定時執(zhí)行爬取 操作。瓜可不能停!
接下來,對代碼進行 小部分改造。
數(shù)據(jù)請求封裝
由于 superagent 請求是個異步方法,我們可以將整個請求方法用 Promise 封裝起來,然后 每隔指定時間 調(diào)用此方法即可。
function getHotSearchList() {return new Promise((resolve, reject) => {superagent.get(hotSearchURL, (err, res) => {if (err) reject("request error");const $ = cheerio.load(res.text);let hotList = [];$("#pl_top_realtimehot table tbody tr").each(function (index) {if (index !== 0) {const $td = $(this).children().eq(1);const link = weiboURL + $td.find("a").attr("href");const text = $td.find("a").text();const hotValue = $td.find("span").text();const icon = $td.find("img").attr("src")? "https:" + $td.find("img").attr("src"): "";hotList.push({index,link,text,hotValue,icon,});}});hotList.length ? resolve(hotList) : reject("errer");});}); }node-schedule 詳解
定時任務我們可以使用 node-schedule 這個 nodejs庫 來完成。
https://github.com/node-schedule/node-schedule
先安裝
npm i node-schedule -D頭部引入
const nodeSchedule = require("node-schedule");用法(每分鐘的第 30 秒定時執(zhí)行一次):
const rule = "30 * * * * *"; schedule.scheduleJob(rule, () => {console.log(new Date()); });規(guī)則參數(shù):
* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun) │ │ │ │ └───── month (1 - 12) │ │ │ └────────── day of month (1 - 31) │ │ └─────────────── hour (0 - 23) │ └──────────────────── minute (0 - 59) └───────────────────────── second (0 - 59, OPTIONAL)6 個占位符從左到右依次代表:秒、分、時、日、月、周幾
* 表示通配符,匹配任意。當 * 為秒時,表示任意秒都會觸發(fā),其他類推。
來看一個 每小時的第20分鐘20秒 定時執(zhí)行的規(guī)則:
更多規(guī)則自行搭配。
定時爬取,寫入文件
使用定時任務來執(zhí)行上面的請求數(shù)據(jù),寫入文件操作:
nodeSchedule.scheduleJob("30 * * * * *", async function () {try {const hotList = await getHotSearchList();await fs.writeFileSync(`${__dirname}/hotSearch.json`,JSON.stringify(hotList),"utf-8");} catch (error) {console.error(error);} });哦對,別忘了 捕獲異常
下面貼上完整代碼(可直接 ctrl c/v):
const cheerio = require("cheerio"); const superagent = require("superagent"); const fs = require("fs"); const nodeSchedule = require("node-schedule"); const weiboURL = "https://s.weibo.com"; const hotSearchURL = weiboURL + "/top/summary?cate=realtimehot"; function getHotSearchList() {return new Promise((resolve, reject) => {superagent.get(hotSearchURL, (err, res) => {if (err) reject("request error");const $ = cheerio.load(res.text);let hotList = [];$("#pl_top_realtimehot table tbody tr").each(function (index) {if (index !== 0) {const $td = $(this).children().eq(1);const link = weiboURL + $td.find("a").attr("href");const text = $td.find("a").text();const hotValue = $td.find("span").text();const icon = $td.find("img").attr("src")? "https:" + $td.find("img").attr("src"): "";hotList.push({index,link,text,hotValue,icon,});}});hotList.length ? resolve(hotList) : reject("errer");});}); } nodeSchedule.scheduleJob("30 * * * * *", async function () {try {const hotList = await getHotSearchList();await fs.writeFileSync(`${__dirname}/hotSearch.json`,JSON.stringify(hotList),"utf-8");} catch (error) {console.error(error);} });各種玩法
-
以上代碼可直接集成進現(xiàn)有的 express koa eggjs 或者原生的 nodejs 項目中,作為接口返回給前端。
-
集成進 Serverless,作為接口返回給前端。
-
對接微信公眾號,發(fā)送 熱搜 關鍵字即可實時獲取熱搜數(shù)據(jù)。
-
集成進 微信機器人 ,每天在指定的時間給自己/群里發(fā)送微博熱搜數(shù)據(jù)。
-
other…
程序員導航站:https://iiter.cn
代碼 github 已開源:
https://github.com/isnl/weibo-hotSearch-crawler
總結
以上是生活随笔為你收集整理的nodejs实现定时爬取微博热搜的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wifi密码怎么重新设置
- 下一篇: node.js之文件读写模块,配合递归函