前回の記事はステーションBのデータをクロールしたもので、目的は私自身のコメントを削除することであり、詳細は序文に記載されています。コメントを受け取りました。(自分のコメントのみ) 削除するにはどうすればよいですか?
方法 1
コメントが保存されているフォルダーで次のコードを実行して、現在のフォルダーの txt ファイルに保存されている、削除するすべてのコメントの URL を取得します。動画コメントの場合は、get_url()のURLを生成するコードをご自身で修正してください。
import os
import csv
import re
dir_path=os.path.abspath(os.path.dirname(__file__))
delete_comment_url_list=[] # 所有要删除的评论的URL
def input_mid(): # 输入帐号ID
while True:
mid=input('请输入你的帐号ID,是一串数字,可以在个人空间的URL中看到,或个人空间右侧的UID:')
pattern=re.compile(r'\D+') # 非数字内容至少出现一次
try:
result=pattern.search(mid).group()
except AttributeError: # 没有匹配到非数字内容,说明输入格式正确,所以终止循环
break
except:
raise Exception('发生了未捕获的异常')
if result:
print('无效的帐号ID,请重新输入')
return mid
def get_dynamic_id(file_name): # 从csv文件名中提取dynamic_id
temp_str=file_name.split('ID')[1]
pattern=re.compile(r'\D{1}') # 只匹配一次,即第一个非数字字符
result=pattern.search(temp_str).group() # ID后面的第一个非数字字符
dynamic_id=temp_str.split(result,1)[0] # ID这两个字符已去掉,ID后第一个字符是数字,从这个数字开始,到第一个非数字字符结束(不含这个非数字字符),就是dynamic_id
return dynamic_id
def get_url(mid): # 生成需要删除的评论的URL
file_list=os.listdir(dir_path)
for file_name in file_list:
file_path=dir_path+'/'+file_name
if (file_name[-3:] in ['csv','CSV']) and (os.path.isfile(file_path)):
with open(file_path,'r',newline='',encoding='utf-8') as f:
reader=csv.DictReader(f) # 将每行中的信息映射到一个OrderedDict。省略fieldnames参数时,键是csv文件的标题
for row in reader: # row是当前行的内容
if row['mid']==mid:
dynamic_id=get_dynamic_id(file_name)
url='https://t.bilibili.com/'+dynamic_id+'#reply'+row['rpid']
delete_comment_url_list.append(url)
def save_data(): # 每行写入一个URL,复制的代理IP池.py
file_path=dir_path+'/'+'delete_bilibili_comment_url.txt'
with open(file_path,'w',encoding='utf-8') as f:
f.write('') # 由于实际写入时是追加,所以每次重新运行时需先清空旧数据
with open(file_path,'a',encoding='utf-8') as f:
for i in delete_comment_url_list:
f.write(i+'\n')
print('数据已保存到本地')
if __name__=='__main__':
mid=input_mid()
get_url(mid)
save_data()
リンク例(ニュース用ですが、動画も#返信+コメントIDで繋ぎます)
https://t.bilibili.com/56764707xxxxxxxxxx#reply53xxxxxxxx
このテキストを開いて中のリンクにアクセスすると、自分のコメントを見つけることができます。見つからない場合は、ページを一番下までスクロールし、図に示すようにページ番号を手動で数回切り替えてから、ブラウザのアドレス バーで Enter キーを押します (ページを更新しようとはしませんでしたが、は機能しません)、さらに数回試して、 [自分のコメント] を見つけて、その横にある 3 つの点をクリックして削除します。
方法 2
削除するコメントが多数ある場合、方法 1 は不適切であり、時間がかかりすぎます。
まず手動でコメントを削除し、fiddlerでパッケージをキャプチャし、URLとパラメータに従ってコードを記述し、自動的にバッチで削除します。
コードを実行するには、Cookie とキャプチャされたフォーム パラメーター csrf の値を入力する必要があります。
さらに、 csrf パラメータは、Flask や Django のフォームの隠しフィールドに似ているはずです。これはフォーム内の非表示の HTML 要素です (図に示すように) が、右クリックでは見つかりませんでした。 -check. その値を取得する方法があります。教えてください。
import requests
import os
import re
import csv
import time
url='https://api.bilibili.com/x/v2/reply/del'
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36 Edg/96.0.1054.43',
'Referer':'https://t.bilibili.com/',
'Cookie':"" # 需要Cookie,可在打开一条动态时用fiddler抓取
}
dir_path=os.path.abspath(os.path.dirname(__file__))
error_info_list=[] # 所有删除评论失败时错误日志需要的信息,列表中每个元素是字典,键是信息中参数名,值是参数值
OK_info_list=[] # 所有删除评论成功时日志需要的信息,列表中每个元素是字典,键是信息中参数名,值是参数值
def input_mid(): # 输入帐号ID
while True:
mid=input('请输入你的帐号ID,是一串数字,可以在个人空间的URL中看到,或个人空间右侧的UID:')
pattern=re.compile(r'\D+') # 非数字内容至少出现一次
try:
result=pattern.search(mid).group()
except AttributeError: # 没有匹配到非数字内容,说明输入格式正确,所以终止循环
break
except:
raise Exception('发生了未捕获的异常')
if result:
print('无效的帐号ID,请重新输入')
return mid
def get_dynamic_id_and_oid_and_type1(file_name): # 获取dynamic_id和oid和type1
temp_list=file_name.split(',')
dynamic_id=temp_list[0].split('ID')[1]
oid=temp_list[1].split('oid')[1]
temp_str_for_type=temp_list[2].split('type')[1]
pattern=re.compile(r'\D{1}')
result=pattern.search(temp_str_for_type).group()
type1=temp_str_for_type.split(result,1)[0] # 和爬取评论的代码中type_in_url_for_get_comment是同一个值
return dynamic_id, oid, type1
def get_rpid(file_path,mid): # 获取当前动态中指定用户的所有评论ID
rpid_list=[] # 有些动态下,同一个人有多条评论,把它们的rpid存在列表中
with open(file_path,'r',newline='',encoding='utf-8') as f:
reader=csv.DictReader(f)
for row in reader:
if row['mid']==mid:
rpid_list.append(row['rpid'])
return rpid_list
def save_error_log(): # 删除失败时,保存日志
file_name='删除评论失败时错误日志.txt'
file_path=dir_path+'/'+file_name
# 每行写入一句日志
with open(file_path,'w',encoding='utf-8') as f:
f.write('') # 由于实际写入时是追加,所以每次重新运行时需先清空旧数据
with open(file_path,'a',encoding='utf-8') as f:
for error_info_dict in error_info_list:
t=f'删除B站动态ID{error_info_dict["dynamic_id"]}中评论ID{error_info_dict["rpid"]}时失败,对应的oid是{error_info_dict["oid"]},type是{error_info_dict["type1"]},服务端返回的json数据是{error_info_dict["text"]}'
f.write(t+'\n')
print('错误日志已保存到本地')
def save_OK_log(): # 记录已删除的评论信息
file_name='删除评论成功时日志.txt'
file_path=dir_path+'/'+file_name
# 每行写入一句日志
with open(file_path,'w',encoding='utf-8') as f:
f.write('') # 由于实际写入时是追加,所以每次重新运行时需先清空旧数据
with open(file_path,'a',encoding='utf-8') as f:
for OK_info_dict in OK_info_list:
t=f'已删除B站动态ID{OK_info_dict["dynamic_id"]}中评论ID{OK_info_dict["rpid"]},服务端返回的json数据是{OK_info_dict["text"]}'
f.write(t+'\n')
print('成功日志已保存到本地')
def delete_comment(file_name,file_path,mid): # 删除评论
# 若执行过程中因报错导致终止,则有一些评论已被删除,剩一些未删除;再次执行时,还会删除之前已删除的评论,B站仍然返回删除成功的json数据(手动构造含#reply的URL,访问时(按博客里方法一多试几次)无法定位到对应的评论,说明真的已被删除)。所以不影响最终结果,我就未适配这种情况(即下次删除前先自动剔除已删除的),只记录了删除成功的日志
dynamic_id, oid, type1=get_dynamic_id_and_oid_and_type1(file_name)
rpid_list=get_rpid(file_path,mid)
if len(rpid_list)>0: # 说明当前csv文件中有指定用户的评论
for rpid in rpid_list:
form_data={
'oid':oid,
'type':type1,
'jsonp':'jsonp',
'rpid':rpid,
'csrf':'' # 只要保持登录状态,无论是否关闭浏览器再重新打开,csrf的值不变。未测试退出帐号重新登录会不会变,若变,则手动构造含#reply的URL,访问后定位到评论并手动删除评论,期间用fiddler抓取表单数据。除了抓包,应该还能在前端代码中找到csrf的值,因一般提交表单时,input框附近有表单隐藏域,但我在这里没找到。
}
r=requests.post(url,headers=headers,data=form_data)
time.sleep(5)
result=r.json()
global request_count # main函数中已定义
request_count=request_count+1
code=result['code']
message=result['message']
ttl=result['ttl']
if not (code==0 and message=='0' and ttl==1): # 删除失败
error_info_dict={}
error_info_dict['dynamic_id']=dynamic_id
error_info_dict['oid']=oid
error_info_dict['type1']=type1
error_info_dict['rpid']=rpid
error_info_dict['text']=r.text # 传r.text就不用分别传code和message和ttl
error_info_list.append(error_info_dict)
print('删除失败,详情见本地错误日志文件(程序运行结束且非人为结束才生成)')
else: # 删除成功
OK_info_dict={}
OK_info_dict['dynamic_id']=dynamic_id
OK_info_dict['rpid']=rpid
OK_info_dict['text']=r.text # 传r.text就不用分别传code和message和ttl
OK_info_list.append(OK_info_dict)
if __name__=='__main__':
csv_file_count=0 # 保存评论数据的csv文件总数
request_count=0 # 发送删除请求的次数,发送就说明当前文件中有要删除的指定用户的评论,所以也表示含要删除的指定用户的评论的csv文件总数
mid=input_mid()
print('正在运行,请耐心等待')
file_list=os.listdir(dir_path)
for file_name in file_list:
file_path=dir_path+'/'+file_name
if (file_name[-3:] in ['csv','CSV']) and (os.path.isfile(file_path)):
delete_comment(file_name,file_path,mid)
csv_file_count=csv_file_count+1
print(f'共有{csv_file_count}个csv文件,其中{request_count}个含要删除的指定用户的评论')
if request_count>0:
if len(OK_info_list)>0:
save_OK_log()
if len(error_info_list)>0:
save_error_log()
else:
print('所有评论已删除')