[Linux C]百度音乐API实现在线搜歌

最近在做科大讯飞的语音解析模块,主要用于语音控制播放。采集语音输入后,送给科大讯飞语音的SDK,云服务器返回JSON的数据,再解析拿到URL地址,最后送给播放器去播放。不知是否是尚未上线的product,申请的appid,解析JSON后拿到的URL地址,歌曲播放的时间都很短,一般不到1分钟。

一番网络搜索后,据说百度有个未公开的搜歌API,只要拿到歌手和歌曲名,就可以传给这个URL,然后百度就会回你一个XML文件,解析这个文件,就可以拿到你要的MP3播放地址了,那就开始干活吧。

百度音乐搜索API实现说明

搜歌API: http://box.zhangmen.baidu.com/x?op=12&count=1&title=
在上面这个地址后面加上要搜索的歌手和歌曲名,如下图
这里写图片描述
至于这里的歌手和歌曲名,都是由科大讯飞的SDK返回的JSON数据,解析出来的,此处不需要关心。

在浏览器的地址栏上输入上面的地址,就会返回如下图的内容:
[Linux C]百度音乐API实现在线搜歌_第1张图片
然后将红色框框的1和2拼凑在一起,就得到了mp3的地址,即
http://zhangmenshiting.baidu.com/data2/music/88329745/88329745.mp3?xcode=1c4dab84d5d0d5dc44a2e9dde28d95e5117565b2b6f412d5&mid=0.00592347543617

具体流程图如下图:

[Linux C]百度音乐API实现在线搜歌_第2张图片

本文主要讲述实线框框内的实现

  • Decode 中文 URL
    因为URL带有中文字符,做HTTP请求的时候,需要将中文字符 decode一下,方能识别,具体方法如下
CHAR from_hex(CHAR ch) 
{
    return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}

CHAR *url_decode(CHAR *str) 
{
    CHAR *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;

    while (*pstr) 
    {
        if (*pstr == '%') 
        {
            if (pstr[1] && pstr[2]) 
            {
                *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
                pstr += 2;
            }
        } 
        else if (*pstr == '+') 
        { 
            *pbuf++ = ' ';
        } 
        else 
        {
            *pbuf++ = *pstr;
        }
        pstr++;
    }
    *pbuf = '\0';

    return buf;
}

拿到decode的字符后,将其拼凑成一个完整的URL即可。

  • CURL模拟HTTP请求
    拿到可以访问的URL后,接下来就是用CURL模拟HTTP请求,去抓取百度搜歌的内容,此处利用的CURL将数据写到buffer的方式,并非保存文件方式,具体实现如下:
static INT32 curl_http_download_progress_callback(void *p,
                    double t, /* dltotal */
                     double d, /* dlnow */
                     double ultotal,
                     double ulnow)
{
    INT32 currentPercent = 0;
    if(t != 0)
    {
        currentPercent = (int)((double)100*(d/t));  
    }

    printf("Curl DownLoad percent : %d\n", currentPercent);

    if(100 == currentPercent)
    {
        sem_post(&semDownLoadFinished);
    }

    return CURL_RET_OK;
}

static size_t curl_http_write_memory_cb(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;

  mem->memory = realloc(mem->memory, mem->size + realsize + 1);
  if (mem->memory == NULL) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    exit(EXIT_FAILURE);
  }

  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;

  return realsize;
}

INT32 curl_http_get_page(const CHAR * url)
{
    CURL *curl;
    CURLcode res;

    chunk.memory = malloc(1);  /* will be grown as needed by the realloc above */
    chunk.size = 0;    /* no data at this point */

    res = curl_global_init(CURL_GLOBAL_ALL);
    if(CURLE_OK != res)
    {
        return CURL_RET_FAIL;
    }

    curl = curl_easy_init();
    if(curl) 
    {        
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_http_write_memory_cb); 
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_http_download_progress_callback);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,curl);

        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);                        // display debug msg

        curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }    

    return CURL_RET_OK;
}

在确保网页的内容全部保存到buffer中,这里用到了CURL的CURLOPT_PROGRESSFUNCTION参数,通过它,你可以拿到下载的百分比,当下载到100%时,会post一个信号量,收到这个信号量,就可以进行下一个环节了

注意:
1.这里没有做CURL超时处理,请自行加上
2.关于CURL的使用,可以参考以下地址
curl使用小结
ubuntu 13.10下安装curl
ubuntu下运行程序提示找不到libcurl动态库

  • XML解析
    拿到网页内容后,解析来就是XML字段。这里是利用libxml2去做解析,具体实现如下
CHAR * xml_parse_file(CHAR *buf, INT32 len)
{
    INT32 str1Len=0, str2Len=0;
    CHAR *temp1URL=NULL, *temp2URL=NULL;
    CHAR * retURL = NULL;

    xmlDocPtr doc;
    xmlNodePtr root,node;
    xmlChar *value;

    //xml_str_replace(buf, "encoding=\"gb2312\"", "encoding=\"utf-8\""); //此处因为在板子上跑,xml解析会出错,说不支持gb2312,而PC上的LINUX不需要转换即可解析,PC上加了也可以解析出来

    doc = xmlParseMemory(buf,len);    //parse xml in memory
    if (NULL == doc) 
    {  
        printf("Document not parsed successfully\n");
        return NULL; 
    } 

    root=xmlDocGetRootElement(doc);
    for(node=root->children;node;node=node->next)
    {
        if(xmlStrcasecmp(node->name,BAD_CAST"url")==0)
            break;
    }

    if(node==NULL)
    {
        TONLY_VOICE_LOG_ERR("no node = content\n");
        return NULL;
    }

    for(node=node->children;node;node=node->next)
    {
        if(xmlStrcasecmp(node->name,BAD_CAST"encode")==0)
        {   
            value=xmlNodeGetContent(node);            

            temp1URL = strrchr((CHAR *)value, '/');
            str1Len = strlen((CHAR *)value) - strlen((CHAR *)temp1URL);

            temp2URL = (CHAR *)malloc(sizeof(CHAR)*str1Len+1);            
            memset(temp2URL, 0, sizeof(CHAR)*str1Len+1);
            memcpy(temp2URL, value, str1Len+1);
            temp2URL[str1Len+1] = '\0';

            printf("Cut out the decode URL is %s\n",temp2URL);             
            xmlFree(value);
        }
        else if(xmlStrcasecmp(node->name,BAD_CAST"decode")==0)
        {       
            value=xmlNodeGetContent(node);

            if(temp2URL)
            {
                str2Len = strlen((CHAR *)value) + strlen((CHAR *)temp2URL);

                retURL = (CHAR *)malloc(sizeof(CHAR)*str2Len + 1);
                memset(retURL, 0, sizeof(CHAR)*strlen((CHAR *)value) + 1);
                strcpy(retURL, temp2URL);
                strcat(retURL, (CHAR *)value);
                retURL[str2Len+1] = '\0';
                free(temp2URL);

                printf("retURL is %s\n",retURL); 
            }

            xmlFree(value);
        }
    }
    xmlFreeDoc(doc);    

    return retURL;
}

最后xml_parse_file返回的字符串,就是最终的mp3播放地址,将其送给播放器就可以实现播放了。

注意:
1.libxml2的安装方法如下:
(1)sudo apt-get install libxml2
(2)sudo apt-get install libxml2-dev

2.libxml2使用方法
[Linux C]利用libxml2解析xml文件
Linux环境下C使用的XML解析库:libxml2

参考资料

  1. 百度MP3音乐API接口及应用
  2. 在线音乐API的研究
  3. 抓包获取百度音乐API
  4. linux c语言字符串函数replace,indexOf,substring等的实现
  5. libxml2官网

你可能感兴趣的:(C语言,curl,libxml2,百度音乐)