XIU2/UserScript

[自动无缝翻页] `自定义翻页规则` 示例说明

XIU2 opened this issue · 4 comments

XIU2 commented

自动无缝翻页 脚本从 v4.8.8 版本开始,支持了自定义翻页规则。

不过当初仅为自用,压根没有考虑过这需求,我只能勉强实现自定义翻页规则,因此只适用于简单的静态加载内容网站,当然这类网站占大多数,我写的数百规则中大部分都是这类网站,因此一般是够用了~

# 自写规则 前提条件

  1. 了解 JSON 基本格式 ( 主要是逗号、转义、双引号 )
  2. 了解 CSS 或 XPath 选择器用法 ( 优先使用 CSS 选择器)
  3. 可能需要会一点 JavaScript 语言 / 正则表达式 ( 依靠 URL 匹配规则 )

简单的来说,这就是提供给 懂一些技术 的用户自给自足制作规则用的~

大多数网站的规则一般都只需要这样:

// "aaa" 是规则名,唯一,自定义翻页规则 将覆盖同名的 外置翻页规则
// "url" 是用来控制哪些网站中页面适用该规则,省略后代表该规则应用于全站
// "scrollD" 是用来控制翻页敏感度的(越大就越早触发翻页,访问速度慢的网站需要调大,可省略(注意逗号),默认 2000)

"aaa": {
    "host": "aaa.com",
    "url": "/xxx/",
    "pager": {
        "nextL": "xxx",
        "pageE": "xxx",
        "replaceE": "xxx",
        "scrollD": 2000
    }
}

大家也可以在下面 提出改进建议分享规则~


# 规则总览(只列出了目前 自定义翻页规则 能用的)

「 点击展开查看 」
    host:       先匹配域名,可以是文本,也可以是正则表达式,也可以是数组(数组中也可以文本和正则表达式混合使用)
    url:         匹配到域名后,再来匹配 URL,也可以用来执行一些 JS 代码
    urlC:      对于使用 pjax 技术的网站,需要监听 URL 变化来重新判断翻页规则(需要放在 url: 中,自定义规则的话需要使用 fun.isUrlC())

    noReferer:   获取下一页内容时,不携带 Referer(部分网站携带与不携带可能不一样)
    hiddenPN:    不显示脚本左下角的页码
    history:     添加历史记录 并 修改当前 URL(默认开启,对于不支持的网站要设置为 false)
    thread:      对于社区类网站,要在 帖子内 的规则中加入这个,用于脚本的 [帖子内自动翻页] 功能(即用户可以选择开启/关闭所有社区类网站帖子内的自动翻页)
    style:       要插入网页的 CSS Style 样式
    retry:       允许获取失败后重试
    blank:       强制新标签页打开链接(1 = <base> 方式,2 = 对 body 点击事件委托,3 = 仅对 pageE 的父元素点击事件委托,4 = 仅对 pageE 的父元素添加 target="_blank")

pager: {
    type:     翻页模式
       1 = 由脚本实现自动无缝翻页,可省略(适用于:静态加载内容网站,常规模式)

       2 = 只需要点击下一页按钮(适用于:网站自带了 自动无缝翻页 功能)
           nextText:    按钮文本,当按钮文本 = 该文本时,才会点击按钮加载下一页(避免一瞬间加载太多次下一页,下同)
           nextTextOf:  按钮文本的一部分,当按钮文本包含该文本时,才会点击按钮加载下一页
           nextHTML:    按钮内元素,当按钮内元素 = 该元素内容时,才会点击按钮加载下一页
           interval:    点击间隔时间,对于没有按钮文字变化的按钮,可以手动指定间隔时间(单位 ms,默认 500,当指定上面三个时,会忽略 interval)
           isHidden:    只有下一页按钮可见时(没有被隐藏),才会点击

       3 = 依靠 [基准元素] 与 [浏览器可视区域底部] 之间的距离缩小来触发翻页(适用于:主体元素下方内容太多 且 高度不固定时)
           scrollE:     作为基准线的元素(一般为底部页码元素),和 replaceE 一样的话可以省略
           scrollD:     基准元素 - 可视区域底部

       4 = 动态加载类网站(适用于:简单的动态加载内容网站)
           insertE:     用来插入元素的函数

       5 = 插入 iframe 方式来加载下一页,无限套娃(适用于:部分动态加载内容的网站,需要允许 iframe 且支持通过 GET/POST 直接打开下一页)
           style:       加载 iframe 前要插入的 CSS Style 样式(比如为了悬浮的样式与下一页的重叠,隐藏网页底部间距提高阅读连续性)
           iframe:      这个必须加到 pager{} 外面(这样才会在该域名的 iframe 框架下运行脚本)

       6 = 通过 iframe 获取下一页动态加载内容插入本页,只有一个娃(适用于:部分动态加载内容的网站,与上面不同的是,该模式适合简单的网页,没有复杂事件什么的)
           loadTime:    预留的网页加载时间,确保网页内容加载完成

    nextL:    下一页链接所在元素
    pageE:    要从下一页获取的元素
    insertP:  下一页元素插入本页的位置(数组第一个是基准元素,第二个是基准元素的前后具体位置)
       1 = 插入基准元素自身的前面
       2 = 插入基准元素内,第一个子元素前面
       3 = 插入基准元素内,最后一个子元素后面
       4 = 插入基准元素自身的后面
       5 = 插入 pageE 列表最后一个元素的后面(该 insertP 可以直接省略不写,等同于 ['pageE', 5] )
       6 = 插入该元素自身内部末尾(针对小说网站等文本类的),附带参数 insertP6Br: true, 用来中间插入换行
    // 小技巧:当基准元素是下一页主体元素的父元素时(或者说要将下一页元素插入到本页同元素最后一个后面时)是可以省略不写 insertP
         例如:当 pageE: 'css;ul>li' 且 insertP: ['css;ul', 3] 时,实际等同于 ['css;ul>li', 5]
               当 pageE: 'css;.item' 且 insertP: ['css;.item', 4] 时,实际等同于 ['css;.item', 5]
               当 pageE: 'css;.item' 且 insertP: ['css;.page', 1] 时,实际等同于 ['css;.item', 5]
         注意:如 pageE 中选择了多类元素,则不能省略 insertP(比如包含 `,` 与 `|` 符号),除非另外的选择器是 <script> <style> <link> 标签

    replaceE: 要替换为下一页内容的元素(比如页码),省略则不进行替换,省力的话可以和 nextL 内容一样(但不能是 JS 代码)
    scrollD: 翻页动作触发点([滚动条] 与 [网页底部] 之间的距离),数值越大,越早开始翻页,一般是访问网页速度越慢,该值就需要越大,省略后默认 2000

    scriptT:  单独插入 <script> 标签
       0 = 下一页的所有 <script> 标签
       1 = 下一页的所有 <script> 标签(不包括 src 链接)
       2 = 下一页主体元素 (pageE) 的同级 <script> 标签
       3 = 下一页主体元素 (pageE) 的子元素 <script> 标签

    interval:   翻页后间隔时间(单位 ms)
    forceHTTPS: 下一页链接强制 HTTPS
},
function: {
       bF = 插入前执行函数
       bFp = 参数
       aF = 插入后执行函数
       aFp = 参数
}

## 内置函数

为了方便,规则中执行 JS 代码时可以使用脚本内置函数:

「 点击展开查看 」
// 返回当前网页的 URL 路径,等同于 location.pathname
fun.lp()

// 该 indexOF 默认不区分大小写,不过正则表达式除外,需要自己加上 /i 修饰符
// 当前网页的 URL 路径中如果包含指定文本则返回 true,等同于 location.pathname.indexOf('xxx') > -1
fun.indexOF('xxx')
// 也支持正则,对于一些需要复合/多个条件判断时才有必要用这个,否则直接正则写到 "url": "/xxx/" 更好
fun.indexOF(/xxx/)
// 加上 's' 参数,则匹配范围改为 URL 末尾的参数,等同于 location.location.search.indexOf('xxx') > -1
fun.indexOF('xxx', 's')

// 根据 UA 和屏幕大小判断是否为手机浏览器,是手机就返回 true,适用于手机版、电脑版网页域名一致但规则不一致的
fun.isMobile()

// 返回所有指定元素,支持 CSS、Xpath 选择器
fun.getAll('元素选择器', 目标节点(省略则为 document))
// 返回一个指定元素,支持 CSS、Xpath 选择器
fun.getOne()
// 返回所有指定元素,支持 Xpath 选择器
fun.getAllXpath()
// 返回一个指定元素,支持 Xpath 选择器
fun.getXpath()
// 返回所有指定元素,支持 CSS 选择器
fun.getAllCSS()
// 返回一个指定元素,支持 CSS 选择器
fun.getCSS()

// 通用型获取下一页地址(从 元素 中获取页码)
fun.getNextE()
// 通用型获取下一页地址(从 元素 中获取页码,URL 替换 page= 参数)
fun.getNextEP()
// 通用型获取下一页地址(从 元素 中获取页码,URL 替换 pathname 路径)
fun.getNextEPN()
// 通用型获取下一页地址(从 URL 中获取页码,URL 替换 pathname 路径)
fun.getNextUPN()
// 通用型获取下一页地址(从 URL 中获取页码,URL 替换 page= 参数)
fun.getNextUP()
// 通用型获取下一页地址(从 form input 中获取,返回 GET URL)
fun.getNextF()

// 插入 <Style> 自定义 CSS 样式
fun.insStyle()

// 插入前函数(加载图片)
fun.src_bF(pageE, [0, '图片元素选择器', '属性名'])
// 如果你要选择的是 img[data-original] 或 img[data-src] 且加载模式为 0,那么后面这个数组可以省略为 fun.src_bF(pageE)

// 插入前函数(正则过滤,主要针对小说站这种)
fun.xs_bF(pageE, [/正则/, '替换为该内容,可空'])

## URL 匹配 "url": "xxx",

相比于其他同类脚本,我当初为了方便及避免正则折磨,将 域名 和 URL 分开匹配了,毕竟大量复杂的正则匹配会很费时间,分开后就先匹配域名,再去匹配 URL,间接也提高了效率(而且域名大都是文本,非常快 ~

匹配 URL 路径示例(正则表达式匹配范围仅是域名后面的 路径 及 ? 参数):

注意:URL 匹配是从前往后按顺序匹配的,因此同一个网站有多个不同页面规则时,如果 URL 匹配规则有些许重复的地方就需要控制一下顺序。另外,对于正则表达式均不区分大小写(即脚本默认加上 /i 修饰符)。

「 点击展开查看 」
## URL 路径示例:/ (首页)

# 正则表达式
"url": "/^\\/$/",
# 脚本内置函数
"url": "return (fun.lp() == '/')",
# 上面这个等同于下面这个,只是有点长不好记写着不方便,所以我才搞了个内置函数
"url": "return (location.pathname == '/')",

## URL 路径示例:非 / (即不是首页)

"url": "/^\\/.+/",
"url": "return (fun.lp() != '/')",
"url": "return (location.pathname != '/')",

## URL 路径示例:/post (即位于根路径)

"url": "/^\\/post/",
"url": "return (fun.lp() == '/post')",
"url": "return (location.pathname == '/post')",

## URL 路径示例:/aaa/post/111 (即位于路径中间)

"url": "/\\/post\\//",
"url": "return fun.indexOF('/post/')",
"url": "return location.pathname.indexOf('/post')",

## URL 路径示例:/aaa?pid=233 (即只匹配路径参数)

"url": "/pid=/",
"url": "return fun.indexOF('pid=', 's')",
"url": "return location.search.indexOf('pid=')",

## URL 路径示例:/aaa/post/111?pid=233 (即需要同时匹配 /post/ 和 pid=)
## 中间的连接符 && 等于 且 的意思,即需要两边都满足条件才会返回 true

"url": "return (fun.indexOF('/post/') && fun.indexOF('pid=', 's'))",
"url": "return (location.pathname.indexOf('/post') && location.search.indexOf('pid='))",

## URL 路径示例:/aaa/post/111 或 /search (即只需要匹配 /post/ 或 /search)
## 中间的连接符 || 等于 或 的意思,继续只需要任意一个满足条件就会返回 true

"url": "return (fun.indexOF('/post/') || fun.indexOF('/search'))",
"url": "return (location.pathname.indexOf('/post') || location.pathname.indexOf('/search'))",

## 一些情况下,难以依靠 URL 特征来匹配页面,那么还能用网页元素
## 比如目标网页中含有一个 `#pager` 元素(常用页码元素),那么就可以这样写:

"url": "return fun.getCSS('#pager')",

# 规则示例

大家也可以参考我写的数百个翻页规则:https://github.com/XIU2/UserScript/blob/master/other/Autopage/rules.json

注意:因为自定义翻页规则的局限性以及格式的差别,因此不能完全照搬脚本内置规则!
以下是我从内置翻页规则中找的几个规则,并改成了自定义翻页规则的格式,大家可以自行对比理解差别。

注意:以下为了方便对每一条规则进行解释,我直接在规则旁边写了注释,但 JSON 是不存在注释的,因此添加到脚本中时记得移除所有注释内容,否则会报错!

当只有一个规则时,是这样的(示例):

{
    "aaa": {
        "host": "xxxx",
        "xxxx": "..."
    }
}

当有多个规则时,要这样连接到一起的(最后一个 } 不加逗号):

{
    "aaa": {
        "host": "xxxx",
        "xxxx": "..."
    },
    "bbb": {
        "host": "xxxx",
        "xxxx": "..."
    }
}

因此在写入自定义翻页规则的时候,记得包裹着所有规则的 { } 大括号,以及最容易犯错的逗号问题(最后一个 } 不能有逗号)。


## 同一个网站 不同页面 适用不同翻页规则

不同翻页规则的 host 是可以一样的,但是规则名不能一样,通过不同的 url 规则来进行匹配。
比如下面这个就是先从 URL 匹配 /a 如果没找到,那么再去匹配 /b,如果还没找到那么就直接应用 aaa_3 规则(因为这个没有 url 规则,所以就变成兜底的规则了)。

{
    "aaa": {
        "host": "a.com",
        "url": "/^\\/a/",
        "xxxx": "xxxx"
    },
    "aaa_2": {
        "host": "a.com",
        "url": "return (fun.lp() == '/b')",
        "xxxx": "xxxx"
    },
    "aaa_3": {
        "host": "a.com",
        "xxxx": "xxxx"
    }
}

## 翻页模式 1

这个模式是最常见的,即网站是纯静态加载内容的。

// 规则名(唯一,不能重复,可以是中文、字母、数字什么的)
"百度搜索": {
// 单个域名
// 支持正则,格式示例如:"host": "/\\.baidu\\.com/", 也支持多个正则组成的数组
    "host": "www.baidu.com",
// 当 URL 路径 = /s 时才应用该规则
// 不过为了方便自定义规则,所以这里也给改成了支持正则表达式,像这样:"url": "/^\/s$/",
    "url": "return (fun.lp() == '/s')",
// 应用规则时,要插入的自定义 CSS 样式(比如用来隐藏广告什么的)
    "style": "xxxx {display: none !important;}",
    "pager": {
// 模式 1 的话,可以省略 "type": 1, 这条规则
// 这个是 Xpath 选择器,即在 id="page" 的元素下寻找一个内容包含 下一页的 a 元素
// 这里也可以执行 JS 代码来 return 返回下一页 URL,但要记得在开头加上 js; 标识
        "nextL": "id('page')//a[contains(text(),'下一页')]",
// 这个是主体内容元素,即 id="content_left " 的元素下所有子元素
// 因为要向主体内容元素末尾插入,所以 insertP 可以省略
        "pageE": "#content_left",
// 这个是将当前网页中的某些元素替换为下一页的同类元素(如页码),想省力的话可以和 nextL 内容一样(但不能是 JS 代码)
        "replaceE": "#page",
// 这个是翻页动作触发点,即滚动条 与 网页底部之间小于 2000 时就会翻页,越大就意味着翻页越频繁
        "scrollD": 2000
    }
}
"在线之家": {
// 多个域名的话,就要写成数组了
    "host": [
            "www.zxzj.me",
            "www.zxzj.fun"
    ],
    "url": "return (fun.lp() != '/' && !fun.indexOF('/detail/') && !fun.indexOF('/video/'))",
    "style": "div.stui-page__all {display: none !important;}",
    "pager": {
        "nextL": "//ul[contains(@class, 'stui-page__item')]//a[text()='下一页']",
        "pageE": "ul.stui-vodlist > li",
        "replaceE": "ul.stui-page__item",
        "scrollD": 1000
    },
// 这下面的就是翻页前/后,要做的事情了
    "function": {
// 插入前执行的代码,在将主体元素插入网页之前,先处理一下(相比于插入后处理,会更快更好)
// 这个是脚本内置的函数,用来加载图片(一些网站是默认图片,只有浏览到时才会加载图片,而脚本翻页后就只能由脚本去加载图片了)
// 这个里面的 0 代表图片是 src 属性(1 代表是 background-image: url() 背景样式类的图片)
// 这个里面的 0 代表图片是 src 属性(1 代表是 background-image: url() 背景样式类的图片)
// 这个 "img[data-original]" 代表要寻找的图片 CSS 选择器
// 这个 "data-original" 代表要把图片的 data-original 属性内容设置为 src 属性
        "bF": "return fun.src_bF(pageE, [1, 'a[data-original]', 'data-original'])"
    }
}
"动漫花园": {
    "host": [
        "dmhy.org",
        "share.dmhy.org",
        "dmhy.anoneko.com"
    ],
    "pager": {
        "nextL": "//div[@class='nav_title']/a[contains(text(), '下一')]",
        "pageE": "#topic_list > tbody > tr",
        "replaceE": ".nav_title"
    },
    "function": {
// 插入后执行的代码
// 直接插入 script 标签是不会执行其内部代码的,只有这样插入后才行
        "aF": "document.body.appendChild(document.createElement('script')).textContent = `xxx`"
    }
}

## 翻页模式 2

这个算是第二常见的,即网站自带了自动无缝翻页功能,只需要点一下网页中相应按钮即可。

"discuz_forum": {
    "host": "www.52pojie.cn",
// 没有 url 则代表该规则适用于全站,在规则不影响其他页面的情况下,可以偷懒不写
    "pager": {
// 模式 2
        "type": 2,
// 网页自带的无缝加载下一页/更多内容的元素
        "nextL": "#autopbn",
// 上面元素包含 "下一页" 文字时,脚本才会点击,用来避免多次重复点击,不写的话则会有默认 500ms 的间隔,可以用 interval 规则指定间隔(例如 "interval": 1000, 就是点击下一页元素后最少间隔 1000 毫秒才会继续点击)
        "nextTextOf": "下一页",
        "scrollD": 2000
    }
}

## 翻页模式 3

模式 1 的变种,对于部分网页主体内容与网页底部之间距离不固定的网站,需要把基准从网页底部改为页码等指定元素处。

"3DM": {
    "host": "www.3dmgame.com",
    "pager": {
// 模式 3
            "type": 3,
            "nextL": "li.next > a",
// 有些网站需要插入主体内容后,执行一些网页自身的 JS 文件,所以这里同时选择了 script 和网页主体元素
            "pageE": ".news_warp_center > *, script[src*='common.js']",
            "replaceE": ".pagewrap",
// 基准元素,当和 replaceE 一样时,可以省略
            "scrollE": ".pagewrap",
// 如果要运行 pageE 里选择的 script 代码,那么就需要指定 2
            "scriptT": 2,
// 为了避免翻页过快,而设置的翻页最小间隔(单位 ms)
            "interval": 500,
            "scrollD": 500
    }
}

## 翻页模式 5

对于部分动态加载内容的网站,如果其支持 iframe 且可以通过 GET 直接打开下一页,那么就能用该模式。

插入 iframe 方式来加载下一页,无限套娃~

"知乎 - 用户主页/收藏夹": {
    "host": "www.zhihu.com",
    "url": "fun.isUrlC(); if (fun.indexOF(/\\/people\\/.+\\/.+/) || fun.indexOF('/collection/')) {if (self != top) {fun.insStyle('#ProfileHeader {display: none !important;}')}; return true;}",
// 强制新标签页打开链接
    "forceTarget": true,
// 允许脚本在该网站的 iframe 中运行,模式 5 必须加上
    "iframe": true,
    "pager": {
// 模式 5
        "type": 5,
// 如果要用 JS 代码生成下一页 URL,则记得在前面加上 js; 标识
        "nextL": "js;let next = fun.getCSS('.Pagination .PaginationButton--current+button:not(.PaginationButton-next)');if (next) return (location.origin + location.pathname + '?page=' + next.textContent)",
// 如果要在插入下一页 iframe 后向本页插入自定义样式,则还可以在这里加个 "style" 规则(比如为了悬浮的样式与下一页的重叠,隐藏网页底部间距提高阅读连续性)
        "scrollD": 2000
    }
}

## 翻页模式 6

对于部分动态加载内容的网站,与上面不同的是,该模式适合简单的网页,没有复杂事件什么的。

插入 iframe 加载下一页,等其处理完后,获取下一页动态加载内容插入本页,只有一个娃~

"拷贝漫画 - 分类页": {
// 对于有规律的多域名,可以写成正则表达式
    "host": "/copymanga\\./",
    "url": "/\\/comics/",
    "pager": {
// 模式 6
        "type": 6,
        "nextL": "li.next > a",
        "pageE": ".exemptComicItem",
        "replaceE": "ul.page-all",
// 预留的下一页 iframe 网页加载时间,确保网页内容加载完成
        "loadTime": 1000,
        "scrollD": 3000
    },
    "function": {
// 插入本页前,将懒加载图片改为直接加载
// 如果你要选择的是 img[data-original] 或 img[data-src] 且加载模式为 0,那么后面这个数组可以省略为 return fun.src_bF(pageE)
        "bF": "return fun.src_bF(pageE, [0,'img[data-src]','data-src'])"
    }
}
XIU2 commented

感觉这样搞还是有点不方便,就干脆又加了个 外置翻页规则列表,把我最近这几天写的 自定义翻页规则 都给放进去了(后续也会把一些简单的网站规则都放进去),这样用户只需要 更新外置翻页规则 即可。

当然,自定义翻页规则依然保留,提供给有动手能力的人~

您好!版主大大,我以前一直用这个https://github.com/machsix/Super-preloader, 这个最近更新很少了,感觉你这个自定义翻页规则真的棒,不知道能否添加分隔栏,因为不同页面连接得太完美了,导致我们很难分清哪些没看,那些看了!

XIU2 commented

@Yunuuuu 不会添加分隔栏,你看我脚本名字就知道了:自动“无缝”翻页
我就是因为不喜欢其他脚本的一些设计(特别是分隔栏我看着很别扭),所以我才专门按照我个人喜好写了这个脚本。

多谢回复!明白了,谢谢您!

你好 大佬 可以添加这个https://www.haoman8.com无缝翻页吗 看漫画的

XIU2 commented

@Viene0850 已添加支持~(阅读页、分类页

更新外置翻页规则 即可(浏览器右上角 Tampermonkey 扩展图标内的脚本菜单


另外,下次提交申请时,请新开 Issues,这个 Issues 下只讨论自定义翻页规则相关话题。