免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2606 | 回复: 0
打印 上一主题 下一主题

怎样利用CSDN论坛公开的API实现自己的论坛工具 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-30 15:31 |只看该作者 |倒序浏览
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;
        }
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP