简易漫画⽹站搭建-漫画喵Server版
⼩喵的唠叨话:寒假的时候写了⼀个漫画爬⾍,爬取了好⼏个漫画,不过⼀直没有到合适的漫画阅读的⼯具。因此最近就试着⾃⼰写⼀个漫画的⽹站,放在公⽹上或者局域⽹⾥,这样就能随时随地⽤⼿机、Pad看漫画了。
本⽂系原创,转载请注明出处~
写在前⾯
这⾥,我们先试着思考⼏个问题:
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
└── 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 只是让它更加的⽅便了⽽已(⾄少对于这个项⽬来说)。
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是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()
<('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
require语句⽤来引⼊依赖,app是express的封装的对象。通过⽅法就可以给指定的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
这个接⼝⽤来获取所有的漫画列表。
/**
* 获取漫画列表
*/
<('/get_cartoon_list', function (req, res) {
if (err) {
res.jsonp(ErrorHelper.internal_error());
}
res.jsonp({'cartoon': files, 'code': 0});
});
});
这个函数⼗分的简单,通过fs读取store中的⽂件名,然后⽤json的格式返回回去。这⾥⼩喵⽤的jsonp,为了解决跨域请求的问题,不过我们的页⾯和服务是⼀台机器的,所以这部分并不需要。
2) get_chapter_list
这个接⼝⽤来获取漫画的章节的信息,所以需要输⼊参数,这⾥定为cartoon。
/**
* 获取章节信息
*/
<('/get_chapter_list', function (req, res) {
var cartoon = req.query.cartoon;
if (!cartoon) {
res.jsonp(ErrorHelper.missing_param('cartoon'));
return;
}
if (!checkParam(cartoon)) {
res._param('cartoon', cartoon));
return;
}
var cartoon_dir = __dirname + '/public/store/' + cartoon;
if (!exists) {
res._found(cartoon));
return;
}
if (err) {
res.jsonp(ErrorHelper.internal_error());
return;
}
var chapter_list = String().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;
<('/get_img_list', function (req, res) {
var cartoon = req.query.cartoon;
if (!cartoon) {
res.jsonp(ErrorHelper.missing_param('cartoon'));
return;
}
if (!checkParam(cartoon)) {
res._param('cartoon', cartoon));
return;
}
var chapter = req.query.chapter;
if (!chapter) {
res.jsonp(ErrorHelper.missing_param('chapter'));
return;
}
简单网页if (!checkParam(chapter)) {
res._param('chapter', chapter));
return;
}
var cartoon_dir = __dirname + '/public/store/' + cartoon;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论