摘要:前言根據(jù)慕課網(wǎng)實現(xiàn)電影微信公眾號前后端開發(fā)學習后的改造由于上下班期間會看會小說,但是無奈廣告太多,還要收費,于是結(jié)合課程,進行開發(fā),并上傳到自己的微信小程序。
前言:根據(jù)慕課網(wǎng) Koa2 實現(xiàn)電影微信公眾號前后端開發(fā) 學習后的改造
由于上下班期間會看會小說,但是無奈廣告太多,還要收費,于是結(jié)合課程,進行開發(fā),并上傳到自己的微信小程序。
github
大致的思路:
1.連接數(shù)據(jù)庫
2.跑定時任務,進行數(shù)據(jù)庫的更新
3.開啟接口服務
4.微信小程序接口調(diào)用
連接本地的mongodb數(shù)據(jù)庫
const mongoose = require("mongoose") var db = "mongodb://localhost/story-bookShelf" exports.connect = () => { let maxConnectTimes = 0 return new Promise((resolve, reject) => { if (process.env.NODE_ENV !== "production") { mongoose.set("debug", false) } mongoose.connect(db) mongoose.connection.on("disconnected", () => { maxConnectTimes++ if (maxConnectTimes < 5) { mongoose.connect(db) } else { throw new Error("數(shù)據(jù)庫掛了吧,快去修吧") } }) mongoose.connection.on("error", err => { console.log(err) maxConnectTimes++ if (maxConnectTimes < 5) { mongoose.connect(db) } else { throw new Error("數(shù)據(jù)庫掛了吧,快去修吧") } }) mongoose.connection.once("open", () => { resolve() console.log("MongoDB Connected successfully!") }) }) }
然后初始化定義好的Schema
const mongoose = require("mongoose") const Schema = mongoose.Schema const bookSchema = new Schema({ name: { type: String }, bookId: { unique: true, type: Number } }) ...... mongoose.model("Book", bookSchema)2.跑定時任務,進行數(shù)據(jù)庫的更新
這一步驟主要是在定時進行數(shù)據(jù)庫小說章節(jié)的更新,用的是 node-schedule進行定時跑任務。
小說章節(jié)數(shù)是否增加,沒增加不用進行爬取。同時在爬取的時候需要提前前5章爬取,避免一些作者為了占坑,提前寫的預告。
每一本小說就開一個子進程child_process去跑,將數(shù)據(jù)存儲到mongo, 同時存儲子進程對后續(xù)有用。
定時跑任務時候會遇到上一條任務還在跑,所以在每一次跑之前都清空一遍儲存的子進程,將子進程殺掉。
章節(jié)任務
// chapter.js const cp = require("child_process") const { resolve } = require("path") const mongoose = require("mongoose") const { childProcessStore } = require("../lib/child_process_store") // 全局存儲子進程 /** * * @param {書本ID} bookId * @param {從哪里開始查找} startNum */ exports.taskChapter = async(bookId, startNum = 0) => { const Chapter = mongoose.model("Chapter") const script = resolve(__dirname, "../crawler/chapter.js") // 真正執(zhí)行爬蟲任務模塊 const child = cp.fork(script, []) // 開啟IPC通道,傳遞數(shù)據(jù) let invoked = false // 這里等子進程將數(shù)據(jù)傳回來,然后存儲到mongo中(具體爬取看下一段代碼) child.on("message", async data => { // 先找一下是否有數(shù)據(jù)了 let chapterData = await Chapter.findOne({ chapterId: data.chapterId }) // 需要將拿到的章節(jié)與存儲的章節(jié)做對比 防止作者占坑 if (!chapterData) { chapterData = new Chapter(data) await chapterData.save() return } // 進行字數(shù)對比 相差50字符 if ((data.content.length - 50 >= 0) && (data.content.length - 50 > chapterData.content.length)) { Chapter.updateOne ( { chapterId: +data.chapterId }, { content : data.content } ); } }) child.send({ // 發(fā)送給子進程進行爬取 bookId, // 哪本小說 startNum // 從哪個章節(jié)開始爬 }) // 存儲所有章節(jié)的爬取 用于跑進程刪除子進程 childProcessStore.set("chapter", child) }
真正開啟爬蟲,用的是 puppeteer,谷歌內(nèi)核的爬取,功能很強大。
分兩步:
1.爬對應小說的章節(jié)目錄,拿到章節(jié)數(shù)組
2.根據(jù)傳進來的startNum 進行章節(jié)startNum 的往后爬取
// crawler/chapter.js const puppeteer = require("puppeteer") let url = `http://www.mytxt.cc/read/` // 目標網(wǎng)址 const sleep = time => new Promise(resolve => { setTimeout(resolve, time) }) process.on("message", async book => { url = `${url}${book.bookId}/` console.log("Start visit the target page --- chapter", url) // 找到對應的小說,拿到具體的章節(jié)數(shù)組 const browser = await puppeteer.launch({ args: ["--no-sandbox"], dumpio: false }).catch(err => { console.log("browser--error:", err) browser.close }) const page = await browser.newPage() await page.goto(url, { waitUntil: "networkidle2" }) await sleep(3000) await page.waitForSelector(".story_list_m62topxs") // 找到具體字段的class let result = await page.evaluate((book) => { let list = document.querySelectorAll(".cp_dd_m62topxs li") let reg = new RegExp(`${book.bookId}/(S*).html`) let chapter = Array.from(list).map((item, index) => { return { title: item.innerText, chapterId: item.innerHTML.match(reg)[1] } }) return chapter }, book) // 截取從哪里開始爬章節(jié) let tempResult = result.slice(book.startNum, result.length) for (let i = 0; i < tempResult.length; i++) { let chapterId = tempResult[i].chapterId console.log("開始爬url:", `${url}${chapterId}.html`) await page.goto(`${url}${chapterId}.html`, { waitUntil: "networkidle2" }) await sleep(2000) const content = await page.evaluate(() => { return document.querySelectorAll(".detail_con_m62topxs p")[0].innerText }) tempResult[i].content = content tempResult[i].bookId = book.bookId process.send(tempResult[i]) // 通過IPC將數(shù)據(jù)傳回去,觸發(fā)child.on("message") } browser.close() process.exit(0) })3.開啟接口
做的任務主要是,拿mongodb的數(shù)據(jù),同時通過koa-router發(fā)布路由
先定義好路由裝飾器,方便后續(xù)使用 具體看 decorator.js
底層拿到數(shù)據(jù)庫的數(shù)據(jù)
service/book.js // 拿到數(shù)據(jù)庫存儲的值 const Chapter = mongoose.model("Chapter") // 獲取具體的章節(jié)內(nèi)容 export const getDetailChapter = async (data) => { const chapter = await Chapter.findOne({ chapterId: data.chapterId, bookId: data.bookId }, { content: 1, title: 1, chapterId: 1 }) // console.log("getDetailChapter::", chapter) return chapter } ...
路由定義 后續(xù)的接口就是 ‘/api/book/chapter’
@controller("/api/book") export class bookController { @post("/chapter") async getDetailChapter (ctx, next) { const { chapterId, bookId } = ctx.request.body.data const list = await getDetailChapter({ chapterId, bookId }) ctx.body = { success: true, data: list } } }4.微信小程序
使用wepy進行開發(fā),功能也是很簡單,具體開發(fā)可以參見小程序代碼,這里不做詳細講述。
支持記錄每一章的進度,與全局設(shè)置。后續(xù)可以自己發(fā)揮。
在目標網(wǎng)站找到小說的Id之后就能進行查找了。
接下來講解部署到服務器細節(jié)。
最后,在這里特別感謝@汪江 江哥的幫助,我前后琢磨了兩個月,而他就用了三天,謝謝你不厭其煩的幫助,與你共事很開心。
以上只是我的不成熟的技術(shù),歡迎各位留言指教。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100334.html
摘要:更新于利用重構(gòu)了下此項目,有興趣的可以參考看看,傳送門首先感謝作者的分享,很贊,原文地址這里記錄下編碼遇到的問題,這里只針對進行了驗證。 更新于 2019-01-02 利用 eggjs 重構(gòu)了下此項目,有興趣的可以參考看看,傳送門 首先感謝作者的分享,很贊~,原文地址 這里記錄下編碼遇到的問題,這里只針對 sever 進行了驗證。有同樣遇到問題的童鞋,可以作為參照~ 本地環(huán)境: ...
摘要:基本功能提供小說操作相關(guān)的所有提供登錄注冊相關(guān)實現(xiàn)驗證碼定期自動更新小說爬蟲部署運行即可實現(xiàn)一鍵部署。如果還想更近一步的實現(xiàn)自動部署的話,可以試試開源免費。 項目地址 前言 作為一個優(yōu)秀前端er,除了要精通前端基礎(chǔ)外,其他的如后臺,運維,linux等都要有所了解。這樣你才能對自己所負責的項目有一個整體的把握,不同端開發(fā)思維的碰撞,有助于你形成良好的代碼習慣,寫出高效優(yōu)質(zhì)的代碼。話不多說...
摘要:平日學習接觸過的網(wǎng)站積累,以每月的形式發(fā)布。年以前看這個網(wǎng)址概況在線地址前端開發(fā)群月報提交原則技術(shù)文章新的為主。 平日學習接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個網(wǎng)址:http://www.kancloud.cn/jsfron... 概況 在線地址:http://www.kancloud.cn/jsfront/month/82796 JS前端開發(fā)群月報 提交原則: 技...
摘要:平日學習接觸過的網(wǎng)站積累,以每月的形式發(fā)布。年以前看這個網(wǎng)址概況在線地址前端開發(fā)群月報提交原則技術(shù)文章新的為主。 平日學習接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個網(wǎng)址:http://www.kancloud.cn/jsfron... 概況 在線地址:http://www.kancloud.cn/jsfront/month/82796 JS前端開發(fā)群月報 提交原則: 技...
閱讀 9345·2021-11-18 10:02
閱讀 2644·2019-08-30 15:43
閱讀 2683·2019-08-30 13:50
閱讀 1409·2019-08-30 11:20
閱讀 2732·2019-08-29 15:03
閱讀 3655·2019-08-29 12:36
閱讀 948·2019-08-23 17:04
閱讀 644·2019-08-23 14:18