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 |
属性 |
描述 |
topicPostDate |
InnerHTML |
发帖时间 |
topicBody |
InnerHTML |
帖子内容 |
sectionId |
content |
论坛id |
isPrime |
class |
是否精华,为空则不是精华 |
isCheckOut |
InnerHTML |
是否已结,为空则未结 |
topicPoint |
InnerHTML |
给分 |
topicUsername |
InnerHTML |
发帖用户 |
description |
content |
标题 |
topicViewUrl |
content |
帖子Url |
replyId |
name |
回复id |
replyCount |
InnerHTML |
回复数 |
reply_{id} |
OuterHTML |
指定id的回复区域 |
replyUsername |
InnerHTML |
回复用户名 |
replyNickname |
InnerHTML |
回复用户昵称 |
replyDate |
InnerHTML |
回复日期 |
replyLayer |
InnerHTML |
回复楼层 |
replyPoint |
InnerHTML |
回复得分 |
replyBody |
InnerHTML |
回复内容 |
错误ID
错误id由四位二进制数表示,前两位为功能号,后两位为具体错误号,比如错误0301,表明为回复功能的内容为空错误
功能号列表
00:通用
01:结贴
02:发帖
03:回复
04:积分捐赠
每个功能号的具体错误,将另行文档说明
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=2226087