更多
昨天,我介绍了Discuz!论坛登录的Python脚本实现(点击进入)。今天,我继续这一系列,介绍Discuz!论坛最常见的一类脚本操作——发帖及回复。这里,我只实现普通主题的发表以及回复的脚本。
首先,我们依然要来分析发主题帖和回复时传输的数据包所post的数据:
上面这张图是发帖时的post数据。
formhash是昨天登录脚本中在登录后页面中获得的formhash。如果没有正确的formhash,发帖时就会显示“您的来路请求不正确”的错误提示,请大家一定要注意!
subject和message分别是发表的新主题帖的题目和帖子内容。
上面三项是必须要post的数据。下面再对剩下的几组键值对进行简要的介绍,这些键值对不是必需出现的,在程序中就不对其进行实现了。
posttime是发帖时间,会由程序自动生成,不需要我们关心。
wysiwyg全称为“What You See Is What You Get”,翻译成中文就是“所见即所得”。在Discuz!论坛发贴的高级界面我们会看到这一复选框,目前的名字为“纯文本”。
replycredit相关的一组键是回帖奖励。这里不对其进行具体的介绍。
readperm是阅读权限,Discuz!最高的阅读权限是255。
price是主题价格,Discuz!论坛允许对一些用户组设置可以发布付费主题,这样用户查看主题需要花费某种积分给发帖人。
tags是主题的标签,目前Discuz!默认每个主题最多有5个标签。
rushreplyfrom以及后续的几项是抢楼帖相关的选项,我们在这里也不再具体介绍。不过,当设置为抢楼帖时还会有一个叫做rushreply的选项出现。
usesig的含义是是否使用用户的个人签名档。
allownoticeauthor是指发帖后,如果有其他用户回复这个主题,是否已“提醒”的方式通知楼主。
当然,还有一些其他选项,需要勾选后,其相关的键值对才会出现在post数据中,我在这里也就不再做介绍了。
下面再来看回复时候的post数据。
这次的post数据不多,和上面发帖时也有很多一样的。不过在回复时,不再需要subject这一选项,我们只需要把formhash和message两项post过去就可以了。
接下来就是我的代码了。
第一个文件,依然是配置文件config.py,和昨天相比增加了如下的代码:
config.py
Python
POSTURL = DOMAIN + r'forum.php?mod=post&action=newthread&fid=FID&extra=&topicsubmit=yes'
REPLYURL = DOMAIN + r'forum.php?mod=post&action=reply&tid=TID&extra=&replysubmit=yes'
1
2
3
POSTURL=DOMAIN+r'forum.php?mod=post&action=newthread&fid=FID&extra=&topicsubmit=yes'
REPLYURL=DOMAIN+r'forum.php?mod=post&action=reply&tid=TID&extra=&replysubmit=yes'
这两行分别是发新主题和回复时的对应地址,我们可以从post的数据包中看到这个地址。
然后,我们修改discuz.py这个基类所在的文件,在Discuz类里添加一个字符串截断的代码,其余部分不变,可以参考昨天的文章:
discuz.py
Python
def _interception(self, string, length):
'''字符串截取(为简单起见,认为所有字均占3个字符)'''
if (len(string) > (length / 3)):
return string[0:(length / 3 - 1)] + '...'
else:
return string
1
2
3
4
5
6
def_interception(self,string,length):
'''字符串截取(为简单起见,认为所有字均占3个字符)'''
if(len(string)>(length/3)):
returnstring[0:(length/3-1)]+'...'
else:
returnstring
由于Discuz!默认主题题目不超过80个字符,对于utf-8来说也就是26个汉字,因此我们需要一个字符串截断方法,防止由于主题题目过长造成的发帖失败。在这里,为简单起见,认为任何字均为3个字符,不考虑英文字符、数字、半角标点等情况。
接下来,就是最重要的Post类了,我在post.py文件中进行实现。
post.py
Python
# -*- coding: utf-8 -*-
import re
import config
import discuz
class Post(discuz.Discuz):
def __init__(self):
super(Post, self).__init__()
self.post_success_pattern = re.compile(r'') # 发帖成功时匹配
self.post_fail_pattern = re.compile(r'
self.post_error_pattern = re.compile(r'
(?u)(.+)
') # 发贴失败的错误信息def newthread(self, fid, subject, message):
postdata = {
'subject': self._interception(subject, 80),
'message': message,
'formhash': self.formhash,
}
base_url = config.POSTURL
url = base_url.replace('FID', fid)
self.operate = self._get_response(url, postdata)
prefix = '主题 "%s" ' % postdata['subject']
self.__verify_post_status(prefix)
def reply(self, tid, message):
postdata = {
'message': message,
'formhash': self.formhash,
}
base_url = config.REPLYURL
url = base_url.replace('TID', tid)
self.operate = self._get_response(url, postdata)
prefix = '回复 "%s" ' % self._interception(message, 80)
self.__verify_post_status(prefix)
def __verify_post_status(self, prefix):
page_content = self.operate.read().decode('utf-8')
if self.post_success_pattern.search(page_content):
print "%s发布成功!" % prefix
elif self.post_fail_pattern.search(page_content):
post_error_message = self.post_error_pattern.search(page_content)
try:
print "%s发布失败!原因是:%s。" % (prefix, post_error_message.group(1))
except:
print "%s发布失败!原因是:未知原因。" % prefix
else:
print "无法确定%s发布状态" % prefix
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
# -*- coding: utf-8 -*-
importre
importconfig
importdiscuz
classPost(discuz.Discuz):
def__init__(self):
super(Post,self).__init__()
self.post_success_pattern=re.compile(r'')# 发帖成功时匹配
self.post_fail_pattern=re.compile(r'
self.post_error_pattern=re.compile(r'
(?u)(.+)
')# 发贴失败的错误信息defnewthread(self,fid,subject,message):
postdata={
'subject':self._interception(subject,80),
'message':message,
'formhash':self.formhash,
}
base_url=config.POSTURL
url=base_url.replace('FID',fid)
self.operate=self._get_response(url,postdata)
prefix='主题 "%s" '%postdata['subject']
self.__verify_post_status(prefix)
defreply(self,tid,message):
postdata={
'message':message,
'formhash':self.formhash,
}
base_url=config.REPLYURL
url=base_url.replace('TID',tid)
self.operate=self._get_response(url,postdata)
prefix='回复 "%s" '%self._interception(message,80)
self.__verify_post_status(prefix)
def__verify_post_status(self,prefix):
page_content=self.operate.read().decode('utf-8')
ifself.post_success_pattern.search(page_content):
print"%s发布成功!"%prefix
elifself.post_fail_pattern.search(page_content):
post_error_message=self.post_error_pattern.search(page_content)
try:
print"%s发布失败!原因是:%s。"%(prefix,post_error_message.group(1))
except:
print"%s发布失败!原因是:未知原因。"%prefix
else:
print"无法确定%s发布状态"%prefix
这里包含两个公开的方法,分别是发新主题的newthread和发回复的reply。还有一个私有方法__verify_post_status,是用于验证发表状态的。
最后是发帖和回复的使用方法。
post-demo.py
Python
# -*- coding: utf-8 -*-
import config
import post
if __name__ == "__main__":
my_account = post.Post()
my_account.login(config.USERNAME, config.PASSWORD)
my_account.newthread('2', u'发帖主题', u'发帖内容')
my_account.reply('10', u'发帖回复')
1
2
3
4
5
6
7
8
9
10
# -*- coding: utf-8 -*-
importconfig
importpost
if__name__=="__main__":
my_account=post.Post()
my_account.login(config.USERNAME,config.PASSWORD)
my_account.newthread('2',u'发帖主题',u'发帖内容')
my_account.reply('10',u'发帖回复')
这样,我们就实现了Discuz!最基本的发帖(普通帖)和回复功能的脚本,一般的使用也以这种发帖为主。当然,如果还有更高需求,要发表一些特殊类型帖(如悬赏帖、投票帖、辩论帖、活动帖、商品帖)的话,可以自己得到传输的post数据,分析需要填写的内容,在post.py中进行完善。