代码部分可能存在大量令人高血压的语句,如果使您感到不适,非常抱歉
省流版:请各位帮忙看看下面一段代码里哪里存在可能导致内存溢出/泄漏的问题。如果可以的话,请指点一下如何优化,万分感谢。
以下是完整代码内容(已隐去部分隐私信息):
import sys
f = open('a.log', 'a')
sys.stdout = f
sys.stderr = f # redirect std err, if necessary
import xlrd3
games = xlrd3.open_workbook(filename=r'games.xlsx')
gamelist = games.sheet_names()
sgames = xlrd3.open_workbook(filename=r'sgames.xlsx')
sgamelist = sgames.sheet_names()
import asyncio
from bilibili_api import Credential, sync, user
from bilibili_api.session import Session, Event, send_msg
from bilibili_api.user import User, RelationType
from bilibili_api.utils.picture import Picture
#登录凭据,记得改成自己的
SESSDATA = "※"
BILI_JCT = "※"
BUVID3 = "※"
credential = Credential(sessdata=SESSDATA, bili_jct=BILI_JCT, buvid3=BUVID3)
session = Session(credential)
list1 = [] #用于记录收到私信的粉丝的 uid
list2 = [] #用于记录关注时间,作为判定取关并重新关注操作的凭据
list3 = [] #用于记录无视开关状态的白名单 uid
with open('list1.txt',"r") as f: #读取已记录列表,该列表存储在本地,位置为指令执行时所处的目录
for line in f:
list1.append(line.strip('\n'))
with open('list2.txt',"r") as f: #同上
for line in f:
list2.append(line.strip('\n'))
with open('list3.txt',"r") as f: #同上
for line in f:
list3.append(line.strip('\n'))
status = []
@session.on(Event.TEXT) #轮询,检测收到文字私信时触发
async def reply(event: Event):
token = "1"
code = event.content[-5:]
name = event.content[:-5]
uid = event.sender_uid #查询发信者的 uid
talker = User(uid, credential)
rela = await talker.get_relation(uid)
follow = rela['be_relation']['attribute'] #查询关注关系,此处描述的是对方对你的关注状态:0=未关注,2=已关注,6=互关,128=黑名单
mtime = rela['be_relation']['mtime'] #关注时间,以时间戳的形式记录,未关注时为 0 ,取关后重新关注会刷新,以此为凭据进行拉黑;需注意关系变为互相关注时也会刷新,后文单独拎出来讨论
fid = str(uid) #转换为字符串
ftime = fid + str(mtime) #同上
if fid == "※":
if event.content == "/终止": #杀死进程
await session.reply(event, "脚本已停运")
session.close()
elif event.content == "/开始": #用于开始自动回复的口令,触发后将开始自动回复私信
await session.reply(event, "脚本已开启")
status.append(token)
elif event.content == "/结束": #用于暂停自动回复的口令,触发后将不再自动回复私信
await session.reply(event, "脚本已暂停")
status.clear()
elif event.content.isdigit():
if event.content in list1:
list1.remove(event.content)
with open('list1.txt', "w") as f:
for element in list1:
f.write(element + "\n")
await send_msg(credential=credential, msg_type=Event.TEXT, content="用户已移出黑名单", receiver_id=uid)
else:
await send_msg(credential=credential, msg_type=Event.TEXT, content="用户当前不在黑名单中,操作失败", receiver_id=uid)
if token in status or fid in list3: #开关开启 [或] 发信人在白名单内
if follow > 0 and follow < 128: #筛选:已关注用户
if fid not in list1: #筛选:未通过关键词获取过回复的粉丝
if event.content == "彩蛋":
await session.reply(event, "恭喜你找到了一颗彩蛋!")
elif name in gamelist: #正确的游戏关键词,接下来将进一步检测口令中的数字
gamex = games.sheet_by_name(sheet_name=name)
codelist = gamex.col_values(0)
if code in codelist: #口令正确,标志着对方成功获取回复;触发时发信人的 uid 和关注时间会随之被记录
def search(gamecode):
num_rows = gamex.nrows
for row in range(num_rows):
if gamex.cell_value(row, 0) == code:
return gamex.cell_value(row, 1)
return None
password = search(code)
await session.reply(event, "非常感谢您下载本人汉化的游戏《" + name + "》。\n 您的密码是:\n" + password) #正确关键词对应的回复
list1.extend([fid]) #记录发信粉丝的 uid
with open("list1.txt","a+") as f:
f.write(fid + "\n")
list2.extend([ftime]) #记录发信粉丝的 uid 和关注时间
with open("list2.txt","a+") as f:
f.write(ftime + "\n")
else: #曾通过关键词获取过回复的粉丝
if ftime in list2 or follow == 6: #对方两次私信的关注时间一致(或关系为互相关注),说明对方自上次获取回复后没有取消关注过(或对方是你的好友),此时执行正常流程,跟上文一致,但不重复记录
if event.content == "彩蛋":
await session.reply(event, "恭喜你找到了一颗彩蛋!")
#我需要治疗 1.1
elif name in gamelist: #正确的游戏关键词,接下来将进一步检测口令中的数字
gamex = games.sheet_by_name(sheet_name=name)
codelist = gamex.col_values(0)
if code in codelist: #口令正确,标志着对方成功获取回复
def search(gamecode):
num_rows = gamex.nrows
for row in range(num_rows):
if gamex.cell_value(row, 0) == code:
return gamex.cell_value(row, 1)
return None
password = search(code)
await session.reply(event, "非常感谢您下载本人汉化的游戏《" + name + "》。\n 您的密码是:\n" + password) #正确关键词对应的回复
else: #对方两次私信的关注时间不一致且不是你的互关好友,说明对方自上次获取回复后曾取消关注
await session.reply(event, "获取密码功能已失效。")
#如果想要拉黑,复制粘贴这一句: [ await talker.modify_relation(relation=RelationType.BLOCK)]
#即使开关关闭、发送人也不在白名单时也能获取的游戏
elif follow > 0 and follow < 128: #筛选:已关注用户
if fid not in list1: #筛选:未通过关键词获取过回复的粉丝
if event.content == "彩蛋α":
await session.reply(event, "恭喜你找到了一颗高级彩蛋!")
elif name in sgamelist: #正确的关键词,接下来将进一步检测口令中的数字
gamex = sgames.sheet_by_name(sheet_name=name)
codelist = gamex.col_values(0)
if code in codelist: #口令正确,标志着对方成功获取回复;触发时发信人的 uid 和关注时间会随之被记录
def search(gamecode):
num_rows = gamex.nrows
for row in range(num_rows):
if gamex.cell_value(row, 0) == code:
return gamex.cell_value(row, 1)
return None
password = search(code)
await session.reply(event, "非常感谢您下载本人汉化的游戏《" + name + "》。\n 您的密码是:\n" + password) #正确关键词对应的回复
list1.extend([fid]) #记录发信粉丝的 uid
with open("list1.txt","a+") as f:
f.write(fid + "\n")
list2.extend([ftime]) #记录发信粉丝的 uid 和关注时间
with open("list2.txt","a+") as f:
f.write(ftime + "\n")
else: #曾通过关键词获取过回复的粉丝
if ftime in list2 or follow == 6: #对方两次私信的关注时间一致(或关系为互相关注),说明对方自上次获取回复后没有取消关注过(或对方是你的好友),此时执行正常流程,跟上文一致,但不重复记录
if event.content == "彩蛋α":
await session.reply(event, "恭喜你找到了一颗高级彩蛋!")
elif name in sgamelist: #正确的关键词,接下来将进一步检测口令中的数字
gamex = sgames.sheet_by_name(sheet_name=name)
codelist = gamex.col_values(0)
if code in codelist: #口令正确,标志着对方成功获取回复;触发时发信人的 uid 和关注时间会随之被记录
def search(gamecode):
num_rows = gamex.nrows
for row in range(num_rows):
if gamex.cell_value(row, 0) == code:
return gamex.cell_value(row, 1)
return None
password = str(search(code))
await session.reply(event, "非常感谢您下载本人汉化的游戏《" + name + "》。\n 您的密码是:\n" + password) #正确关键词对应的回复
else: #对方两次私信的关注时间不一致且不是你的互关好友,说明对方自上次获取回复后曾取消关注
await session.reply(event, "获取密码功能已失效。") #可删除
sync(session.start())
关于文件:脚本内涉及到的几个.txt 和.xlsx 文件大小均不超过 20kb ,这些文件本身的大小应该不是问题。日志输出文件.log 这段时间的运行下来,大小已经达到 20Mb+了,但由于是只写不读,应该也不是这个的问题?
背景:如您所见,这段脚本的作用是实现“bilibili 根据私信收到的关键词触发自动回复”的功能,目的是为下载本人汉化的游戏作品的用户分发启动密码。
本人此前没有编程经验,某天想到设置自动回复的点子后误打误撞发现了Bilibili API的库,于是动手实践,一点点试错码出来了一个姑且能满足需要的脚本。后来在使用过程中慢慢改进,断断续续花了四个多月的时间变成了现在看到的这个样子。
现在这个脚本挂在 Oracle Cloud 的服务器上长期运行。最初一两个月还算顺利,但从两个多月前开始偶尔会出现脚本没有响应的状况,b 站给自己发送的私信不会触发回复,Xftp 和 Xshell 也无法连接到远程主机,但每次等一会儿就会自行恢复。而最近半个月这种断线变得越来越频繁,每次断线的时间也在延长。查看了 Oracle 提供的统计数据发现了端倪:
每次断线都跟内存占用率暴增的时间点吻合,图表上缺失的时间段也正是断线的持续时间。我意识到可能是内存溢出导致的服务器宕机,但在正常运行期间查看进程的内存占用率也看不出所以然。目前只能推测是这段脚本里什么地方写得不好,引入了大量临时变量之类的导致内存爆了。由于缺乏相关的知识,我对于该怎么解决也没有头绪,四处搜寻下找到了本站,斗胆提问,希望能得到各位的指点。
有任何建议或看法都非常欢迎。感谢!
题外话,这些汉化作品是免费发布的,不会向用户收取费用。设置启动密码的初衷是避免盗卖,此前的资源因为无需验证,时常会被人二次上传并牟利;而在采用这种方式之后用户必须通过私信我来获取密码,盗卖的情况就没再发生过了。不用 b 站自带的自动回复功能是因为支持的关键词数量太少了。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.