script(腳本節點)用js代碼寫,用于解析獲取到的數據并轉換為目標數據。本節分節點對漫畫插件示例的xml代碼部分和js代碼部分講解(輸出格式說明)。節尾贈送小說插件輸出格式說明,其他類型插件輸出格式說明請見[插件開放平臺http://sited.noear.org/dev/](http://sited.noear.org/dev/) 里面的官方開發文檔PDF。
<br>
## 2.4.1 hots、updates節點
```xml
<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"/>
```
```javascript
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 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)};
```
漫畫(或有目錄圖片)輸出格式[dtype=1]如下
|處理事件|輸出格式(字符串)|說明|
|:-|:-|:-|
|>login.check|"1"或"0"|詳見高級篇插件加login節點教程|
|>hots.parse|[{name:"",url:"",logo:""}]||
|>updates.parse|[{name:"",url:"",logo?:"",newSection:"",updateTime:"yyyy-MM-dd"}]||
hots節點通過hots.parse向用戶展示熱門漫畫列表,上面示例js部分function hots_parse(url,html){}對應xml部分hots節點的@parse屬性、@url屬性(目標網址),所有parse(url,html)函數的html參數表示url對應的源代碼文本(為電腦瀏覽器Ctrl+u后顯示的不加載js的html源代碼,而不是F12開發者工具Elements面板中顯示的加載js后的html代碼)。hots.parse輸出含有name(漫畫名字)、url(該漫畫主頁)、logo(漫畫封面,實在沒有就留空)的數組(字符串格式,后面節點的也是字符串格式)。urla函數是補全網址給任何節點調用,因為源網站上面漫畫網址可能是相對路徑,要識別補全協議、域名。<br>
updates節點通過updates.parse展示最新漫畫列表,updates_parse(url,html)及輸出類似hots_parse的,只是輸出數組里多出newSection(最新章節)、updateTime(更新時間)。
<br>
## 2.4.2 tags、tag節點
```xml
<tags title="分類" cache="1d" method="get" parse="tags_parse" url="https://m.comic.naver.com/webtoon/genre.nhn"/>
……
<tag>
<tag cache="10m" showImg="1" w="1" h="1" method="get" parse="tag_parse" expr="webtoon\/genre.nhn"/>
</tag>
```
```javascript
function tags_parse(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 tag_parse(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);
}
```
|>tags.parse|[{title:"",group:"",url:""}]|
|:-|:-|
|>tag.parse|[{name:"",url:"",logo:"",author:"",status:"",newSection:"",updateTime:""}]|
tags節點展示漫畫分類列表(是展示所有分類標簽),如果不用item靜態配置就用tags.parse解析,tags.parse輸出含有title(某一分類名字)、url(該分類的網址)、group(該分類的所屬分組)的數組。注:group為非必須項(只能在分組的第一項里出現),比如輸出數組有國產漫畫、日本漫畫、少女漫畫、少男漫畫的,源網站有寫是按產地、按受眾性別分類的,可以在數組第一項、第三項里加上group:"產地"、group:"受眾性別"。<br>
tag節點通過tag.parse展示某一個分類的漫畫列表,tag.parse輸出含有name(漫畫名字)、url(該漫畫主頁)、logo(漫畫封面)、author(漫畫作者)、status(完本/連載狀態)、newSection(新章節的名字)、updateTime(更新時間)的數組,后4個值中有幾個在多多貓界面排斥占用同一行位置(多多貓展示模板決定的),所以tag和search節點的后4個值我會把想寫的幾個值都寫在updateTime值里面(字符串拼接)。如果某一個分類的漫畫列表第1、2、3頁是`xx.com/p1、xx.com/p2、xx.com/p3`,想要tag_parse(url,html)對`xx.com/p1、xx.com/p2、xx.com/p3`解析,必須要在tags節點輸出該分類的網址為`xx.com/p@page`,第一次進入tag節點會對`xx.com/p1`解析,用戶在tag界面每次上拉進入下一頁,tag節點會每次對@page屬性加1替換@page為用戶當前頁數。
<br>
## 2.4.3 search節點
```xml
<search cache="1d" method="get" parse="search_parse" url="https://m.comic.naver.com/search/result.nhn?keyword=@key&searchType=WEBTOON"/>
```
```javascript
function search_parse(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);
}
```
|>search.parse|[{name:"",url:"",logo:"",author:"",status:"",newSection:"",updateTime:"",btag:""}]|
|:-|:-|
search節點通過search.parse展示關鍵字@key的搜索結果列表,如果xml部分@method屬性等于get的,@key寫在@url屬性里如`abc.com/@key`,用戶填寫搜索xx時,search_parse(url,html)的url參數自動變成`abc.com/xx`;如果xml部分@method屬性等于post的(對于搜索頁面post請求常見),@key寫在@args屬性里。search.parse輸出類似tag.parse的,(author、status、newSection、updateTime也像tag節點排斥占用情況),只是search.parse輸出數組里多出btag(即對搜索結果項業務類型的真實描述如漫畫,不會顯示在多多貓界面)。
<br>
## 2.4.4book、sections節點
```xml
<book cache="1d" method="get" buildUrl="book_buildUrl" parse="book_parse" expr="\/list\.nhn" >
<sections cache="1d" method="get" buildUrl="sections_buildUrl" parseUrl="sections_parseUrl" parse="sections_parse" />
</book>
```
```javascript
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 sections_buildUrl(url) {
return url.replace(/^http.+?\/\/[^\/]+/i, 'https://m.comic.naver.com');
}
function sections_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 sections_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);
}
```
如果一個節點xml部分有@parse、@parseUrl、@buildUrl屬性,js最先運行buildUrl函數,中間運行parseUrl,最后運行parse。上面示例代碼中我用`function book_buildUrl(url){}`來轉換接收的url參數域名為手機網頁版域名,效果是用戶在多多貓首頁輸入電腦網頁版或者手機網頁版的漫畫主頁,被能book.expr屬性牽引入book節點變成手機網頁版解析(也可以改成電腦網頁版),方便用戶輸入任意版本的漫畫主頁就能直接進入book節點界面。<br>
上面示例代碼中`function book_buildUrl(url){}`另一個作用是兼容舊版本插件不同域名的收藏。比如有個插件以前網址是`a.com`,用戶收藏了漫畫`a.com/book1.html`,后來源網頁`a.com/book1.html`已關閉了并且遷移為`b.com/book1.html`。打開a網站插件收藏的漫畫被book.expr(比如book\d+.html)牽引后默認解析`a.com/book1.html`,因為源網頁打不開會解析失敗,此時只要通過buildUrl變成`b.com/book1.html`后就能提供b網站漫畫網址給后面parseUrl或parse環節來解析a網站插件收藏漫畫的數據(還要在meta.expr節點里同時匹配新舊兩個域名比如`a\.com|b\.com`;meta.url節點內容也不能改,以兼容用戶在舊域名的收藏;并且在main節點增加durl屬性為變化后網站首頁)(因為book節點S按鈕打開的是book節點最先接收的url網址,所以用戶點擊a網站插件收藏漫畫進入book界面后,點擊S按鈕會默認進入瀏覽器訪問已關閉的`a.com/book1.html`,建議通過`function buildWeb(url){}`來修改S按鈕訪問網址為`b.com/book1.html`,buildWeb函數大括號里可以直接復制用于兼容舊版本插件收藏的buildUrl函數里面代碼,此時book節點的xml部分要對應加上buildWeb屬性)。
|>book.parseUrl?|url或CALL::url(多個時用";"隔開)<br>url=目標url(其請求結果轉到parse處理)<br>CALL::url = 中轉解析url(其請求結果仍回到parseUrl處理)<br>目標url需要中轉解析,或分布在多個url上時使用。||
|:-|:-|:-|
|>book.parse|{name:"",author:"",intro:"",logo:"",updateTime:"yyyy-MM-dd",isSectionsAsc:1或0,sections:[{name:"",url:""}]}|book節點(dtype等于1時)通過book.parse展示某一本漫畫書的介紹和目錄,book.parse輸出含有name(漫畫名字)、author(該漫畫作者)、intro(漫畫介紹)、logo(漫畫封面)、updateTime(漫畫更新時間)、isSectionsAsc(章節排序,1為源網頁是升序,0為降序,如果不寫該屬性就是0的效果)、sections(即每一話目錄數組,name為每一話名字,url為該話網址。url為空時,name則為分組)的對象。|
當book.parse接入url和源網站存放該漫畫目錄數據的網址不一樣時(比如后者分為幾頁網址)可以如上面示例使用sections節點,sections節點必須含buildUrl和parse屬性,有需要時可用parseUrl屬性但(截止v35引擎)要運行buildUrl才能進入parseUrl。sections節點buildUrl和parseUrl的xml屬性和js輸出格式和book節點的類似。<br>
sections_buildUrl(url)的url參數就是book上級節點輸出的url(不一定是book_parse的url參數,有book_parseUrl就是parseUrl的url參數,有book_buildUrl則是buildUrl的url參數),sections_parseUrl(url,html)輸出全話目錄所分布的url(多個時用";"隔開)。sections.parse輸出含有name(每一話漫畫名字)、url(該話網址)的數組,如果xml部分有sections節點,js部分book_parse里面data.sections變量直接為空白數組不填內容。book節點存儲的全局變量數據在sections的buildUrl讀取不了,需要在后面的parseUrl或parse環節讀取。
<br>
## 2.4.5 section節點(dtype=1)
```xml
<section cache="1d" method="get" options="0,0,0,1" parseUrl="section_parseUrl" parse="section_parse" header="referer"/>
```
```javascript
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);
}
```
|>section.parseUrl?|url;url;url(目標url字符串;多個時用";"隔開)<br>section的數據分布在多個網頁;或目標url需要中轉解析時使用|如果能滿足需要,可以用buildUrl(url)不用parseUrl(url,html)函數,區別是前者通過上級節點輸出的url生成新的目標url(不讀取html源代碼)
|:-|:-|:-|
|>section.parse|["",""](圖片地址的數組)<br>或者,帶背景音樂的{bg:"",list:["",""]}需引擎v25支持)<br>或者,帶背景音樂+帶時間位置的{bg:"",list:[{url:"",time:xxx},{...}]}(xxx毫秒)<br>bg=背景音樂url網址,用于支持有聲漫畫|section節點(dtype等于1)通過section.parse展示漫畫某一話所有圖片。上面示例源網站需要referer驗證才能顯示圖片,所以在xml部分加上header屬性(值為"referer")|
## 2.4.6 section節點(dtype=2)
|>section.parse|[{d:"xxx",t:1,c:"#999999",b:1,i:1,u:1,w:10,h:10}]<br>d:數據<br>t:類型(1,文本;8,視頻;9,圖片;10,原始大小圖片[居中])<br>c?:顏色<br>b?:是否加粗<br>i?:是否斜體<br>u?:是否下劃線<br>w?:視圖寬(當t=8或10時有效)<br>h?:視圖高(當t=8或10時有效)|dtype1漫畫和dtype2小說的格式唯一區別在于section_parse的輸出,前者輸出圖片(可帶音頻),后者輸出含有文本可帶圖片、視頻(比較少見)的數組。|
|:-|:-|:-|
(完)
- 序言
- 第一章 基礎
- 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基本用法