本帖最后由 秋声赋 于 2012-6-10 14:44 编辑
简单易懂的现代魔法:C#编程教程(七)Bilibili小工具② 下载地址的解析这次先说视频下载地址的解析.
关于下载地址的获取,我用的方法主要参考了http://9ch.co/t13951,1-1.html这个帖子,当然还是有区别的,毕竟我们是要用程序获取,而不是手动获取.先说明一点,只支持新浪,优酷和土豆,因为我找不到其他的视频源了,毕竟是演示作用…本帖主要会用到AddressResolution和 BilibiliVideo类,当用到相应函数的代码,我也会发出来,详细的请自己看源代码.整体思路是这样的先用BilibiliVideo .GetVideoObject函数开始构造BilibiliVideo对象,代码如下[mw_shl_code=csharp,true] /// *本站禁止HTML标签噢*
/// 用来创建Video对象
/// </summary>
/// <param name="VideoUri">视频地址</param>
/// *本站禁止HTML标签噢* </returns>
public static BilibiliVideo GetVideoObject(string VideoUri)
{
return GetVideoObject(AddressResolution.GetVideoAid(VideoUri).Item1,
AddressResolution.GetVideoAid(VideoUri).Item2);
}
/// *本站禁止HTML标签噢*
/// 用来创建Video对象
/// </summary>
/// <param name="aid">视频AID号,无av</param>
/// <param name="page">视频所在页数</param>
/// *本站禁止HTML标签噢* BilibiliVideo对象</returns>
public static BilibiliVideo GetVideoObject(string aid, int page = 1)
{
//http://api.bilibili.tv/view?type=xml&appkey=03fc8eb101b091fb&id=270656&page=1
const string appkey="03fc8eb101b091fb";
string api = string.Format("http://api.bilibili.tv/view?type={0}&appkey={1}&id={2}&page={3}", "xml", appkey, aid, page.ToString());
WebClient wc = new WebClient();
byte[] result = wc.DownloadData(api);
MemoryStream ms = new MemoryStream(result);
XmlTextReader xr = new XmlTextReader(ms);
xr.ReadStartElement("info");
BilibiliVideo vd = new BilibiliVideo();
vd.index = page;
while (xr.Read())
{
switch (xr.Name)
{
case "play": vd.play = int.Parse(xr.ReadString()); break;
case "review": vd.revie = int.Parse(xr.ReadString()); break;
case "video_review": vd.video_review = int.Parse(xr.ReadString()); break;
case "favorites": vd.favorites = int.Parse(xr.ReadString()); break;
case "title": vd.title = xr.ReadString(); break;
case "description": vd.description = xr.ReadString(); break;
case "tag": vd.tag = xr.ReadString(); break;
case "pic": vd.pic = xr.ReadString(); break;
case "pages": vd.pages = int.Parse(xr.ReadString()); break;
case "from": vd.from = xr.ReadString(); break;
case "author": vd.author = xr.ReadString(); break;
case "mid": vd.mid = xr.ReadString(); break;
case "vid": vd.vid = xr.ReadString(); break;
case "offsite": vd.offsite = xr.ReadString(); break;
default: break;
}
}
return vd;
}[/mw_shl_code]
因为正常情况下,传进来的是视频的地址,所以先会获取视频的Aid号和所在页数||=========================================================
通过AddressResolution.GetVideoAid函数解析视频的Aid号和所在页数,[mw_shl_code=csharp,true] /// *本站禁止HTML标签噢*
/// 获取视频的Aid
/// </summary>
/// <param name="uri">视频的完整地址</param>
/// *本站禁止HTML标签噢* 视频的AID号,视频所在页数</returns>
public static Tuple<string, int> GetVideoAid(string uri)
{
//http://www.bilibili.tv/video/av280915/ index_3.html
string Aid = string.Empty; ;
int Page = 1;
if (uri.Contains("http") | uri.Contains("bilibili"))
{
string[] uriGroup = uri.Split('/');
foreach (var s in uriGroup)
{
if (s.Contains("av"))
{
Aid = s.Substring(2);
}
if (s.Contains("index_"))
{
Page = int.Parse(s.Substring(s.IndexOf('_'), s.IndexOf('.') - s.IndexOf('_')));
}
}
}
else
{
if (uri.Contains("av"))
{
Aid = uri.Substring(2);
}
else
{
Aid = uri;
}
}
try
{
if (int.Parse(Aid) <= 0 | Aid == string.Empty)
{
throw new Exception();
}
}
catch
{
return null;
}
return new Tuple<string, int>(Aid,Page);
}[/mw_shl_code]
其中Tuple<string,int>是二元组,就是包括一个string和一个int的变量,前面代表了Aid号,后面的代表了视频所在页数.整体来说,这段代码应该很容易理解,用户的输入正常情况下就2种一种类似 http://www.bilibili.tv/video/av280915/ 和http://www.bilibili.tv/video/av280915/ index_3.html,一种类似 av280915这样,这2种很容易区分开来,只要判断字符串是否含有”http”或者” bilibili”就行了.if (uri.Contains("http") | uri.Contains("bilibili")) 完成了这项工作.string[] uriGroup =uri.Split('/'); 的作用是以’/’位分隔符将字符串分解成一个字符串数组.我们只要检测每个字符串就能获得Aid号和Page了.应该很好懂,我就不详细说了.||===============================================
然后返回到BilibiliVideo .GetVideoObject函数中来.这里就会用到B站公布给我们的API中的读取视频信息API,B站的文档给出的解释如下:[mw_shl_code=xml,true]http://api.bilibili.tv/view?type=<类型>&appkey= *本站禁止HTML标签噢* &id= *本站禁止HTML标签噢* &page=<页数>
参数:
type: 可选xml/json
id: aid
page: page
appkey 输入获得的APPKey
返回内容:
play 播放次数
review 评论数
video_review 弹幕数
favorites 收藏
title 标题
description 描述
tag 关键字
pic 封面
pages 页数
from 视频来源
author 作者
mid 作者mid
vid 视频源编码
offsite Flash播放调用地址(如果沒有则此视频无法在站外播放)
[/mw_shl_code]
其中appkey是要向B站申请的,代码中那个是我的,你们有兴趣的话可以自己去申请个,很简单, 在http://www.bilibili.tv/html/help.html申请.关于WebAPI的使用,其实就是填好相应参数后,直接访问那个地址就行了,是一个xml文件(我们选的)大概是这样子:[mw_shl_code=xml,true] *本站禁止HTML标签噢*
*本站禁止HTML标签噢* 15881</play>
*本站禁止HTML标签噢* 54</review>
*本站禁止HTML标签噢* 679</video_review>
*本站禁止HTML标签噢* 308</favorites>
*本站禁止HTML标签噢* 【发呆用】良曲10首——请把那眼泪变成温柔的回忆</title>
*本站禁止HTML标签噢*
渣浪 发呆用、作业用、游戏用都可以,up再次精心挑选的图片和歌曲,附带字幕,觉得可以的话记得顶一个啊,能全程盯着视频发呆的将成为超越up主的存在
</description>
*本站禁止HTML标签噢* 发呆、作业、游戏用,好听</tag>
*本站禁止HTML标签噢*
http://i0.loli.my/user/3016/301631/133751612893b754d500338c25.jpg
</pic>
*本站禁止HTML标签噢* 欣の迷失</author>
*本站禁止HTML标签噢* 301631</mid>
*本站禁止HTML标签噢* 1</pages>
<partname/>
*本站禁止HTML标签噢* sina</from>
*本站禁止HTML标签噢* 77392481</vid>
*本站禁止HTML标签噢*
http://static.loli.my/miniloader.swf?aid=270656&page=1
</offsite>
</info>
[/mw_shl_code]
当然我们肯定不能用浏览器访问再手动把获得的信息填到对象中,所以我们要把这个xml文件下载下来,这里就要用到WebClient类了,使用起来非常简单,直接给他个uri就行了,他会自动帮我们下载,并返回一个byte数组,当然也可以直接返回字符串,但是字符串可能会有编码问题,所以这里没有用.然后是xml文件的解析,这里用到XmlTextReader,简单的讲下,先用ReadStartElement函数设定开始读取的元素,这里是到”info”,再用Read函数一个一个的读取,并判断元素的Name属性,并用ReadString函数获取元素的值填入相应的BilibiliVideo对象属性中.到这里创建BilibiliVideo对象的工作就完成了.||============================================
然后主线程会直接调用AddressResolution. ResolutionVideo函数,用来获取视频的下载地址列表,[mw_shl_code=csharp,true] /// *本站禁止HTML标签噢*
/// 解析视频下载地址
/// </summary>
/// <param name="vd">所需解析的视频</param>
/// *本站禁止HTML标签噢* 视频的下载地址列表</returns>
public static string[] ResolutionVideo(BilibiliVideo vd)
{
string[] lstResult = null;
switch (vd.from)
{
case "youku": lstResult = GetVideoSource_Youku(vd); break;
case "sina": lstResult = GetVideoSource_Sina(vd); break;
case "tudou": lstResult = GetVideoSource_Tudou(vd); break;
default: break;
}
return lstResult;
}
[/mw_shl_code]
因为每个视频源的获取地址的方式都不一样,所以先判断是哪个视频源,幸运的是,在刚刚B站返回给我们的信息中有视频源以及视频源所对应的vid号这里只分新浪,优酷,土豆三种,其他的懒得做了.如果看了上面我给的网址,应该就很好理解了,我们分开来说.先说新浪[mw_shl_code=csharp,true]public static string[] GetVideoSource_Sina(BilibiliVideo vd)
{
List *本站禁止HTML标签噢* result=new List *本站禁止HTML标签噢* ();
WebClient wc = new WebClient();
string SinaUrl = string.Format(@"http://v.iask.com/v_play.php?vid={0}",vd.vid);
byte[] askbyte = wc.DownloadData(SinaUrl);
string[] askresults = Encoding.UTF8.GetString(askbyte).Split('\n');
foreach (var s in askresults)
{
if (s.IndexOf(@"http://") >= 0)
{
var a = s.IndexOf(@"http://");
var b = s.IndexOf(@"]]><");
var uri = s.Substring(s.IndexOf(@"http://"), s.IndexOf(@"]]><") - s.IndexOf(@"http://"));
result.Add(uri);
}
}
return result.ToArray();
}[/mw_shl_code]
新浪的很简单,只要按照http://v.iask.com/v_play.php?vid= <vid> 这种格式去访问就能获得下载地址的xml文件,这个xml有点长我就不发出来了,大家可以去http://v.iask.com/v_play.php?vid=77392481这里看看这里面形如:<![CDATA[http://flv.sina.lxdns.com/201206101347/d41040c2e78319b08ac7e0c044f0e8a6/77392529.hlv]]>就是下载地址了,这里用xml文件解析也许有些麻烦,我们直接通过字符串处理来获取下载地址.先下载下来,再用Encoding.UTF8.GetString转换成字符串,分成一行一行的,注意,上面那句话只是一行不是多行.我们发现只有下载地址中会包含” http:// “这样的字符,我们以这个为依据搜索每行,只要有这个就是我们需要的下载地址,然后通过处理获得正确格式的下载地址.然后的优酷和土豆
优酷和土豆的稍微麻烦点,要通过www.flvcd.com来获取,这个网站可以输入优酷或者土豆的地址就直接获得相应的下载地址,当然不是返回xml文件的,所以我们要通过解析网页的源代码获得这些下载地址.[mw_shl_code=csharp,true] public static string[] GetVideoSource_Youku(BilibiliVideo vd)
{
List *本站禁止HTML标签噢* result=new List *本站禁止HTML标签噢* ();
WebClient wc = new WebClient();
string YoukuUrl = string.Format(@"http://v.youku.com/v_show/id_{0}.html", vd.vid);
string AskUrl = @"http://www.flvcd.com/parse.php?kw=" + YoukuUrl + "&format=super";
byte[] askbyte = wc.DownloadData(AskUrl);
string pattern = "<a href *= *\\\"(http://f\\.youku\\.com/player/getFlvPath/[^\\\"]+)";
MatchCollection mas = Regex.Matches(Encoding.UTF8.GetString(askbyte), pattern);
foreach (Match ss in mas)
{
string te = ss.Value;
int i = te.IndexOf('"');
te = te.Substring(i + 1);
result.Add(te);
}
return result.ToArray();
}[/mw_shl_code]
下载网页什么的我就不重复说了.很幸运的是我在网上找到了关于这个网页抓取优酷地址的正则表达式,我并不会正则表达式…但是C#里面用起来还是很方便的, 调用Regex.Matches就行了,相信大家看代码都能看懂.然后是土豆的[mw_shl_code=csharp,true] public static string[] GetVideoSource_Tudou(BilibiliVideo vd)
{
List *本站禁止HTML标签噢* result = new List *本站禁止HTML标签噢* ();
WebClient wc = new WebClient();
string TudouUrl = string.Format(@"http://www.tudou.com/programs/view/{0}/", vd.vid);
string AskUrl = @"http://www.flvcd.com/parse.php?kw=" + TudouUrl + "&format=super";
byte[] askbyte = wc.DownloadData(AskUrl);
string[] askresults = Encoding.UTF8.GetString(askbyte).Split('\n');
foreach (var s in askresults)
{
if (s.IndexOf(@" *本站禁止HTML标签噢* http") >= 0)
{
var uri = s.Substring(s.IndexOf("http"));
result.Add(uri);
}
}
return result.ToArray();
}[/mw_shl_code]
因为没有现成的正则表达式,我也不会写,所以我们先分析下网页的源文件.会发现所有的下载地址前面是一部分都是” *本站禁止HTML标签噢* http”这样的,我们就用这个搜索每行字符串,就能抽取相应的地址了.||===================================================
最后将获得的下载地址列表保存到BilibiliVideo对象的DownAdressLst属性中.
并更新界面的对应的DataGrid控件dg_DownAdress
[mw_shl_code=csharp,true] this.dg_DownAdress.ItemsSource =((BilibiliVideo)sender2).DownAdressLst.Select(x => new { 下载地址 = x }).ToList();[/mw_shl_code]
这里说下不要直接把String[]绑定到DataGrid的ItemsSource上,那样只会显示字符串的长度
