├── run.sh ├── loop.sh ├── README.md ├── Dockerfile └── hostloc_auto_get_points.py /run.sh: -------------------------------------------------------------------------------- 1 | touch /app/log 2 | nohup sh /app/loop.sh >> /app/log & 3 | tail -f /app/log 4 | -------------------------------------------------------------------------------- /loop.sh: -------------------------------------------------------------------------------- 1 | while true; 2 | do 3 | python3 /app/hostloc_auto_get_points.py 4 | sleep 6h 5 | done 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | env 2 | HOSTLOC_USERNAME=username1,username2... 3 | HOSTLOC_PASSWORD=password1,password2... 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | WORKDIR /app 3 | RUN apk update \ 4 | && apk add --no-cache curl python3 py3-pip \ 5 | && pip install requests pyaes \ 6 | && curl -L https://raw.githubusercontent.com/jzl1/hostloc-auto-get-points-docker/main/hostloc_auto_get_points.py > /app/hostloc_auto_get_points.py \ 7 | && curl -L https://raw.githubusercontent.com/jzl1/hostloc-auto-get-points-docker/main/run.sh > /app/run.sh \ 8 | && curl -L https://raw.githubusercontent.com/jzl1/hostloc-auto-get-points-docker/main/loop.sh > /app/loop.sh \ 9 | && chmod 755 /app/* \ 10 | && rm -rf /var/cache/apk/* 11 | CMD ["/bin/sh","/app/run.sh"] 12 | -------------------------------------------------------------------------------- /hostloc_auto_get_points.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import random 4 | import re 5 | import textwrap 6 | import requests 7 | 8 | from pyaes import AESModeOfOperationCBC 9 | from requests import Session as req_Session 10 | 11 | 12 | # 随机生成用户空间链接 13 | def randomly_gen_uspace_url() -> list: 14 | url_list = [] 15 | # 访问小黑屋用户空间不会获得积分、生成的随机数可能会重复,这里多生成两个链接用作冗余 16 | for i in range(12): 17 | uid = random.randint(10000, 50000) 18 | url = "https://hostloc.com/space-uid-{}.html".format(str(uid)) 19 | url_list.append(url) 20 | return url_list 21 | 22 | 23 | # 使用Python实现防CC验证页面中JS写的的toNumbers函数 24 | def toNumbers(secret: str) -> list: 25 | text = [] 26 | for value in textwrap.wrap(secret, 2): 27 | text.append(int(value, 16)) 28 | return text 29 | 30 | 31 | # 不带Cookies访问论坛首页,检查是否开启了防CC机制,将开启状态、AES计算所需的参数全部放在一个字典中返回 32 | def check_anti_cc() -> dict: 33 | result_dict = {} 34 | headers = { 35 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" 36 | } 37 | home_page = "https://hostloc.com/forum.php" 38 | res = requests.get(home_page, headers=headers) 39 | aes_keys = re.findall('toNumbers\("(.*?)"\)', res.text) 40 | cookie_name = re.findall('cookie="(.*?)="', res.text) 41 | 42 | if len(aes_keys) != 0: # 开启了防CC机制 43 | print("检测到防 CC 机制开启!") 44 | if len(aes_keys) != 3 or len(cookie_name) != 1: # 正则表达式匹配到了参数,但是参数个数不对(不正常的情况) 45 | result_dict["ok"] = 0 46 | else: # 匹配正常时将参数存到result_dict中 47 | result_dict["ok"] = 1 48 | result_dict["cookie_name"] = cookie_name[0] 49 | result_dict["a"] = aes_keys[0] 50 | result_dict["b"] = aes_keys[1] 51 | result_dict["c"] = aes_keys[2] 52 | else: 53 | pass 54 | 55 | return result_dict 56 | 57 | 58 | # 在开启了防CC机制时使用获取到的数据进行AES解密计算生成一条Cookie(未开启防CC机制时返回空Cookies) 59 | def gen_anti_cc_cookies() -> dict: 60 | cookies = {} 61 | anti_cc_status = check_anti_cc() 62 | 63 | if anti_cc_status: # 不为空,代表开启了防CC机制 64 | if anti_cc_status["ok"] == 0: 65 | print("防 CC 验证过程所需参数不符合要求,页面可能存在错误!") 66 | else: # 使用获取到的三个值进行AES Cipher-Block Chaining解密计算以生成特定的Cookie值用于通过防CC验证 67 | print("自动模拟计尝试通过防 CC 验证") 68 | a = bytes(toNumbers(anti_cc_status["a"])) 69 | b = bytes(toNumbers(anti_cc_status["b"])) 70 | c = bytes(toNumbers(anti_cc_status["c"])) 71 | cbc_mode = AESModeOfOperationCBC(a, b) 72 | result = cbc_mode.decrypt(c) 73 | 74 | name = anti_cc_status["cookie_name"] 75 | cookies[name] = result.hex() 76 | else: 77 | pass 78 | 79 | return cookies 80 | 81 | 82 | # 登录帐户 83 | def login(username: str, password: str) -> req_Session: 84 | headers = { 85 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 86 | "origin": "https://hostloc.com", 87 | "referer": "https://hostloc.com/forum.php", 88 | } 89 | login_url = "https://hostloc.com/member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1" 90 | login_data = { 91 | "fastloginfield": "username", 92 | "username": username, 93 | "password": password, 94 | "quickforward": "yes", 95 | "handlekey": "ls", 96 | } 97 | 98 | s = req_Session() 99 | s.headers.update(headers) 100 | s.cookies.update(gen_anti_cc_cookies()) 101 | res = s.post(url=login_url, data=login_data) 102 | res.raise_for_status() 103 | return s 104 | 105 | 106 | # 通过抓取用户设置页面的标题检查是否登录成功 107 | def check_login_status(s: req_Session, number_c: int) -> bool: 108 | test_url = "https://hostloc.com/home.php?mod=spacecp" 109 | res = s.get(test_url) 110 | res.raise_for_status() 111 | res.encoding = "utf-8" 112 | test_title = re.findall("(.*?)<\/title>", res.text) 113 | 114 | if len(test_title) != 0: # 确保正则匹配到了内容,防止出现数组索引越界的情况 115 | if test_title[0] != "个人资料 - 全球主机交流论坛 - Powered by Discuz!": 116 | print("第", number_c, "个帐户登录失败!") 117 | return False 118 | else: 119 | print("第", number_c, "个帐户登录成功!") 120 | return True 121 | else: 122 | print("无法在用户设置页面找到标题,该页面存在错误或被防 CC 机制拦截!") 123 | return False 124 | 125 | 126 | # 抓取并打印输出帐户当前积分 127 | def print_current_points(s: req_Session): 128 | test_url = "https://hostloc.com/forum.php" 129 | res = s.get(test_url) 130 | res.raise_for_status() 131 | res.encoding = "utf-8" 132 | points = re.findall("积分: (\d+)", res.text) 133 | 134 | if len(points) != 0: # 确保正则匹配到了内容,防止出现数组索引越界的情况 135 | print("帐户当前积分:" + points[0]) 136 | else: 137 | print("无法获取帐户积分,可能页面存在错误或者未登录!") 138 | time.sleep(5) 139 | 140 | 141 | # 依次访问随机生成的用户空间链接获取积分 142 | def get_points(s: req_Session, number_c: int): 143 | if check_login_status(s, number_c): 144 | print_current_points(s) # 打印帐户当前积分 145 | url_list = randomly_gen_uspace_url() 146 | # 依次访问用户空间链接获取积分,出现错误时不中断程序继续尝试访问下一个链接 147 | for i in range(len(url_list)): 148 | url = url_list[i] 149 | try: 150 | res = s.get(url) 151 | res.raise_for_status() 152 | print("第", i + 1, "个用户空间链接访问成功") 153 | time.sleep(5) # 每访问一个链接后休眠5秒,以避免触发论坛的防CC机制 154 | except Exception as e: 155 | print("链接访问异常:" + str(e)) 156 | continue 157 | print_current_points(s) # 再次打印帐户当前积分 158 | else: 159 | print("请检查你的帐户是否正确!") 160 | 161 | 162 | # 打印输出当前ip地址 163 | def print_my_ip(): 164 | api_url = "https://api.ipify.org/" 165 | try: 166 | res = requests.get(url=api_url) 167 | res.raise_for_status() 168 | res.encoding = "utf-8" 169 | print("当前使用 ip 地址:" + res.text) 170 | except Exception as e: 171 | print("获取当前 ip 地址失败:" + str(e)) 172 | 173 | 174 | if __name__ == "__main__": 175 | username = os.environ["HOSTLOC_USERNAME"] 176 | password = os.environ["HOSTLOC_PASSWORD"] 177 | 178 | # 分割用户名和密码为列表 179 | user_list = username.split(",") 180 | passwd_list = password.split(",") 181 | 182 | if not username or not password: 183 | print("未检测到用户名或密码,请检查环境变量是否设置正确!") 184 | elif len(user_list) != len(passwd_list): 185 | print("用户名与密码个数不匹配,请检查环境变量设置是否错漏!") 186 | else: 187 | print_my_ip() 188 | print("共检测到", len(user_list), "个帐户,开始获取积分") 189 | print("*" * 30) 190 | 191 | # 依次登录帐户获取积分,出现错误时不中断程序继续尝试下一个帐户 192 | for i in range(len(user_list)): 193 | try: 194 | s = login(user_list[i], passwd_list[i]) 195 | get_points(s, i + 1) 196 | print("*" * 30) 197 | except Exception as e: 198 | print("程序执行异常:" + str(e)) 199 | print("*" * 30) 200 | continue 201 | 202 | print("程序执行完毕,获取积分过程结束") --------------------------------------------------------------------------------