您的位置:首页 > 其它

怎样利用CSDN论坛公开的API实现自己的论坛工具

2008-03-28 15:34 567 查看
csdn论坛公开了一些常用api,不过内部测试阶段,地址是http://forum.csdn.net/OpenApi/forumapi.asmx还有一个使用的demo,http://forum.csdn.net/OpenApi/ForumOpenAPIDemo.rar,源码在这里下载demo源码

总体概述:

公开的方法如下:

CheckOutTopic :结贴

GetForums :获得论坛列表

GetTopicsOfUser :获得我的帖子,我参与的帖子,我得分的帖子,别人问我的帖子

GetUserProfile :获得用户资料

PointDonate :积分捐赠

Post :发帖

Reply :回帖

至于获得帖子和获得帖子列表的方法,虽然没有提供独立的api,但是都可以借助论坛现有的资源,待会会单独讲到

上面的API除了GetForums外,都需要输入一个identity实体,表明你的身份,返回了一个bool型变量,表示操作是否完成,结果会以out变量的形式输出,同时输出的一般还有错误信息Error

identity的参考定义如下

/// <summary>
/// 用户身份信息
/// </summary>
public struct Identity{
/// <summary>
/// 用户名
/// </summary>
public string username;
/// <summary>
/// 密码
/// </summary>
public string password;
}

他包含用于身份验证的用户名和密码,除获得论坛列表以外,其他的操作均要求此参数,调用者可以将用户信息加密存与本地,详细请参考账户管理

而错误信息的参考定义如下



/// <summary>
/// 错误信息
/// </summary>
public struct Error
{
/// <summary>
/// 错误id
/// </summary>
public int errId;

private string _errInfo;
/// <summary>
/// 错误信息
/// </summary>
public string errInfo;

/// <summary>
/// 描述
/// </summary>
public string description;

}



这个实体存放了调用过程中返回的错误,如果返回结果为false,我们就可以查看或者错误信息:

比如下面的积分捐赠的代码段


Identity id=dp.GetDefaultAccount();


Error error;




if (!openApiService.PointDonate(dp.GetDefaultAccount(), tbUserName.Text, point, "abc", out error))


ErrorForm.ShowDialog(error);


else


MessageBox.Show("捐赠成功");





获得论坛GetForums

GetForums 非常的简单,没有传入参数,方法的返回值是一个Forum实体数组

Forum的参考定义和具体字段含义如下

/// <summary>
/// 论坛信息
/// </summary>
public struct Forum
{
/// <summary>
/// 论坛id
/// </summary>
public Guid forumId;
/// <summary>
/// 父论坛id
/// </summary>
public Guid parentForumId;
/// <summary>
/// 论坛名称
/// </summary>
public string name;
/// <summary>
/// 别名
/// </summary>
public string alias;
/// <summary>
/// 是否技术论坛
/// </summary>
public bool IsTech;
/// <summary>
/// 版主
/// </summary>
public string[] morderators;
/// <summary>
/// 积分归属论坛
/// </summary>
public Guid pointBelongsToForumId;

}



获得用户信息GetUserProfile :

方法定义如下:

/// <summary>
/// 获得用户信息
/// </summary>
/// <param name="identity">用户身份信息</param>
/// <param name="profile">用户信息</param>
/// <param name="error">错误信息</param>
/// <param name="username">需要获得用户信息的用户名</param>
/// <returns>操作是否成功</returns>
public bool GetUserProfile(Identity identity, string username, out UserProfile profile, out Error error)



此方法用于查询某用户的用户信息,包括用户昵称,可用分,用户技术专家分,非技术专家分,以及他在各个论坛的得分和级别(只展示用户在其有得分的论坛信息)

用户信息UserProfile的参考定义和字段含义如下

public struct UserProfile
{
/// <summary>
/// 可用分
/// </summary>
public int point;
/// <summary>
/// 技术专家分
/// </summary>
public int techExpertPoint;
/// <summary>
/// 用户在各个论坛的积分和级别信息
/// </summary>
public List<TopForum> topForums;
/// <summary>
/// 非技术专家分
/// </summary>
public int nonTechExpertPoint;
/// <summary>
/// 昵称
/// </summary>
public string nickName;
/// <summary>
/// 用户名
/// </summary>
public string username;
}

/// <summary>
/// 用户在各个论坛的积分和级别
/// </summary>
public struct TopForum{
/// <summary>
/// 论坛
/// </summary>
public Guid forumId;
/// <summary>
/// 专家分
/// </summary>
public int expertPoint;
/// <summary>
/// 星级
/// </summary>
public string rank;
}

发帖Post :

发帖方法定义如下






/**//// <summary>


/// 发帖


/// </summary>


/// <param name="identity">用户***</param>


/// <param name="post">帖子</param>


/// <param name="error">错误信息</param>


/// <param name="topicUrl">帖子链接</param>


/// <returns>发帖是否成功</returns>


public bool Post(Identity identity, Post post, out Error error, out string topicUrl)



Post结构参考定义



/// <summary>
/// 帖子
/// </summary>
public struct Post
{
/// <summary>
/// 论坛id(发帖时必须)
/// </summary>
public Guid forumId;
/// <summary>
/// 标题(发帖时必须)
/// </summary>
public string subject;
/// <summary>
/// 帖子内容(发帖时必须)
/// </summary>
public string body;
/// <summary>
/// 标签
/// </summary>
public string tag;
/// <summary>
/// 给分
/// </summary>
public int point;
/// <summary>
/// 是否问专家贴(发帖时必须)
/// </summary>
public bool isAskExpert;
/// <summary>
/// 专家用户名称(若isAskExpert,必须)
/// </summary>
public string expertUserName;
/// <summary>
/// 编辑器类型(发帖时必须),现只支持ubb类型
/// </summary>
public EditorType editor;
/// <summary>
/// 帖子链接
/// </summary>
public string url;

}

回帖Reply :






/**//// <summary>


/// 回复帖子


/// </summary>


/// <param name="identity">用户***</param>


/// <param name="reply">回复</param>


/// <param name="error">错误信息</param>


/// <param name="replyId">回复id</param>


/// <param name="layer">楼层</param>


/// <returns>回复是否成功</returns>


public bool Reply(Identity identity, Reply reply, out Error error, out long replyId, out int layer)



回复实体参考定义如下

/// <summary>
/// 回复
/// </summary>
public struct Reply
{
/// <summary>
/// 论坛id(必须)
/// </summary>
public Guid forumId;
/// <summary>
/// 帖子url(必须)
/// </summary>
public string topicUrl;
/// <summary>
/// 回复内容(必须)
/// </summary>
public string body;
/// <summary>
/// 是否需要ubb(必须)
/// </summary>
public EditorType editor;
}



结帖CheckOutTopic:

/// <summary>
/// 结贴
/// </summary>
/// <param name="identity">用户***</param>
/// <param name="topicUrl">帖子链接</param>
/// <param name="forumId">论坛id</param>
/// <param name="replyPoints">回复给分列表</param>
/// <param name="error">错误</param>
/// <returns>结贴是否成功</returns>
public bool CheckOutTopic(Identity identity, string topicUrl, Guid forumId, List<ReplyPoint> replyPoints, out Error error)



List<ReplyPoint> replyPoints为回复id和给分数组



/// <summary>
/// 回帖得分
/// </summary>
public struct ReplyPoint
{
/// <summary>
/// 回复id
/// </summary>
public long replyId;
/// <summary>
/// 得分
/// </summary>
public int point;
}

积分捐赠PointDonate

/// <summary>
/// 可用分捐赠
/// </summary>
/// <param name="identity">用户***</param>
/// <param name="toUser">捐赠对象</param>
/// <param name="point">捐赠积分</param>
/// <param name="reason">原因</param>
/// <param name="error">错误</param>
/// <returns>捐赠是否成功</returns>
public bool PointDonate(Identity identity, string toUser, int point, string reason, out Error error)



获得我的帖子,我参与的帖子,我得分的帖子,别人问我的帖子 GetTopicsOfUser

/// <summary>
/// 获得我发表的帖子,我回复过的帖子,我得分的帖子
/// </summary>
/// <param name="listType">列表类型</param>
/// <param name="forumId">论坛id</param>
/// <param name="posts">帖子列表</param>
/// <param name="error">错误信息</param>
/// <param name="identity">身份信息</param>
/// <returns>是否成功</returns>
[WebMethod]
public bool GetTopicsOfUser(Identity identity, UserTopicListType listType, Guid forumId, out List<Post> posts, out Error error)



列表类型定义如下

/// <summary>
/// 用户帖子列表类型
/// </summary>
public enum UserTopicListType
{
/// <summary>
/// 用户的帖子
/// </summary>
TopicOfUser,

/// <summary>
/// 用户回复过的帖子
/// </summary>
TopicUserJoined,

/// <summary>
/// 用户得分的帖子
/// </summary>
TopicUserRewarded,

/// <summary>
/// 所有问专家
/// </summary>
AllAskExpert
}



获得帖子列表

获得帖子列表,包括

待解决

抢分区

零回复

热点区

已解决

精华区
没有提供独立的Webservice,原因是这些帖子列表均提供了Rss,调用者通过Rss获得需要的信息

一个列子列表的rss链接由论坛别名和列表类型两部分组成

比如灌水乐园抢分区的Rss链接为http://forum.csdn.net/Rss/FreeZone/RobPointList/

其中***部分(FreeZone)为论坛别名,红色部分(RobPointList)表明列表类型为抢分区,我们可以通过如下代码简单实现取得rss并转为dataset



public DataTable GetTopicListRss()
{
string url = "http://forum.csdn.net/Rss/FreeZone/RobPointList/";
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
WebResponse response=request.GetResponse();
DataSet result = new DataSet();
Stream rssStream = response.GetResponseStream();
StreamReader sr = new StreamReader(rssStream, Utility.GetEncoding());
result.ReadXml(sr);
return result.Tables[2];
}



获得与解析帖子

公开的API也没专门获得帖子的方法,主要是处于性能的考虑,想要获得帖子,就直接获取帖子html文件,如果需要帖子的信息,比如发帖人,分数,就必须解析帖子文件,文件中提供了一系列标识(csdnid),让解析者可以通过其找到对应的内容,并且在所附demo中,也提供了一个经过改造的解析模块,调用者可以使用这个模块,通过csdnid来找到帖子文件中具体的内容

什么是csdnid?

打开任意一个帖子文件,里面都会看到一些由csdnid标识的元素,这些元素的属性和内容一般都具有特殊的意义,比如帖子源文件中的下面html代码




<meta id="topicViewUrl" csdnid="topicViewUrl" content="http://topic.csdn.net/u/20080328/15/ce3f9a96-7f91-4dea-83fb-23beffe36cb8.html">


<meta csdnid="sectionId" content="a3049f56-b572-48f5-89be-4797b70d71cd">



csdnid="topicViewUrl" 的meta元素的content属性,说明了帖子的url,为:http://topic.csdn.net/u/20080328/15/ce3f9a96-7f91-4dea-83fb-23beffe36cb8.html

而csdnid="sectionId"的meta元素的content属性,说明了帖子的论坛id为:
a3049f56-b572-48f5-89be-4797b70d71cd

而<var csdnid="topicUsername" id="topicUserName">Orange1997</var>中,此元素的innerHTML为发帖用户名

如何解析帖子文件并得到我们想要的信息

解析html文件有很多方法,这里使用使用经过改进的开源html解析其HtmlAgilityPack,Demo中有此模块,

基本使用方法

加载Html文件

下面方法可以把某个html加载进来

HtmlDocument d = new HtmlDocument();
d.Load("C:/test.html");

Load方法还有多个重载,可以从Stream,StreamReader等对象中加载html文档

加载后使用GetElementsbyCsdnId来获得指定csdnid标识的元素,比如

d.GetElementsbyCsdnId("topicBody"),获得所有用csdnid="topicBody"标识的元素

注意这里的返回值是一个元素数组,因为csdnid和id属性不同,是可以重复的;

下面的代码是demo中用于解析帖子文件的方法,详细使用请看demo源码

private InternalTopic ParseFile(StreamReader reader){
InternalTopic post = new InternalTopic();
HtmlDocument d = new HtmlDocument();
d.Load(reader);
post.body=((HtmlNode)d.GetElementsbyCsdnId("topicBody")[0]).InnerHtml;
post.forumId = new Guid(((HtmlNode)d.GetElementsbyCsdnId("sectionId")[0]).Attributes["content"].Value);
post.subject = ((HtmlNode)d.GetElementsbyCsdnId("topicSubject")[0]).InnerHtml;
post.point = int.Parse(((HtmlNode)d.GetElementsbyCsdnId("topicPoint")[0]).InnerHtml);
post.tags = ((HtmlNode)d.GetElementsbyCsdnId("keywords")[0]).Attributes["content"].Value;
post.username = ((HtmlNode)d.GetElementsbyCsdnId("topicUsername")[0]).InnerHtml;
post.postDate = DateTime.Parse(((HtmlNode)d.GetElementsbyCsdnId("topicPostDate")[0]).InnerHtml);
post.topicUrl = ((HtmlNode)d.GetElementsbyCsdnId("topicViewUrl")[0]).Attributes["content"].Value;
Guid topicId;
DateTime postDate;
if (!Utility.TryParseTopicUrl(post.topicUrl, out postDate, out topicId))
{
throw new ArgumentException("错误的帖子链接");
}
ArrayList replylist = d.GetElementsbyCsdnId("replyId");
if (replylist != null)
{
foreach (HtmlNode n in replylist)
{
long rid = long.Parse(n.Attributes["name"].Value);
post.replies.Add(ParseReply(d, rid));
}
}
string dataPath = Utility.WriteData(topicId.ToString() + ".xml", typeof(InternalTopic), post);
return post;
}
/// <summary>
/// 解析回复
/// </summary>
/// <param name="d">html文件</param>
/// <param name="rid">回复id</param>
/// <returns></returns>
private InternalReply ParseReply(HtmlDocument d,long rid)
{
HtmlNode replyTable = d.GetElementsbyCsdnId("reply_" + rid.ToString())[0] as HtmlNode;
HtmlDocument replyHtml = new HtmlDocument();
replyHtml.LoadHtml(replyTable.OuterHtml);
InternalReply reply = new InternalReply();
reply.replyId=rid;
reply.username = ((HtmlNode)replyHtml.GetElementsbyCsdnId("replyUsername")[0]).InnerHtml;
reply.replyDate = DateTime.Parse(((HtmlNode)replyHtml.GetElementsbyCsdnId("replyDate")[0]).InnerHtml);
reply.body = ((HtmlNode)replyHtml.GetElementsbyCsdnId("replyBody")[0]).InnerHtml;
reply.point = int.Parse(((HtmlNode)replyHtml.GetElementsbyCsdnId("replyPoint")[0]).InnerHtml);
reply.layer = int.Parse(((HtmlNode)replyHtml.GetElementsbyCsdnId("replyLayer")[0]).InnerHtml);
return reply;
}

常用csdnid参考

csdnid属性描述
topicPostDateInnerHTML发帖时间
topicBodyInnerHTML帖子内容
sectionIdcontent论坛id
isPrimeclass是否精华,为空则不是精华
isCheckOutInnerHTML是否已结,为空则未结
topicPointInnerHTML给分
topicUsernameInnerHTML发帖用户
descriptioncontent标题
topicViewUrlcontent帖子Url
replyIdname回复id
replyCountInnerHTML回复数
reply_{id}OuterHTML指定id的回复区域
replyUsernameInnerHTML回复用户名
replyNicknameInnerHTML回复用户昵称
replyDateInnerHTML回复日期
replyLayerInnerHTML回复楼层
replyPointInnerHTML回复得分
replyBodyInnerHTML回复内容

错误ID

错误id由四位二进制数表示,前两位为功能号,后两位为具体错误号,比如错误0301,表明为回复功能的内容为空错误

功能号列表

00:通用

01:结贴

02:发帖

03:回复

04:积分捐赠

每个功能号的具体错误,将另行文档说明
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐