Pt-Gen 项目地址收集

** 2019.5.13更新: 由于豆瓣限制 ( 豆瓣疑下线所有公开 API ),本人不再保证非本人搭建api的有效性。 **

请认准:
https://api.rhilip.info/ptgen.htmlhttps://ptgen.rhilip.info/

相关介绍Blog:


欸,本贴收集了基于 Rhilip/Pt-help 或者 BFDZ/PT-Gen 建立的公开性 豆瓣、IMDb、Bangumi、Steam链接自动生成简介 的工具页面。

(方便你在被限制或者暂时无法使用时进行切换,注意,相同API地址的返回或者限制一致

前两个官方版本之外,排名不分先后

前端页面 API地址
https://api.rhilip.info/ptgen.html https://api.rhilip.info/tool/movieinfo/gen
https://www.bfdz.ink/tools/ptgen https://api.nas.ink/infogen
https://pter.club/ptgen.html https://api.druexoy.top/movieinfo/gen
https://pt.gztown.net/movieinfo.html https://api.druexoy.top/movieinfo/gen
http://yezi.ga/ https://api.yezi.ga/infogen

通过编辑修改torrent文件来辅种

本文在 北洋园PT :: 查看主题 “【高级辅种教程】教你编辑修改torrent文件” - Powered by NexusPHP 基础上修改,如果你没有tju帐号,可以访问原作者 @DXV5 在Github上的原文备份 https://github.com/ylxb2016/PT-help

此处转载以及修改均获得原作者 @DXV5 的许可,并对他在使用多种软件对种子文件进行修改的尝试表示感谢23333(虽然我本人还是推荐直接从发布站直接搜索下载

从种子结构说起

我曾在以前的Blog中提到过Bencode的编码方式,使用winHex(如果没有,用NotePad打开看看也行)就可以直接打开你从BT站下载的torrent,可以发现torrent种子其实是一个大的字典被序列化后的字符串,用Bencode Editor解析结果如下:

TIM截图20190225163842.png

其顶层有announcecreated byinfo等字段。在这些字段中info字段最为关键,因为一个种子的info_hash值就是通过对该字段进行sha1计算出来的。

1
2
// 此处代码仅作示例
$info_hash = pack("H*", sha1(Bencode::encode($torrent_dict['info'])));

PT站点对种子文件的修改

体感上,@DXV5 将站点对上传torrent后的修改情况大致分为以下3种情况:

1.只修改了announce字段,或添加announce-list的list
2.修改了announce、announce-list、source字段
3.除了常规的修改announce、announce-list、source字段,还会添加或者修改一些字段ttg_tag,publisher-url,comment或者一些特殊的检验字段什么的。	

以TJUPT为例子,TJUPT属于情况2和情况3
- 如果原torrent包含source字段,那么就是常规的修改announce字段,添加announce-list的list,修改source为“[www.tjupt.org] 北洋园PT”.
- 如果原torrent不包含source字段(比如转发TTG的种子),除了常规的修改announce字段,添加announce-list的list,会删除了TTG的torrent特有的ttg_tag字段,增加了source字段“[www.tjupt.org] 北洋园PT”,还修改了comment字段“Torrent From TJUPT”和created by“[www.tjupt.org]”字段。

国内、国外大部分PT站点都属于情况2,也就是修改了announce+source字段.
如果是情况3的话,就比较繁琐了,需要修改的地方比较多了,有的甚至就没必要去编辑torrent来得到相同hash的种子了


但是实际,我们可以简单的将其再归纳为2种情况(或者说两个步骤),即修改 root->info 的内容(在这个层次对种子的任何修改都会更改种子的info_hash信息)以及非 root->info,服务器会在种子文件上传和下载的过程中对其做修改,但是两者更改的信息是不一样的。

种子上传过程中主要修改的是种子的root->info信息,诸如设root->info->private=1,添加(或者更改)root->info->source信息。而不同的info_hash会被bt软件识别为不一样的种子。这也是为什么NP构架的种子在bt软件中分开,而不是向ZX的种子那样变成添加tracker,因为各站的source是不一样的。ttg_tag这个自造的非标准tag其实也是source的一种变形。此外,非root->info的信息同样可以在上传的时候更改,例如NP就会在上传的时候更改root->announce信息并移除root->announce-list,root->node,以防止上传者信息泄露。

用户下载种子文件过程就可以修改种子的其他信息(非root->info信息)了,比如announce、announce-list、publisher-url、comment字段。这些字段的修改不影响种子的info_hash,且其修改可以为用户添加其passkey信息,或者通过comment提供展示信息。

2018年PT站发种观察报告

同前一篇使用Pt-Board数据的分析文章 基于 Pt-Board 的国内 Pt 站点大数据分析

因前文不涉及2018年数据,故本文对国内PT站2018年数据进行分析。

本帖娱乐统计向,标题党和瞎BB,都请不要太当真。
不吹不黑不挑事,不诋毁任何站点,不希望伤害任何人。
谢绝任何形式转载,拒绝搞事!

Pt-Analytics介绍

详见: https://rhilip.github.io/PT-help/ptanalytics ,因为使用了ES6语法,请使用现代浏览器打开浏览。

首先后端脚本定时从数据库中统计数据并生成对应的json静态信息。
页面使用$.getJSON()方法从后端API中获取信息并存在localStorage中。
渲染方法从localStorage中获取信息并使用echarts渲染出折线图、日历热力图等统计图形。

涉及站点数据总览

对2018年整年段(时间戳范围为1514736000-1546272000),Pt-Board共观察到53个国内站点共计395951条种子发布记录进行统计,总览结果如下。表内结果按年度发种数倒序排列。

教育网站点

站点 初始种子id 结束种子id 计数
NPU 118285 151591 30180
6V 1613533 1643446 21921
BYR 253944 276648 21739
NYPT 48332 64342 15779
TJUPT 163345 173792 10251
NWSUAF6 133139 142737 9446
SJTU 148511 157076 8112
WHU 43771 48302 4405
ZX 601490 606079 3922
XAUAT6 130647 134742 3400
HUDBT 136965 140241 2973
CUGB 10949 13411 2424
DUTPT 42477 45158 1468
ghtt 8304 28191 1142

国内公网站点

站点 初始种子id 结束种子id 计数
MTeam 220282 268979 43580
HDSKY 61568 89906 28041
Ourbits 66442 93986 27057
TTG 343219 369869 23611
CHDBits 29466 48983 19264
CMCT 59331 79085 19208
HDChina 279865 299635 17380
HDHome 32609 47728 14145
CCFBits 170789 180694 9382
GZTown 4824 11709 6816
BTSCHOOL 1756 7843 5988
HDU 15406 21275 5824
MHZ 2 5836 5781
U2 29827 33513 3102
TLFBits 18222 21394 3009
Hyperay 14405 17411 2987
HDStreet 47683 50721 2781
MINE 5b3105aab0118034121adc04 5c29021e8ccb9280a561902d 2520
OpenCD 72670 75524 2250
HDArea 8925 18478 1888
KeepFrds 6782 8634 1843
HD4FANS 12700 14463 1758
HDTime 12604 14050 1447
HDCity 34922 36392 1343
99PT 1 1192 1036
TCCF 33289 34293 999
PTHOME 1 1141 864
JoyHD 67950 68919 853
TVHome 1 755 744
AFan 1 755 225
CHCDVD 57 240 133
SolaGS 1209 1309 55
TTHD 7080 7129 50
pter.club 1 89 39
MyHDbits 21 64 35
91Ring 12 19 8
QXXZ 126 133 8
HDNode 147 148 2

数据说明:

  1. 因Pt-Board在2018年全量抓取对应pt站点信息,故上述计数中包括站点在2018年发布但是被删除的种子数。
  2. 在4月19号,Pt-Board所在服务器出现严重的网络中断且未被及时发现,导致部分热门站点缺失该日数据。
  3. 内网型站点不做统计。

漫谈PT架构(5): 构造一个SPT(Announced部分)

相比于相对简单的Scrape,Announce构造相对麻烦。(嗯,从上篇文章的发出之后,我又尝试了ThinkPHP5、Symfony等架构的测试。经过多次尝试后,决定在某个Swoole的PHP框架上再次开发。之后的文章示意代码依次为准。

发出鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽鸽的声音~

总代码示例

这里贴出的是一个示例的步骤(伪代码),可以看到Tracker的Announce步骤依次如下,对字段的检验和选择,获得种子信息并缓存加快响应、处理请求、生成返回信息。我将依次对这几个部分进行说明。

1
2
3
4
5
6
$this->checkAnnounceFields(&$queries);   // 检查请求字段
$this->getTorrentInfo($queries, $userInfo, &$torrentInfo); // 根据info_hash获得种子信息
$role = ($queries['left'] == 0) ? 'yes' : 'no'; // 获得peer身份信息
$this->processAnnounceRequest($queries, $role, $userInfo, $torrentInfo); // 处理请求
$this->generateAnnounceResponse($queries, $role, $torrentInfo, &$rep_dict); // 生成返回信息
return Bencode::encode($rep_dict); // 返回请求

使用Vultr的Snapshots API完成自动备份(Python版)

除了免费赠送可挂载的50G Block Storage之外,Snapshots可能也是吸引我使用Vultr的原因。虽然我一直没有从快照中还原过2333(但是好歹有个心理安慰是不是)

注意,默认情况下最多创建11个Snapshots!!!!

可能是因为本人搜索姿势不对吧,使用“vultr snapshot script”在Google中只搜索到 Automated Snapshots 这一个使用PHP写的脚本。但是为了系统安全,我在php.ini中禁用了“shell_exec”等方法。同时,该PHP脚本只能在Vultr主机上运行且只对当前机器进行备份。

所以自己模仿着这个脚本写了一下Python3的版本,全部脚本如下,你也可以到Gist上查阅:Rhilip/vultr-snapshot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import re
import time
import datetime
import requests

# -------------用户设置 开始-------------- #

# 填写Vultr的API KEY
API_KEY = ""

# 请填写服务器主IP地址或者SUBID,(二选一即可)
MAIN_IP = "1.2.3.4"
SUBID = None

BACKUP_TAG_PREFIX = "auto_backup" # 备份头
MAX_NUM_OF_BACKUPS = 3 # 最大备份数

# -------------用户设置 结束-------------- #

# Get base info
api_endpoint = "https://api.vultr.com/v1/"
day = time.strftime("%Y-%m-%d", time.localtime())


# simple wrapper to access vultr api
def vultr(method = "GET",action = "" , data = None):
return requests.request(method,"{}{}".format(api_endpoint,action),headers = {"API-Key" : API_KEY}, data=data)


server_list = vultr("GET","server/list").json()

# Find subid if not set.
if SUBID == None:
for server_subid,server_info in server_list.items():
if server_info.get("main_ip", None) == MAIN_IP:
SUBID = server_subid
break

if SUBID == None:
raise Exception("Fail to find subid for IP: {}".format(MAIN_IP))

snapshot_list_raw = vultr("GET","snapshot/list").json()

# Resort the raw snapshot list dict to list obj
snapshot_list = [v for k,v in snapshot_list_raw.items()]

# Get auto-backup snapshot list
backup_snapshot_list = list(filter(lambda x:re.search("{}-{}".format(BACKUP_TAG_PREFIX,SUBID),x["description"]),snapshot_list))

# Remove old auto-backup-snapshot
if len(backup_snapshot_list) >= MAX_NUM_OF_BACKUPS:
to_remove_snapshot_list = sorted(backup_snapshot_list,
key = lambda k:datetime.datetime.strptime(k["date_created"],"%Y-%m-%d %H:%M:%S")
)[:-MAX_NUM_OF_BACKUPS]

for s in to_remove_snapshot_list:
vultr("POST","snapshot/destroy",{"SNAPSHOTID": s["SNAPSHOTID"]})

# create New auto-backup-snapshot
vultr("POST","snapshot/create",{"SUBID": SUBID,"description": "{}-{}-{}".format(BACKUP_TAG_PREFIX,SUBID,day)})

完整食用方法如下

NexusPHP允许无Tracker种子上传

在某些NexusPHP构架的PT站点上传无tracker地址的种子时会提示目录缺少值,至少要填写一个tracker

长期以来,这个缺陷一直被要求up者在做种时任意填写一个字段使该tracker地址非空。

不过经过分析,发现这个只需要改一行代码就行。

照例,上NexusPHP的源代码:ZJUT/NexusPHP/takeupload.php#L140 ,是的,只要修改这一行为下列即可~

1
list($info) = dict_check($dict, "info");

完整Patch

TIM截图20181114172045.png

后面都是瞎写的,就不用点开看了233333

使用 CF-Firewall 与 Nginx 联动限制访问频率以及自动ban IP

因为movieinfogen源站的原因,导致我原来公开的PT-Help工具的moveinfo/gen遭到了大量的使用,此外因为原先设计理念上的问题(ps. 不是因为我懒(事实就是,前面都是借口)),没有对相关请求做相关的身份验证。

导致部分人使用脚本批量请求该接口生成对应简介信息,以至于经常被豆瓣封请求,使得正常用户不可用。

(滥用公开服务可耻!!!!为什么不自建!!!源代码都是公开的!!!

请不要恶意使用PT-Gen【pt吧】_百度贴吧

128cf51f3a292df5aa252f46b1315c6035a87365.jpg


那么就来做些限制吧。(想不到我一个写爬虫开始学编程的人如今也要开始做反爬了23333

使用Nginx对User-Agent限制

这是最开始想到的办法,因为很早之前我就使用了 mitchellkrogza/nginx-ultimate-bad-bot-blocker 的相关规则以及自动更新模块,来对一些Bad Bot基于UA头进行限制。

关于这一系列规则的安装还请见其项目的README就行,挺简单的,唯一有些不便的就是它所有的配置都是围绕使用包管理器安装的Nginx来进行的,每次使用都需要夹带相关参数来覆盖默认配置。

所以记得用-h多看对应说明,没确认前千万不要用-x执行。

因为当初的安装记录找不到了,这里就贴一下crontab升级时候使用的吧,希望对同样使用lnmp.org提供的lnmp一件包的朋友有帮助。

1
00 22 * * * sudo /usr/local/sbin/update-ngxblocker -c /usr/local/nginx/conf/ -b /usr/local/nginx/conf/bot.d/ -n Y

**注意:**安装后,可能会通不过Nginx的检验。。记得注释掉没过的就ok。。

该规则自带空白模块,允许用户在bot.d目录下的blacklist-user-agents.conf中添加基于UA的屏蔽,因为原滥用者都是使用脚本调用curl来进行的,所以直接屏蔽UA头为curl的。故在文件中添加

1
"~*\bcurl\b"  3;

然而好景不长,毕竟UA头都是可以伪造的,简单的一个IE就直接破解了2333

TIM截图20181113101412.png

Nginx自带的 rate_limit

通过对访问频率来限制,相关的教程可见官方的文档:

考虑到使用的方便,毕竟这个/tools下不止挂了本人的movieinfogen服务,所以简单设置如下:

1
2
3
4
5
6
7
8
9
10
limit_req_zone $binary_remote_addr zone=pthelp:10m rate=5r/m;
server
{
server_name api.rhilip.info ;

location ~* ^/tool(/.*)?$ {
limit_req zone=pthelp burst=3 nodelay;
# ....
}
}

相对较为宽松,仅限制了每分钟5个请求,而且还补充了burst和nodelay项。对超过限制的直接返回503就行~

这个破起来多简单,我当初自己爬豆瓣的时候都知道time.sleep(),滥用者会想不到吗?

503的话就休息一段时间。或者可以都不用这么这样,反正Nginx最后一定会放行的,疯狂请求不就ok了吗?

TIM截图20181113095353.png

ps. 这个IP的同学在我一天的Nginx记录中疯狂刷出了近2000条记录,其中返回503的就有近1700条。。。。

使用 CF-Firewall 基于IP自动限制

根据对之前的访问频率的判断,我们可以知道,正常用户一天的请求量不会超过50次。(不,是我瞎讲的,不是基于统计的结果)

大体只有爬虫会疯狂的进行请求,所以我们不如根据总访问次数来对爬虫IP进行限制。

但是由于网站在Cloudflare的CDN之后,所以直接在系统层面使用iptables以及配套日志分析脚本对单一IP进行限制是不可能的,如果在Nginx上使用deny则每次修改都要重新reload较为麻烦,所以不如直接使用CF-Firewall进行限制较为方便。

与之有关的项目 SukkaW/cloudflare-block-bad-bot-ruleset

手动添加可以直接在网页上使用Firewall面板,并设置规则为Block即可。
1542076072900.png

通过日志可以看到确实被有效的屏蔽了~~~

通过试验,以CF-Firewall屏蔽会返回404状态码。

1542076267675.png


但是毕竟我们不可能时时刻刻都查看日志吧,所以需要用脚本自动分析日志并通过CF-API来自动禁用对应IP。

CF的Global API Key在profile面板获取。

示例脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import re
import CloudFlare
from collections import Counter

# 基本信息 开始
CF_API_KEY = ''
CF_API_EMAIL = ''
DOMAIN = ''
LOG_FILE = ''
MAX_RATE = 50
# 基本信息 结束

# 后面无需更改
cf = CloudFlare.CloudFlare(email = CF_API_EMAIL, token = CF_API_KEY)
zone_info = cf.zones.get(params = {
'name': DOMAIN
})
zone_id = zone_info[0]['id']

# 获取当前被封禁IP列表防止重复插入
ip_ban_list_raw = cf.zones.firewall.access_rules.rules.get(zone_id, params = {
"configuration.target": "ip",
"mode": "block"
})
ip_ban_list = list(map(lambda x: x["configuration"]["value"], ip_ban_list_raw))

# 获取当前访问日志信息
with open(LOG_FILE, 'r') as f:
log_now = f.readlines()

# 获取访问ip地址
movieinfo_gen_log = list(filter(lambda x: re.search("movieinfo/gen", x), log_now))
ip_log = list(filter(lambda x: x not in ip_ban_list, map(lambda x: re.search("^(.+?) - - \[", x).group(1), movieinfo_gen_log)))

# 使用Counter来计数, 并筛选
ip_count = Counter(ip_log).most_common()
ban_ip_list = list(filter(lambda x: x[1] > MAX_RATE, ip_count))

# 自动禁用
for ip, count in ban_ip_list:
cf.zones.firewall.access_rules.rules.post(zone_id, data = {
"mode": "block",
"configuration": {
"target": "ip",
"value": ip
},
"notes": "Pt-Gen max-rate reached."
})

然后放到crontab中每隔几分钟运行一次不久ok了吗?

后续注意点

  1. Nginx使用set_real_ip_from模块从Cloudflare中获取用户真实IP地址,并记录到access_log中。相关方法将之前Blog: Cloudflare 下 Nginx 获取用户真实 IP 地址 - R 酱小窝
  2. 上述示例脚本真的仅作示意,临时现写的。所以为什么宁愿多写一个脚本也不修改原有架构呀

Pt站点禁用Aria2客户端方法分析

为什么Private Tracker需要限制客户端,因为并不是所有的BT客户端都是完全遵守BEP规范的实现(其实常用的ut、tr之流也并不是完全遵守的2333)。所以以NP框架为代表的站点要尽量把用户使用的客户端限制在允许的范围,使得能相对正确的记录用户的做种信息。

但是这并不能解决一些能伪造User-Agent以及peer_id_prefix的软件,例如Aria2这样的综合下载软件或者一些流量作弊软件。因为他们可以很容易的伪造,以绕过NP的检测。甚至于Aria2专门提供了这样的配置项来进行修改。例如下面的例子中就将Aria2伪装成 Transmission/2.77,能成功的绕过原版的NexusPHP检测。

1
2
3
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.77

ps. 甚至可以这样伪装(UA为Tr 2.79,但peer_id为Tr 2.77).虽然一看就知道是有问题的,但是根据NP的匹配规则是能通过的233333

1
2
peer-id-prefix=-TR2770-
user-agent=Transmission/2.79

那能不能通过一些手段尽可能的检测出来这种伪装的客户端那?这就是本文想要探讨的内容。

太长不看

在announce.php文件 check passkey行为 之前添加以下信息即可~

1
2
3
if (isset($_SERVER["HTTP_Want-Digest"]) || strpos($peer_id, $key) !== false) {
err("Anti-Cheater: You may use Aria2");
}

LNMP环境下NextCloud的安装调优

我真是信了某些站攻略的邪,瞎写的,也不知道自己配置过没有。

随着开学的临近,我也想重新调整下我的文档数据,正好原先使用的坚果云备份时间到期不想继续使用。(在5月做设计的时候遇到很严重的同步问题,一直没能解决)

所以把私人云储存的目光瞄到了 Vultr 赠送的50G空间(对于文档来说够用了,大文件都是用GDrive+OneDrive存储+本地冷备份,照片则本地冷备份+Google Photos+Yandex.Disk)。

环境要求

官方文档见: System requirements — Nextcloud 13 Administration Manual 13 documentation

本人是用的是惯例的: Ubuntu 16.04 LTS + LNMP 1.5 (lnmp.org)

同机多NexusPHP站点改造

虽然我不知道为什么 @NPCHK 大佬会有这种要求,但既然提了就顺带帮忙解决。Nginx以及数据库都没有什么大的问题,对应着分开就是了。但是在缓存方面需要动点手脚,否则多站点公用一个缓存会出现错乱的问题。

NP的Cache方法在 classes\class_cache.php 中,这个class_cache是对PHP的Memcache类的一个扩写。并在function.php中以include的形式引入并在全局声明。

但是可惜的是,Cache的配置并没有在allconfig.php文件提供,而是直接写死在了其构造中。所以我们需要增加prefix的支持。完整patch如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Index: classes/class_cache.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- classes/class_cache.php (revision 84c8ca2263c5e16f912a48e8208f2e06642ca2be)
+++ classes/class_cache.php (revision )
@@ -14,6 +14,7 @@
var $cacheWriteTimes = 0;
var $keyHits = array();
var $languageFolderArray = array();
+ var $prefix = '';

function __construct($host = 'localhost', $port = 11211) {
$success = $this->connect($host, $port); // Connect to memcache
@@ -163,6 +164,7 @@

// Wrapper for Memcache::set, with the zlib option removed and default duration of 1 hour
function cache_value($Key, $Value, $Duration = 3600){
+ $Key = $this->prefix . $Key;
$this->set($Key,$Value, 0, $Duration);
$this->cacheWriteTimes++;
$this->keyHits['write'][$Key] = !$this->keyHits['write'][$Key] ? 1 : $this->keyHits['write'][$Key]+1;
@@ -219,6 +221,7 @@

// Wrapper for Memcache::get. Why? Because wrappers are cool.
function get_value($Key) {
+ $Key = $this->prefix . $Key;
if($this->getClearCache()){
$this->delete_value($Key);
return false;
@@ -237,6 +240,7 @@

// Wrapper for Memcache::delete. For a reason, see above.
function delete_value($Key, $AllLang = false){
+ $Key = $this->prefix . $Key;
if ($AllLang){
$langfolder_array = $this->getLanguageFolderArray();
foreach($langfolder_array as $lf)

看多简单,毕竟整个cache类的底层就那么三个方法。。