成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Koa2 + Mongo + 爬蟲 搭建 小說微信小程序(本地開發(fā)篇)

Kross / 1214人閱讀

摘要:前言根據(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)用

1.連接數(shù)據(jù)庫

連接本地的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

相關(guān)文章

  • Koa2 + Mongo + 爬蟲 搭建 小說信小程序本地開發(fā))---編碼時遇到的問題

    摘要:更新于利用重構(gòu)了下此項目,有興趣的可以參考看看,傳送門首先感謝作者的分享,很贊,原文地址這里記錄下編碼遇到的問題,這里只針對進行了驗證。 更新于 2019-01-02 利用 eggjs 重構(gòu)了下此項目,有興趣的可以參考看看,傳送門 首先感謝作者的分享,很贊~,原文地址 這里記錄下編碼遇到的問題,這里只針對 sever 進行了驗證。有同樣遇到問題的童鞋,可以作為參照~ 本地環(huán)境: ...

    liangdas 評論0 收藏0
  • RN+dva+node+mongo+nginx+docker 從開發(fā)到部署,全棧入坑指引!

    摘要:基本功能提供小說操作相關(guān)的所有提供登錄注冊相關(guān)實現(xiàn)驗證碼定期自動更新小說爬蟲部署運行即可實現(xiàn)一鍵部署。如果還想更近一步的實現(xiàn)自動部署的話,可以試試開源免費。 項目地址 前言 作為一個優(yōu)秀前端er,除了要精通前端基礎(chǔ)外,其他的如后臺,運維,linux等都要有所了解。這樣你才能對自己所負責的項目有一個整體的把握,不同端開發(fā)思維的碰撞,有助于你形成良好的代碼習慣,寫出高效優(yōu)質(zhì)的代碼。話不多說...

    liaorio 評論0 收藏0
  • 2017年1月前端月報

    摘要:平日學習接觸過的網(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ā)群月報 提交原則: 技...

    FuisonDesign 評論0 收藏0
  • 2017年1月前端月報

    摘要:平日學習接觸過的網(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ā)群月報 提交原則: 技...

    ivyzhang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<