## 简介
多多猫插件是一个标签+脚本的xml文件,大概结构如下:
```xml
<!--?xml version="1.0" encoding="UTF-8" ?-->
<sited ver="1" debug="1" engine="34" schema="1">
<meta>
<ua></ua>
<guid></guid>
<title></title>
<author></author>
<contact></contact>
<intro></intro>
<alert></alert>
<url></url>
<expr></expr>
<logo></logo>
<encode></encode>
<about>
<item/>
</about>
</meta>
<main>
<home>
<hots/>
<updates/>
<tags>
<tags/>
</tags>
</home>
<search/>
<tag>
<tag/>
<tag/>
</tag>
<book>
<sections/>
</book>
<section/>
</main>
<script>
<require>
<item url="http://sited.noear.org/addin/js/cheerio.js" lib="cheerio" />
</require>
<code>
<![CDATA[
]]>
</code>
</script>
</sited>
```
这是一个空白的插件示例,用来向大家展示插件的完整架构,它主要分为下面四个部分:
1. `<sited>`根节点,它标注了插件的版本,调试状态,所用的解析引擎版本号等
2. `<meta>`节点, 它标注了开发者、对应网站、标题、登录等信息
3. `<main>`节点, 它声明了用来解析网页内容的函数,内容图片的显示方式等与内容相关的设定
4. `<script>`节点,它包含了`<main>`节点中所声明函数的具体实现,也就是JavaScript代码部分,用来从网页获取内容
**注意**:插件行首的一对尖括号和包括的代码`<!--?xml version="1.0" encoding="UTF-8" ?-->`是每个插件必须的,不要省略,它指定了插件的编码为UTF-8
更深入事项请见[插件开放平台http://sited.noear.org/dev/](http://sited.noear.org/dev/) 里面的官方开发文档PDF
<br>
## 插件示例
下面是一个完整的漫画插件示例,让大家对真实的插件有一个了解
```xml
<!--?xml version="1.0" encoding="UTF-8" ?-->
<sited ver="3" debug="1" engine="34" schema="1">
<meta>
<ua></ua>
<guid>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</guid>
<title>naver网漫</title>
<author>Guang</author>
<contact></contact>
<intro>[漫画](v3修复)LINE WEBTOON韩国naver官网。매일매일 새로운 재미, 네이버 웹툰.(部分章节提示下架,要登陆源网站观看)</intro>
<alert></alert>
<url>http://m.comic.naver.com/</url>
<expr>comic\.naver\.com\/</expr>
<logo>http://static.naver.net/m/comic/im/favicon/favicon_140327.ico</logo>
<encode>UTF-8</encode>
<about title="反馈" mail="xx@xx.com">
<item url="我邮箱是xx@xx.com" />
<item />
<item url="欢迎打赏插件开发者Guang,反馈问题。"/>
<item txt="点击或扫描二维码对[xxx]支付宝打赏" expr="https://qr.alipay.com/xxx" logo="http://xxx.jpg"/>
<item />
<item txt="扫描二维码对我的微信打赏" logo="http://xxx.jpg"/>
<item />
</about>
</meta>
<main dtype="1" btag="漫画">
<home>
<hots cache="1d" showImg="1" w="1" h="1" title="hots节点" method="get" parse="hots_parse" url="https://m.comic.naver.com/webtoon/weekday.nhn"/>
<updates cache="1d" showImg="1" w="1" h="1" title="updates节点" method="get" parse="updates_parse" url="https://m.comic.naver.com/webtoon/weekday.nhn"/>
<tags title="分类">
<tags cache="1d" method="get" parse="tags_parse1" url="https://m.comic.naver.com/webtoon/genre.nhn"/>
<tags cache="1d" method="get" parse="tags_parse_challenge" url="https://m.comic.naver.com/bestChallenge/genre.nhn"/>
</tags>
</home>
<search cache="1d" method="get" parse="search_parse1" url="https://m.comic.naver.com/search/result.nhn?keyword=@key&searchType=WEBTOON" >
<search cache="1d" method="get" parse="search_parse_challenge" url="https://m.comic.naver.com/search/result.nhn?keyword=@key&searchType=BEST_CHALLENGE" />
</search>
<tag>
<tag cache="10m" showImg="1" w="1" h="1" method="get" parse="tag_parse1" expr="webtoon\/genre.nhn"/>
<tag cache="10m" showImg="1" w="1" h="1" method="get" parse="tag_parse_challenge" expr="bestChallenge\/genre.nhn"/>
</tag>
<book cache="1d" method="get" buildUrl="book_buildUrl" parse="book_parse" expr="\/list\.nhn" >
<sections cache="1d" method="get" buildUrl="book_s_buildUrl" parseUrl="book_s_parseUrl" parse="book_s_parse" />
</book>
<section cache="1d" method="get" options="0,0,0,1" parseUrl="section_parseUrl" parse="section_parse" header="referer"/>
</main>
<script>
<require>
<item url="http://sited.noear.org/addin/js/cheerio.js" lib="cheerio" />
</require>
<code>
<![CDATA[
function urla(u) {
var host = "https://m.comic.naver.com";
if (u.indexOf("http") < 0) {
if (u.substr(0, 2) != '//') {
if (u.substr(0, 1) != '/')
u = host + '/' + u;
else
u = host + u;
} else {
u = 'https:' + u;
}
}
return encodeURI(u);
}
function hots_parse(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.toon_lst li .lst a').each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.attr('href')).replace(/&week=.+$/, '');
bm.logo = item.find('img').attr('src');
list.push(bm);
});
return JSON.stringify(list);
}
function updates_parse(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.toon_lst li .lst a').each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.attr('href')).replace(/&week=.+$/, '');
bm.logo = item.find('img').attr('src');
bm.newSection = '';
bm.updateTime = item.find('.sub_info').eq(0).text();
list.push(bm);
});
return JSON.stringify(list);
}
function tags_parse1(url, html) {
var $ = cheerio.load(html);
var list = [];
list.push({
'group': '正式漫画'
});
$('#webtoonGenreTab ul li a').each(function () {
var item = $(this);
var bm = {};
bm.title = item.text();
bm.url = urla(item.attr('href')) + '&sort=NEW&page=@page';
list.push(bm);
});
return JSON.stringify(list);
}
function tags_parse_challenge(url, html) {
var $ = cheerio.load(html);
var list = [];
list.push({
'group': 'best新星漫画'
});
$('#genreTab ul li a').each(function () {
var item = $(this);
var bm = {};
bm.title = item.text();
bm.url = urla(item.attr('href')) + '&sort=UPDATE&page=@page';
list.push(bm);
});
return JSON.stringify(list);
}
function tag_parse1(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.toon_lst li').each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.find('a').attr('href'));
bm.logo = item.find('img').attr('src');
bm.author = '';
bm.status = '正式漫画';
bm.newSection = '';
bm.updateTime = '';
list.push(bm);
});
return JSON.stringify(list);
}
function tag_parse_challenge(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.toon_lst li').each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.find('a').attr('href'));
bm.logo = item.find('img').attr('src');
bm.author = '';
bm.status = 'best新星漫画';
bm.newSection = '';
bm.updateTime = '';
list.push(bm);
});
return JSON.stringify(list);
}
function search_parse1(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.lst').slice(0, 10).each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.children('a').attr('href'));
bm.logo = item.find('img').attr('src');
bm.author = item.find('.sub_info').eq(0).text();
bm.status = '正式漫画';
bm.newSection = '';
bm.updateTime = '';
bm.btag = "正式漫画";
if (!bm.url.match(/javascript:goPcPage/i))
list.push(bm);
});
return JSON.stringify(list);
}
function search_parse_challenge(url, html) {
var $ = cheerio.load(html);
var list = [];
$('.lst').slice(0, 6).each(function () {
var item = $(this);
var bm = {};
bm.name = item.find('.toon_name').text();
bm.url = urla(item.children('a').attr('href'));
bm.logo = item.find('img').attr('src');
bm.author = item.find('.sub_info').eq(0).text();
bm.status = 'best新星漫画';
bm.newSection = '';
bm.updateTime = '';
bm.btag = "best新星漫画";
if (!bm.url.match(/javascript:goPcPage/i))
list.push(bm);
});
return JSON.stringify(list);
}
function book_buildUrl(url) {
return url.replace(/^http.+?\/\/[^\/]+/i, 'https://m.comic.naver.com');
}
function book_parse(url, html) {
var $ = cheerio.load(html);
var data = {};
data.name = $('.info_in .title').text();
data.author = $('.info_in .nm').text();
data.logo = $('.toon_info .im_br img').attr('src');
data.intro = $('.info_in .info_cont').text();
data.updateTime = '';
data.isSectionsAsc = 0;
if ($('.lst>a').length > 1) {
if ($('.lst>a').eq(0).attr('href').match(/no=(\d+)/i)) {
var a0 = $('.lst>a').eq(0).attr('href').match(/no=(\d+)/i)[1];
} else {
var a0 = $('.lst').eq(0).parent().attr('data-no');
}
if ($('.lst>a').eq(1).attr('href').match(/no=(\d+)/i)) {
var a1 = $('.lst>a').eq(1).attr('href').match(/no=(\d+)/i)[1];
} else {
var a1 = $('.lst').eq(1).parent().attr('data-no');
}
if (Number(a0) < Number(a1))
data.isSectionsAsc = 1;
}
data.sections = [];
return JSON.stringify(data);
}
function book_s_buildUrl(url) {
return url.replace(/^http.+?\/\/[^\/]+/i, 'https://m.comic.naver.com');
}
function book_s_parseUrl(url, html) {
var $ = cheerio.load(html);
var urls = url;
if ($('.current_pg').text().match(/\d+\D+(\d+)/i)) {
var pages = Number($('.current_pg').text().match(/\d+\D+(\d+)/i)[1]);
for (i = 2; i <= pages; i++) {
urls = urls + ';' + url + '&page=' + i;
}
}
return urls;
}
function book_s_parse(url, html) {
var $ = cheerio.load(html);
var data = {};
data.sections = [];
$('.lst>a').each(function () {
if ($(this).attr('href') && $(this).attr('href').match(/titleId/i)) {
var bm = {
name: $(this).find('.toon_name').text().trim(),
url: urla($(this).attr('href'))
};
} else {
var bm = {
name: $(this).find('.toon_name').text().trim() + '-要登陆源网站观看',
url: ''
};
}
data.sections.push(bm);
});
return JSON.stringify(data);
}
function section_parseUrl(url, html) {
var $ = cheerio.load(html);
if ($('#id_area').length || $('#toonLayer').length || $('#mflick').length) {
return url;
} else {
curl = html.match(/effecttoonContent[\s\S]+?imageUrl.+?['"\s]+(http.+?)['"\s]/i)[1] + '/';
return urla(html.match(/documentURL\s*:\s*['"]\s*(.+?)\s*['"]/i)[1]);
}
}
function section_parse(url, html) {
var list = [];
if (url.match(/detail.nhn\?titleId=/i) || url.match(/nid.naver.com\/nidlogin/i) || url.match(/detail.nhn\?titleId=.+?#nafullscreen/i)) {
var $ = cheerio.load(html);
if ($('#toonLayer').length) {
$('#toonLayer ul li img').each(function () {
list.push($(this).attr('data-src'));
});
} else if ($('#mflick').length) {
$('#mflick>.swiper-wrapper>.swiper-slide>img').each(function () {
list.push($(this).attr('data-src'));
});
} else {}
} else {
var json = JSON.parse(html);
for (var i in json.pages) {
for (var j in json.pages[i].layers) {
var item = json.pages[i].layers[j].asset.replace('image/', '');
var imageurl = json.assets.image[item];
if (imageurl.match(/\.jpg/i))
list.push(curl + imageurl);
}
}
}
return JSON.stringify(list);
}
]]>
</code>
</script>
</sited>
```
(本节完)
- 序言
- 第一章 基础
- 1.1 Html基础
- 1.2 CSS选择器
- 1.2.1 标签选择器
- 1.2.2 class/id选择器
- 1.2.3 属性选择器
- 1.3 JavaScript基础
- 1.4 json基础
- 第二章 中级
- 2.1 插件结构总览
- 2.2 meta头部节点讲解
- 2.3 main主体节点讲解
- 2.4 script脚本节点讲解
- 2.5 插件的安装调试与发布
- 第三章 高级
- 3.1 插件高级特性
- 3.2 常见内容保护突破方法
- 3.3 开发文档所没说的事
- 3.4 电脑js脚本测试插件
- 3.5 加login节点教程
- 3.6 使用yeoman生成器
- 3.7 自动化发布插件
- 第四章 附录
- 4.1 markdown基本用法