簡易漫畫網站搭建-漫畫喵Server版

来源:http://www.cnblogs.com/idiotgroup/archive/2017/03/15/6551191.html
-Advertisement-
Play Games

本文實現了一個簡單的個人漫畫網站,配合之前的漫畫爬蟲使用。享受無處不在的漫畫生活。 github地址:https://github.com/miaoerduo/cartoon-cat-server 歡迎大家star、fork和指教。 ...


小喵的嘮叨話:寒假的時候寫了一個漫畫爬蟲,爬取了好幾個漫畫,不過一直沒有找到合適的漫畫閱讀的工具。因此最近就試著自己寫一個漫畫的網站,放在公網上或者區域網里,這樣就能隨時隨地用手機、Pad看漫畫了。

 

先放上項目的地址:https://github.com/miaoerduo/cartoon-cat-server ,歡迎大家隨時star、fork和指教。

關於漫畫爬蟲的內容,請參看之前的博客:http://www.miaoerduo.com/python/爬蟲-漫畫喵的100行逆襲.html

 

本文系原創,轉載請註明出處~

小喵的博客:http://www.miaoerduo.com

博客原文:http://www.miaoerduo.com/nodejs/簡易漫畫網站搭建-漫畫喵server版.html ‎

 

寫在前面

這裡,我們先試著思考幾個問題:

1、為什麼要做一個漫畫網站,而不是APP呢?

小喵最開始其實是打算做一個QT的漫畫閱讀軟體,這樣能夠在各種操作系統上用。開發了一小段時間之後,覺得用手機或者Pad看漫畫的情況會更多。難道要給手機、平板甚至是PC都編寫一套軟體嗎?這個小喵確實能力上達不到。其次是,如果是本地的APP的話,圖片可能還是需要存到設備上,這樣同樣很麻煩。於是,漫畫網站就成為最適合的選擇,只要在聯網的情況下(或者在同一個區域網中),只要有瀏覽器,就能夠看漫畫。這才是真正的跨平臺,也是小喵要編寫這個工具的原因。

2、那麼,一個簡單的漫畫網站可以怎麼去實現呢?

講道理的話,其實單純的靜態的網頁可能就足夠了,給每個漫畫圖片編寫一個頁面,包含圖片、下一頁、上一頁等部分就可以。頁面的生成可以是一個腳本程式。不過這樣的話,也有點麻煩,而且最終的頁面的數目可能會很多。小喵不喜歡這種方式。
這裡,小喵採用的方式是前、後端分離的方式。
整個網站由三個部分組成:

  • 數據:也就是漫畫本身。
  • 後臺:後臺程式的功能是根據請求返回用戶漫畫的列表、章節和圖片URL等信息。
  • 前端:調用後臺的程式,得到漫畫的信息並友好的進行顯示。

3、最後,我們使用什麼技術來實現呢?

前端的話,使用標準的Html,Css和Js就可以。後臺是個API Server,Python + Flask 或者 Nodejs + Express 都是不錯的選擇(使用起來很簡單。。。),小喵這裡就選用 Nodejs + Express 的方式編寫後臺(一直寫Js就可以了 :P )。另外,大型的漫畫網站,漫畫的信息應該會存到資料庫中,這樣會方便查找和管理。這裡考慮到漫畫數目比較少,就去掉了資料庫這一步驟,直接通過文件操作來得到漫畫信息,工作量也大大減少了。

 

先看一下最終的效果圖(雖然界面很簡單):

圖1 網站的最終效果圖

現在,喵粉們是不是已經迫不及待的想要動手寫代碼了呢?

一、目錄結構

下麵是小喵的目錄結構:

$ tree blog -N -L 2
blog
├── README.md
├── main.js
├── package.json
└── public
    ├── api.html
    ├── cartoon.html
    ├── chapter.html
    ├── css
    ├── img
    ├── index.html
    ├── js
    └── store

main.js 這裡就是後臺API的程式。因為功能很簡單,所以就放在一個文件中。

package.json 是nodejs的包管理器,在這裡可以定義依賴。我們這裡只依賴Express。

public 這個文件夾用來存放靜態的資源,包括Html、Css、js、image以及漫畫資源(store)。

二、後臺程式

1. 依賴安裝

原生的 Nodejs 就已經十分適合寫API程式了,Express 只是讓它更加的方便了而已(至少對於這個項目來說)。

Nodejs 的安裝十分簡單,在官網上 https://nodejs.org 有下載鏈接,伺服器上使用 apt-get 等工具也很容易安裝。

Express 的話,可以使用npm install express —save 來安裝。這裡小喵使用的package的方式來安裝。在項目根目錄新建package.json文件,寫入配置信息:

{
  "name": "cartoon-cat-server",
  "version": "0.0.1",
  "dependencies": {
    "express": "visionmedia/express"
  }
}

之後使用 npm install 命令就可以完成安裝。安裝完成之後會發現根目錄多了一個 node_modules 文件夾,裡面就是我們的依賴庫了。喵粉們如果下載了我的這個項目的話,第一步也是要進入項目目錄然後輸入 npm install

2. 漫畫的文件結構

我們的漫畫資源都是通過 漫畫喵 這個爬蟲工具下載下來的,因此漫畫都是每個章節都是一個文件夾,每個章節的漫畫圖片都放在對應的文件夾中,而且按照頁碼來命名。

這樣通過遍歷文件夾似乎就能獲取漫畫的信息了!

漫畫列表和章節中的圖片列表都可以通過上述的方式來解決,但是章節的列表卻不行。因為漫畫的章節有時候並沒有明確的順序(比如突然出現一個番外篇啥的),這樣遍歷文件夾預設的順序(按名稱)就可能是錯誤的。

我們有兩個解決辦法:

  1. 按照文件夾的創建時間來顯示文件名。這樣有點不靈活。
  2. 在每個漫畫的根目錄建一個文本文件,用來存放章節的信息。

小喵選擇第二種策略,創建這個list的方法十分簡單粗暴,在漫畫目錄下麵使用:

ls -t -r > index

ls 是linux上面的顯示目錄的工具,-t 表示按時間排序(最上面是最新的),-r 表示倒序,> 是重定向,最終輸出到index這個文件。然後編輯這個文件,刪掉index這一行(系統貌似是先生成index這個文件,然後再執行ls,最後把結果輸入到文件中,因此文件裡面多了一個index的文件名),再做一些必要的調整。

Windows上可以使用:

dir /OD /B > index

dir 是windows的查看目錄的命令,/OD 表示按照時間排序,/B 表示只顯示文件名,> 重定向到index。windows上的這個列表文件中也會出現index這個文件名(看來各種操作系統都一樣)。另外需要註意的是windows的換行和linux或mac不一樣。

這樣,我們就可以通過讀這個index文件來獲取章節的信息了。

最終的漫畫的結構(為了顯示的方便,刪除了很多圖片和章節)如下:

$ tree store -N -L 3
store
├── 犬夜叉
│   ├── index
│   ├── 第1章
│   │   ├── 00001.jpg
│   │   └── 00002.jpg
│   └── 第2章
│       ├── 00001.jpg
│       └── 00002.jpg
└── 極黑的布倫希爾特
    ├── index
    ├── 第1章
    │   ├── 00001.jpg
    │   └── 00002.jpg
    └── 第2章
        ├── 00001.jpg
        └── 00002.jpg

3. API 編寫

Express十分的容易使用。這裡小喵給一個官網的Hello World的教程讓大家看一下:

var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

將上述代碼保存成 main.js,使用 node main.js 就可以啟動這個程式,然後用瀏覽器訪問 http://localhost:3000 ,就能看到一個顯示著 hello world 的頁面。

require 語句用來引入依賴,app 是express的封裝的對象。通過 app.get 方法就可以給指定的url(官方說法叫route)綁定相應的處理方法(GET方法的請求)。處理函數有2個參數 req 表示request,也就是用戶的請求,通過這個對象我們可以獲取用戶的輸入的參數,res 表示response,是一個向用戶返回數據的對象。

listen 用來監聽一個埠啟動服務。

 

這裡小喵先給出自己定義的一些輔助的函數,定義錯誤信息和參數校驗,後面會使用到:

// 引入依賴
var express = require('express');
var fs = require("fs"); // 即file system,用來進行文件操作
var app = express();

/**
 * 錯誤提示
 */
var ErrorHelper = {
    'internal_error': function () {
        return {
            'msg': 'something wrong with server',
            'code': 1
        };
    },
    'missing_param': function (param) {
        return {
            'msg': 'missing param: ' + param,
            'code': 2
        };
    },
    'error_param': function (param, data) {
        return {
            'msg': 'the param ' + param + '(' + data + ') is illegal',
            'code': 3
        }
    },
    'not_found': function (param) {
        return {
            'msg': 'cannot find ' + param,
            'code': 4
        };
    }
};

/**
 * 檢查參數格式,只能輸入字母,數字和漢字
 */
function checkParam(param) {
    return /^[\u4e00-\u9fa5_a-zA-Z0-9]+$/.test(param);
}

1) get_cartoon_list

這個介面用來獲取所有的漫畫列表。

/**
 * 獲取漫畫列表
 */
app.get('/get_cartoon_list', function (req, res) {
    fs.readdir(__dirname + '/public/store', function (err , files) {
        if (err) {
            res.jsonp(ErrorHelper.internal_error());
        }
        res.jsonp({'cartoon': files, 'code': 0});
    });
});

這個函數十分的簡單,通過 fs 讀取store中的文件名,然後用json的格式返回回去。這裡小喵用的jsonp,為瞭解決跨域請求的問題,不過我們的頁面和服務是一臺機器的,所以這部分並不需要。

2) get_chapter_list

這個介面用來獲取漫畫的章節的信息,所以需要輸入參數,這裡定為cartoon。

/**
 * 獲取章節信息
 */
app.get('/get_chapter_list', function (req, res) {

    var cartoon = req.query.cartoon;
    if (!cartoon) {
        res.jsonp(ErrorHelper.missing_param('cartoon'));
        return;
    }

    if (!checkParam(cartoon)) {
        res.jsonp(ErrorHelper.error_param('cartoon', cartoon));
        return;
    }
    var cartoon_dir = __dirname + '/public/store/' + cartoon;
    fs.exists(cartoon_dir + '/index', function (exists) {
        if (!exists) {
            res.jsonp(ErrorHelper.not_found(cartoon));
            return;
        }
        fs.readFile(cartoon_dir + '/index', function (err, data) {
            if (err) {
                res.jsonp(ErrorHelper.internal_error());
                return;
            }

            var chapter_list = data.toString().split('\n').filter(function (d) {
                return d.length > 0;
            });

            res.jsonp({'chapter': chapter_list, 'code': 0});
        });
    });
});

首先判斷輸入的參數,之後判斷對應漫畫的文件夾中是否有index這個文件,如果有的話就讀取然後返回給用戶。

3) get_img_list

這個介面用來返回漫畫的具體章節的圖片的URL,用戶需要輸入漫畫名(cartoon)和章節名(chapter)。註意要修改自己的HOST的地址。

var HOST = "localhost"; // 如果不是在本機上使用,請改成實際的ip地址
                        // 後面的圖片的URL會使用這個變數來構造
var PORT = 3000;

app.get('/get_img_list', function (req, res) {

    var cartoon = req.query.cartoon;
    if (!cartoon) {
        res.jsonp(ErrorHelper.missing_param('cartoon'));
        return;
    }
    if (!checkParam(cartoon)) {
        res.jsonp(ErrorHelper.error_param('cartoon', cartoon));
        return;
    }
    var chapter = req.query.chapter;
    if (!chapter) {
        res.jsonp(ErrorHelper.missing_param('chapter'));
        return;
    }
    if (!checkParam(chapter)) {
        res.jsonp(ErrorHelper.error_param('chapter', chapter));
        return;
    }

    var cartoon_dir = __dirname + '/public/store/' + cartoon;
    fs.exists(cartoon_dir + '/index', function (exists) {
        if (!exists) {
            res.jsonp(ErrorHelper.not_found(cartoon));
            return;
        }
        fs.readdir(cartoon_dir + '/' + chapter, function (err, images) {
            if (err) {
                res.jsonp(ErrorHelper.error_param('chapter', chapter));
                return;
            }
            // 按名字排序
            images.sort(function (lhs, rhs) {
                return parseInt(lhs.split('.')[0]) - parseInt(rhs.split('.')[0]);
            });

            var urls = images.map(function (image) {
                return 'http://' + HOST + ':' + PORT + '/store/' + cartoon + '/' + chapter + '/' + image;
            });
            res.jsonp({'img': urls, 'code': 0});
        });
    });
});

這是目前最複雜的函數了,先檢查參數,然後判斷漫畫是否存在,再判斷章節是否存在,列出章節文件夾裡面的圖片名,並按數字的順序排序。最終構造成URL,返回給用戶。

4) 靜態資源

public文件夾中的資源都是靜態資源,用戶可以通過URL訪問。在這裡Nodejs也是支持的:

app.use('/', express.static('public'));

不過Nodejs本身並不適這種靜態資源的工作,所以如果是生產環境中,建議大家還是使用Nginx等工具,讓Nodejs安心的處理業務邏輯吧。

5) 啟動服務

var server = app.listen(PORT, function () {
    console.log("應用實例,訪問地址為 http://%s:%s", HOST, PORT);
});

三、前端

1. Ajax

前端使用Ajax就可以很容易完成,相信即使是前端小白也能實現,而且還比小喵做的好看(無奈臉)。小喵使用了JQuery 來處理Ajax的內容,界面庫使用了Metro,然而即使這樣也沒有提高網站的顏值。

源碼可以從github上下載到,所以小喵就不重點介紹前端了。

2. 懶載入

有一點需要註意,在漫畫圖片的頁面中,通常會出現大量的圖片,如果只是簡單的使用 img 標簽的話,可能會導致瀏覽器同時載入所有的圖片,如果網速不好的話,我們的體驗也會相當的差(區域網請無視)。所以我們使用一種懶載入的策略,只有可見的圖片才會載入。然後小喵就從github上找相關的插件,然後發現了一個使用比較方便的代碼,還有詳細的原理介紹,感興趣的話大家可以看一下。

圖片懶載入插件實戰:http://www.cnblogs.com/beidan/p/5648240.html
插件的github:https://github.com/beidan/lazeLoadImg

四、寫在後面

至此,我們就搭建好了一個可以隨時玩耍的個人漫畫網站了。喵粉們感興趣可以star、fork這個項目,如果喜歡開發的話,能幫忙一起提高網站的顏值就更好了 O(∩_∩)O哈!

項目地址:https://github.com/miaoerduo/cartoon-cat-server ,歡迎大家隨時star、fork和指教。

PS. 請搭配漫畫喵爬蟲版一起食用:https://github.com/miaoerduo/cartoon-cat

使用的話,按如下的流程:

git clone [email protected]:miaoerduo/cartoon-cat-server.git
cd cartoon-cat-server
npm install
node main.js

另外,為了避免程式突然崩掉,建議大家使用forever這個工具。上面的流程的最後一句node main.js就可以改成下麵的。

npm install forever -g
forever start main.js

這樣,我們的程式就更健壯了。

最後,小喵再說一句,這個項目有很多的缺陷,比如直接訪問文件,Nodejs直接管理靜態文件,需要提供奇怪的參數,沒有驗證用戶等等。因此不適合真的生成中的使用。不過,自娛自樂應該是足夠了。小喵自己是放在宿舍的樹莓派里,睡前躺床上看會兒漫畫。

 

覺得不錯的話,請點個大大的推薦~~

 

希望小喵能和大家一起學習和進步~~ 

 

 

轉載請註明出處~


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

-Advertisement-
Play Games
更多相關文章
  • 在使用 ueditor 開發時, 作為一個web文本編輯器使用時。 當點擊上傳圖片時, 文件夾要延遲好久才能打開。 解決: 針對多圖片上傳, 將/ueditor/dialogs/image/image.js 文件下的以下代碼 改為 針對單圖片上傳, 將/ueditor/ueditor.all.js ...
  • AJAX技術是網頁構建的必備技能之一,本文希望能幫助大家輕鬆的學習這項技術 ...
  • 文本整理了javascript操作DOM的一些常用的api,根據其作用整理成為創建,修改,查詢等多種類型的api,主要用於複習基礎知識,加深對原生js的認識。 基本概念 在講解操作DOM的api之前,首先我們來複習一下一些基本概念,這些概念是掌握api的關鍵,必須理解它們。 Node類型 DOM1級 ...
  • 複習下ajax吧! 1.創建XMLHttpRequest對象 代碼僅供參考,具體功能可以自己擴展。 http://www.cnblogs.com/jiebba/p/6529854.html http://www.cnblogs.com/jiebba 我的博客,來看吧! 如果有錯誤,請留言修改下 哦! ...
  • 1.現在看看對JSONP的封裝 get / post / getJson 在 ajax的基礎上封裝了再次封裝了。 zepto基本差不多完成了, 看了之後,不難發現,zepto也就有幾大模塊: 選擇器 , 一些js原生方法的封裝 , 事件模型模塊 , 再就是 ajax 的封裝 。 代碼僅供參考,具體功 ...
  • background-image繪製多張圖片疊加,示例如下: css3的box-shadow屬性: 讓ie6、7、8都支持border-radius 、box-shadow、text-shadow的方法:用ie-css3.htc 首先下載ie-css3.htc腳本,然後在css中加入: 它的使用方法 ...
  • 獲取URL裡面傳的參數,在Js中不能像後臺一樣使用Request.QueryString來獲取URL裡面參數,下麵介紹兩種方式用來獲取參數 方式一:使用split分隔來獲取,這種方法考試了地址中包含了returnUrl參數的形式,如果地址中本來就包含了另外一個網址,這個時候需要先過濾掉這個網址再去獲 ...
  • 前言 在vue里,組件之間的作用域是獨立的,父組件跟子組件之間的通訊可以通過prop屬性來傳參,但是在兄弟組件之間通訊就比較麻煩了。比如A組件要告訴一件事給B組件,那麼A就要先告訴他們的爸組件,然後爸組件再告訴B。當組件比較多,要互相通訊的事情很多的話,爸組件要管他們那麼多事,很累的。vuex正是為 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...