`
re_reference
  • 浏览: 236764 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

解析搜狗音乐的音乐下载地址

阅读更多
//
//  SougouMusicParser.h
//
//  Created by scott.8an@gmail.com on 12-3-13.
//  Copyright (c) 2012年 littleworn llc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "ASIHTTPRequest.h"
#import "ASINetworkQueue.h"
#import "TFHpple.h"
#import "XPathQuery.h"
#import "TFHppleElement.h"
#import "DataType.h"

//页面地址
#define kRequestURL(s_songName,i_pageNumber) \
        [NSString stringWithFormat:@"http://mp3.sogou.com/music.so?pf=mp3&query=%@&page=%i&w=02009900&dr=1",s_songName,i_pageNumber]

@interface SogouMusicParser : NSObject

+ (SogouMusicParser*)shareInstance;

/**
 发送成功通知的数组结构:
 [
    {
        "song"="save me",
        "artist"="Queen",
        "size"="4.38M",
        "firstChoose_url"="http://ziyuan1.myi.cn/film/YINYUE/fir/firT1563.mp3",
        "other_link"= [
            "http://mul1.tximg.cn/music/group/bbs/mp3/7/090528/1243462409315.mp3",
            "http://api.ning.com/files/SRIkTa2lzwqL1jt26257Yzj3TCFZ6jAJD2HaYhYB*N469JhRrd01xDttiHqjJD7K9WkWr69u6LnXChFfA2Wid0jWUfiIB33m/f.i.r._.mp3"
        ]
    },...
 ]
 **/
- (void)runToGetSongInfoFromSogouWithKeyword:(NSString*)kw pageNumber:(NSInteger)pgNum;
@end



//
//  SougouMusicParser.m
//
//  Created by scott.8an@gmail.com on 12-3-13.
//  Copyright (c) 2012年 littleworn llc. All rights reserved.
//

#import "SogouMusicParser.h"

NSString *const SogouMusicParseSuccessNotification = @"SogouMusicParseSuccessNotification";
NSString *const SogouMusicParseFailedNotification = @"SogouMusicParseFailedNotification";

static SogouMusicParser *parser_ = nil;

@interface SogouMusicParser (Private)
//获得跳转地址的urls
/**
 [
 "http://mp3.sogou.com/down.so?gid=11950F9C68D7EDE7&globalId=1f940cf0056326c8&query=%CE%D2%C",
 "http://mp3.sogou.com/down.so?gid=11950F9C68D7EDE7&globalId=1f940cf0056326c8&query=%CE%D2%C",
 ...
 ]
 **/
- (NSArray*)getJumpToDownloadPageURLsWithKeyword:(NSString*)kw pageNumber:(int)pgNum;

/**
 返回字典结构:
 {
 "song"="save me",
 "artist"="Queen",
 "size"="4.38M",
 "firstChoose_url"="http://ziyuan1.myi.cn/film/YINYUE/fir/firT1563.mp3",
 "other_link"= [
 "http://mul1.tximg.cn/music/group/bbs/mp3/7/090528/1243462409315.mp3",
 "http://api.ning.com/files/SRIkTa2lzwqL1jt26257Yzj3TCFZ6jAJD2HaYhYB*N469JhRrd01xDttiHqjJD7K9WkWr69u6LnXChFfA2Wid0jWUfiIB33m/f.i.r._.mp3"
 ]
 }
 **/
- (NSDictionary*)getMusicInfoByJumpToUrl:(NSString*)jumpToUrl;
@end


@implementation SogouMusicParser

+ (SogouMusicParser*)shareInstance{
    if (!parser_) {
        parser_ = [[self alloc] init];
    }
    return parser_;
}

- (NSArray*)getJumpToDownloadPageURLsWithKeyword:(NSString*)kw pageNumber:(int)pgNum{
    if (kw && [kw length]) {
        //初始化需要返回的数组
        NSMutableArray *urlsArr = [NSMutableArray arrayWithCapacity:0];
        
        //去掉搜索条件两端空格
        NSString *pureKW = [kw stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        
        //用 + 号连接需要要搜索的绑定条件
        NSArray *kwArr = [pureKW componentsSeparatedByString:@" "];
        NSMutableString *neededKw = [NSMutableString stringWithCapacity:0];
        if (kwArr && [kwArr count]) {
            for (NSString *str in kwArr) {
                [neededKw appendFormat:@"%@+",str];
            }
            
            //去掉最后一个 + 号
            [neededKw deleteCharactersInRange:NSMakeRange([neededKw length]-1, 1)];
        }else{
            neededKw = (NSMutableString*)kw;
        }
        //对url编码
        NSString *requestURLStr = kRequestURL(neededKw,pgNum);
        NSString *requestURLStrEnc = [requestURLStr stringByAddingPercentEscapesUsingEncoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)];
    
        //请求页面地址
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:requestURLStrEnc]];
        [request startSynchronous];
        
        //获得返回数据
        //NSString *responseStr = [request responseString];
        NSData  *responseData = [request responseData];
        //NSLog(@"%@",responseStr);
        
        if (responseData) {
            TFHpple *help = [TFHpple hppleWithHTMLData:responseData];
            if (help) {
                //用xpath解析获得歌曲的跳转地址
                //获得tbody节点
                NSString *xPath = @"//*[@id='songlist']";
                TFHppleElement *tableNode = [help peekAtSearchWithXPathQuery:xPath];
                TFHppleElement *tBodyNode = tableNode.firstChild;
                if (tBodyNode) {
                    NSMutableArray *childrenNodes = (NSMutableArray*)tBodyNode.children;
                    if (childrenNodes && [childrenNodes count]) {
                        //移除第一个node
                        [childrenNodes removeObjectAtIndex:0];
                        for (TFHppleElement *element in childrenNodes) {
                            NSArray *tdNodeArr = element.children;
                            if (tdNodeArr && [tdNodeArr count]) {
                                for (TFHppleElement *tdEle in tdNodeArr) {
                                    TFHppleElement *aNode = tdEle.firstChild;
                                    if (aNode && [[aNode objectForKey:@"action"] isEqualToString:@"down"]) {
                                        /**
                                         window.open('/down.so?gid=1A6F5F0BA3CDD9C8&globalId=137c9db3dc0bf7bf&query=%B2%BB%C3%F0%B5%C4%B0%AE&tgid=629801e642a2eadc&pf=mp3&s=%CC%B7%D3%BD%F7%EB&t=%B2%BB%C3%F0%B5%C4%B0%AE&size=4195343&ac=0&c','
                                         ','width=431,height=495,scrollbars=no');
                                         uigsPB('consume=music_down&music_down=28');return(false);
                                         */
                                        NSString *urlThatMixed = [aNode objectForKey:@"onclick"];
                                        NSArray *urlParts = [urlThatMixed componentsSeparatedByString:@"'"];
                                        //取第二个作为uri
                                        NSString *uri = [urlParts objectAtIndex:1];
                                        
                                        if (uri && [uri length]) {
                                            NSString *url = [NSString stringWithFormat:@"http://mp3.sogou.com%@",uri];
                                            //NSLog(@"组合新的跳转地址:http://mp3.sogou.com%@",uri);
                                            
                                            [urlsArr addObject:url];
                                        }
                                    }
                                }
                            }
                        }
                        if ([urlsArr count]) {
                            return urlsArr;
                        }
                    }
                }
            }
        }else{
            //再次尝试
            [self getJumpToDownloadPageURLsWithKeyword:kw pageNumber:pgNum];
        }
    }
    return nil;
}

- (NSDictionary*)getMusicInfoByJumpToUrl:(NSString*)jumpToUrl{
    if (jumpToUrl && [jumpToUrl length]) {
        //获得下载页面,不需要编码,因为获得的数据已经是编码过了的
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:jumpToUrl]];
        [request startSynchronous];
        
        //获得返回界面
        NSString *responseStr = [request responseString];
        NSData *responseData = [request responseData];
      // NSLog(@"返回下载界面的网页信息:%@",responseStr);
        
        if (responseData) {
            NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:0];
            
            //解析页面
            TFHpple *help = [TFHpple hppleWithHTMLData:responseData];
            
            /**
             1.查找歌曲的下载地址
             **/
            
            //href="..+.mp3"
            NSString *pattern = @"href=\"..+.mp3\"";
            NSRegularExpression *exp = [NSRegularExpression regularExpressionWithPattern:pattern
                                                                                 options:NSRegularExpressionCaseInsensitive
                                                                                   error:nil];
            NSArray *metchedArr = [exp matchesInString:responseStr options:NSMatchingReportProgress range:NSMakeRange(0, [responseStr length])];
        
            if (metchedArr && [metchedArr count]) {
                //用数组保存其他连接
                NSMutableArray *otherLinksArr = [NSMutableArray arrayWithCapacity:0];
                for (NSTextCheckingResult *result in metchedArr) {
                    NSRange range = result.range;
                    NSString *urlWithHREF = [responseStr substringWithRange:range];
                    if (urlWithHREF && [urlWithHREF length]) {
                        NSArray *urlParts = [urlWithHREF componentsSeparatedByString:@"\""];
                        if (urlParts && [urlParts count]>2) {
                            NSString *musicURL = [urlParts objectAtIndex:1];
                            
                            //把第一个获得的地址存到首选里去
                            NSString *songURL = [dictionary objectForKey:@"firstChoose_url"];
                            if (!songURL || [songURL length]<1) {
                                [dictionary setObject:musicURL forKey:@"firstChoose_url"];
                            }else{
                                //去重
                                if (![musicURL isEqualToString:songURL]) {
                                    //用数组保存其他链接
                                    [otherLinksArr addObject:musicURL];
                                }
                            }
                        }
                    }
                }
                
                //保存其他链接到字典
                [dictionary setObject:otherLinksArr forKey:@"other_link"];
            }
            
            //如果下载地址解析失败,一下内容都没有必要存在
            NSString *firstChoose_url = [dictionary objectForKey:@"firstChoose_url"];
            if (firstChoose_url && [firstChoose_url length]) {
                /**
                 2.查找歌曲的名称
                 **/
                NSString *xPath = @"//*[@class=\"info1\"]";
                TFHppleElement *info1Node = [help peekAtSearchWithXPathQuery:xPath];
                TFHppleElement *aNode = info1Node.firstChild;
                NSString *songName = [aNode objectForKey:@"title"];
                if (songName && [songName length]) {
                    [dictionary setObject:songName forKey:@"song"];
                }else{
                    [dictionary setObject:@"Unknown" forKey:@"song"];
                }
                
                
                /**
                 3.查找艺术家
                 **/
                NSArray *info1Children = info1Node.children;
                if (info1Children && [info1Children count]) {
                    BOOL isArtistTag = NO;
                    for (TFHppleElement *node in info1Children) {
                        if ([node.tagName isEqualToString:@"a"] && isArtistTag) {
                            NSString *artist = [node objectForKey:@"title"];
                            if (artist && [artist length]) {
                                [dictionary setObject:artist forKey:@"artist"];
                                isArtistTag =NO;
                            }
                        }
                        isArtistTag = YES;
                    }
                }
                NSString *hasArtist = [dictionary objectForKey:@"artist"];
                if (!hasArtist || [hasArtist length]<1) {
                    [dictionary setObject:@"Unknown" forKey:@"artist"];
                }
                
                /**
                 4.查找歌曲的大小
                 **/
                xPath = @"//*[@class=\"info2\"]";
                TFHppleElement *info2Node = [help peekAtSearchWithXPathQuery:xPath];
                NSArray *children = info2Node.children;
                if (children && [children count]) {
                    for (TFHppleElement *node in children) {
                        if ([node.tagName isEqualToString:@"strong"]) {
                            NSString *sizeDesc = node.content;
                            if (sizeDesc && [sizeDesc length]) {
                                [dictionary setObject:sizeDesc forKey:@"size"];
                            }
                        }
                    }
                }
                NSString *hasSize = [dictionary objectForKey:@"size"];
                if (!hasSize || [hasSize length]<1) {
                    [dictionary setObject:@"Unknown" forKey:@"size"];
                }
                
                //NSLog(@"***************歌曲信息:%@",dictionary);
                
                //返回字典
                return dictionary;
            }
            return nil;
        }else{
            //重新请求,直到获取成功
            [self getMusicInfoByJumpToUrl:jumpToUrl];
        }
    }
    return nil;
}

- (void)runToGetSongInfoFromSogouWithKeyword:(NSString*)kw pageNumber:(NSInteger)pgNum{
    if (kw && [kw length]) {
        NSMutableArray *songsInfoArr = [NSMutableArray arrayWithCapacity:0];
        
        NSArray *songsURLsArr = [self getJumpToDownloadPageURLsWithKeyword:kw pageNumber:pgNum];
        
        //如果第一次请求失败,再次请求
        if (!songsInfoArr || [songsInfoArr count]<1) {
            songsURLsArr = [self getJumpToDownloadPageURLsWithKeyword:kw pageNumber:pgNum];
        }
        //如果第二次请求失败,再次请求
        if (!songsInfoArr || [songsInfoArr count]<1) {
            songsURLsArr = [self getJumpToDownloadPageURLsWithKeyword:kw pageNumber:pgNum];
        }
        
        if (songsURLsArr && [songsURLsArr count]) {
            for (NSString *url in songsURLsArr) {
                NSDictionary *songInfo = [self getMusicInfoByJumpToUrl:url];
                if (songInfo && [songInfo count]) {
                    [songsInfoArr addObject:songInfo];
                }
            }
        }
        
        if ([songsInfoArr count]) {
            [[NSNotificationCenter defaultCenter] postNotificationName:SogouMusicParseSuccessNotification
                                                                object:nil userInfo:[NSDictionary dictionaryWithObject:songsInfoArr forKey:@"songsInfo"]];
        }else{
            [[NSNotificationCenter defaultCenter] postNotificationName:SogouMusicParseFailedNotification
                                                                object:nil userInfo:[NSDictionary dictionaryWithObject:@"This page does not exist" 
                                                                                                                forKey:@"msg"]];
        }
    }else{
        [[NSNotificationCenter defaultCenter] postNotificationName:SogouMusicParseFailedNotification
                                                            object:nil userInfo:[NSDictionary dictionaryWithObject:@"Check what you've been input" 
                                                                                                            forKey:@"msg"]];
    }
}
@end

分享到:
评论

相关推荐

    易语言搜狗音乐盒

    《易语言搜狗音乐盒》是一款基于易语言开发的音乐播放软件,它的核心功能是提供搜狗音乐的在线播放和管理服务。通过源代码的学习,我们可以深入理解如何使用易语言来构建类似的多媒体应用,这对于易语言编程爱好者和...

    易语言搜狗音乐盒源码

    在本案例中,我们关注的是一个名为“易语言搜狗音乐盒源码”的项目,它是一个使用易语言编写的音乐播放器,特别针对搜狗音乐平台进行了优化。这个播放器具有基础功能,如播放MP3格式的音频文件,同时提供了额外的...

    搜狗输入法

    《搜狗输入法:最佳中文输入工具的深度解析》 搜狗输入法,被誉为“最好用的输入法”,自发布以来便深受广大用户的喜爱。它以其高效、智能、易用的特点,极大地提升了用户在日常生活和工作中输入汉字的效率。本文将...

    搜狗输入法动画皮肤

    在搜狗输入法的动画皮肤中,可能会有与法国作家雨果的经典小说《悲惨世界》相关的元素,比如主角Cosette、Javert或者著名的音乐剧曲目,这些都可以被融入到皮肤的设计中,带给用户一种文学与艺术的结合体验。...

    搜狗搜狐搜索业务部sogouproduct.ppt

    以下是详细的解析: **搜狗搜索功能**: 1. **网页搜索**:搜狗的网页搜索具有全面性(10亿网页)、快速更新(10分钟内更新)、高速响应(0.1秒内返回结果)和准确性(上下文分析技术)的特点。 2. **智能搜索提示*...

    WPF音乐播放器

    开发者可能使用了网络请求技术,比如HTTP协议,从搜狗音乐平台获取歌曲资源。这涉及到Web API的调用,可能包括发送GET或POST请求,解析返回的JSON或XML数据,然后将搜索结果展示在播放器的界面上。对于初学者来说,...

    【discuz!X1.5】荷塘音乐插件v1.1_GBK

    3. **音乐搜索与播放**:插件提供的搜索功能可以让用户在论坛内直接查找并播放音乐,这涉及到API接口的调用以及音乐数据的解析和展示。 4. **用户体验**:考虑到论坛用户的使用习惯,插件应该有一个简洁易用的界面...

    duilib实现仿酷狗播放器

    6. **歌词显示**:酷狗播放器有同步歌词显示,这需要解析LRC文件并配合音乐播放同步更新。 7. **其他功能**:如播放模式设置(单曲循环、列表循环等)、音效设置、下载管理等也是播放器常见的功能模块。 总的来说...

    搜狗2014 产品岗.pdf

    【搜狗2014产品岗笔试题解析】 在2014年搜狗的产品岗位校招笔试中,考察了应聘者对互联网产品基础知识、技术趋势以及设计工具的理解。以下是根据题目内容提炼的相关知识点: 一、选择题 1. 数据统计中的PV(Page ...

    哈佛.音乐&MTV.PHP搜索引擎

    用户不仅可以进行搜索,还能直接下载找到的资源,这为音乐和MTV爱好者提供了极大的便利。值得注意的是,这个系统对服务器环境的要求相对较低,只需要PHP空间即可运行,无需依赖MySQL数据库,降低了部署的复杂性和...

    搜狗内部培训 桌面组培训文档系列之五_测试用例设计方法.

    ### 搜狗内部培训桌面组测试用例设计方法解析 #### 测试用例设计方法概览 测试用例设计是软件测试过程中的一项核心任务,它旨在确保软件产品的质量和稳定性。搜狗内部培训桌面组的测试用例设计方法,主要聚焦于...

    在c#中我们如何实现歌词的显示

    这里使用的是搜狗音乐API。 - 将歌曲名称编码为URL安全的格式,以便在网络请求中正确传递。 - 构造完整的URL字符串,用于发起HTTP请求。 #### 下载歌词文件 - 使用`WebClient`类来下载歌词文件。`WebClient`是.NET ...

    sogua小偷 v1.0 修改版

    这可能意味着软件具备了从百度音乐或其他与百度相关的服务中抓取和显示歌词的能力,为用户提供便捷的歌词查看体验。同时,这也暗示搜狗小偷可能并非搜狗官方产品,而是由第三方开发,用于整合或提取不同平台资源的...

    贝壳全能(智能)搜索引擎.rar

    "贝壳全能(智能)搜索引擎"是一款综合性的搜索引擎工具,它整合了多个知名搜索引擎的功能,包括百度、谷歌、雅虎、有道、搜狗以及迅雷等,为用户提供了一站式的搜索体验。用户无需在各个搜索引擎之间切换,只需通过此...

    2010腾讯校园招聘产品策划运营类职位笔试题和参考答案.docx

    - 酷我音乐盒:一款集合音乐播放、下载、搜索等功能的数字音乐服务平台。 - QuickTime:苹果公司开发的多媒体框架,用于处理音频和视频内容。 - TheWorld影音播放器:中国的一款多格式视频播放软件,支持多种媒体...

    产品经理面试题.docx

    - 搜狗音乐 - 九天音乐网 - 酷狗音乐盒 **题目4**: 请写出你所知道的所有音频文件的格式(扩展名)。 - .CD - OGG - MP3 - ASF.WMA - WAV - MP3PRO - RM - REAL - APE - MODULE - MIDI - VQF #### 四、产品规划...

    从程序员到CTO大牛企业内部PDF与PPT合集.zip

    QQ音乐的个性化探索 京东亿级流量海量数据搜索架构 C2C市场中推荐系统的挑战与机遇 阿里网络故障智能化治理 腾讯全用户态服务开发套件F-Stack 苏宁易购全站HTTPS实践之路 金山云H.265在视频直播场景下的应用演进 ...

    常用搜索引擎搜索串

    - 搜狗音乐:`http://d.sogou.com/music.so?query=%s` - 雅虎音乐:`http://music.yahoo.com.cn/search?p=%s&pid=ysearch&mimetype=all&source=ysearch_music_result_topsearch&Submit=%CB%D1%B8%E8%C7%FA` - Wo99...

    前端 45 道面试题及答案.docx

    JS引擎则负责解析和执行javascript来实现网页的动态效果。 常见的浏览器内核有: 1. Trident内核:IE,360,傲游,搜狗,世界之窗,腾讯等 2. Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等 3. Presto...

    3G WAP 网址大全

    3. **搜狗WAP** (`http://wap.sogou.com/`):搜狗的WAP搜索引擎,除了基本的网页搜索,还提供了地图、新闻、音乐等服务。 4. **3721WAP** (`http://wap.3721.com/`):3721曾是中国领先的网络搜索和导航服务商之一,...

Global site tag (gtag.js) - Google Analytics