Linux下通过curl实现新浪天气API应用
新浪天气API的使用方法:
API地址:http://php.weather.sina.com.cn/xml.php?city=%B1%B1%BE%A9&password=DJOYnieT8234jlsK&day=0
红色标记为城市代码(也就是城市的中文转为GB2312的十六进制代码,比如北京对应的GB2312十六进制代码为B1B1BEA9),实际上需要查哪个城市就把红色标记改为对应城市代码即可.而实际上打开此url后对应的是一个xml文件,里面包括了此城市的天气信息.
<?xml version="1.0" encoding="UTF-8"?>
<!-- published at 2014-10-31 17:08:03 -->
<Profiles>
<Weather>
<city>北京</city>
<status1>小雨</status1>
<status2>多云</status2>
<figure1>xiaoyu</figure1>
<figure2>duoyun</figure2>
<direction1>无持续风向</direction1>
<direction2>无持续风向</direction2>
<power1>≤3</power1>
<power2>≤3</power2>
<temperature1>14</temperature1>
<temperature2>9</temperature2>
<ssd>5</ssd>
<tgd1>16</tgd1>
<tgd2>16</tgd2>
<zwx>2</zwx>
<ktk>6</ktk>
<pollution>3</pollution>
<xcz></xcz>
<zho></zho>
<diy></diy>
<fas></fas>
<chy>4</chy>
<zho_shuoming>暂无</zho_shuoming>
<diy_shuoming>暂无</diy_shuoming>
<fas_shuoming>暂无</fas_shuoming>
<chy_shuoming>套装、夹衣、风衣、夹克衫、西服套装、马甲衬衫配长裤</chy_shuoming>
<pollution_l>轻度</pollution_l>
<zwx_l>弱</zwx_l>
<ssd_l>略凉</ssd_l>
<fas_l>暂无</fas_l>
<zho_l>暂无</zho_l>
<chy_l>夹衣类</chy_l>
<ktk_l>适宜开启(制热)</ktk_l>
<xcz_l>暂无</xcz_l>
<diy_l>暂无</diy_l>
<pollution_s>对空气污染物扩散无明显影响</pollution_s>
<zwx_s>紫外线弱</zwx_s>
<ssd_s>感觉有些凉,但是凉意微薄,不影响户外活动的开展。</ssd_s>
<ktk_s>适宜开启空调</ktk_s>
<xcz_s>暂无</xcz_s>
<gm>2</gm>
<gm_l>易发期</gm_l>
<gm_s>天气很凉,季节转换的气候,慎重增加衣服;较易引起感冒;</gm_s>
<yd>5</yd>
<yd_l>不适宜</yd_l>
<yd_s>虽然晴空万里,但是天气较凉,多数人不适宜户外运动;</yd_s>
<savedate_weather>2014-10-31</savedate_weather>
<savedate_life>2014-10-31</savedate_life>
<savedate_zhishu>2014-10-31</savedate_zhishu>
</Weather>
</Profiles>
现在我们的工作就是使用curl打开这个xml文件并且将其转换为我们需要的数据结构.
#ifndef __SINA_WEATHER__
#define __SINA_WEATHER__
#include <stdio.h>
#define FALSE (0)
#define TRUE (!0)
#define SINA_WEATHER_URL_HEAD "http://php.weather.sina.com.cn/xml.php?city="
#define SINA_WEATHER_URL_TAIL "&password=DJOYnieT8234jlsK&day=0"
struct date {
short year;
short month;
short day;
};
struct weather_info {
char * city;
//天气情况(中文)
char * status1;
char * status2;
//天气情况(拼音)
char * figure1;
char * figure2;
//风向
char * direction1;
char * direction2;
//风级
char * power1;
char * power2;
//温度
int temperature1;
int temperature2;
//体感指数数值
int ssd;
//体感度指数
char * ssd_l;
//体感度指数说明
char * ssd_s;
//体感温度
int tgd1;
int tgd2;
//紫外线指数数值
int zwx;
//紫外线指数
char * zwx_l;
//紫外线指数说明
char * zwx_s;
//空调指数数值
int ktk;
//空调指数
char * ktk_l;
//空调指数说明
char * ktk_s;
//污染指数数值
int pollution;
//污染扩散条件
char * pollution_l;
//污染指数说明
char * pollution_s;
//穿衣指数数值
int chy;
//穿衣指数
char * chy_l;
//穿衣说明
char * chy_shuoming;
//洗车指数数值
int xcz;
//洗车指数
char * xcz_l;
//洗车指数说明
char * xcz_s;
//感冒指数数值
int gm;
//感冒指数
char * gm_l;
//感冒指数说明
char * gm_s;
//运动指数数值
int yd;
//运动指数
char * yd_l;
//运动指数说明
char * yd_s;
struct date weather_date;
struct date life_date;
struct date zhishu_date;
};
struct weather_info * get_weather_info_from_sina (const char * city_name);
#endif
先说一下流程,首先设计想法是就定义一个接口,调用时只需要输入一个城市名称,则自动返回此城市的天气信息结构体,而在内部,分别执行了UTF-8转GB2312格式, curl初始化, 获取天气XML信息, 填充struct weather_info结构体.
流程图:
代码(sina_weather.c):
#include <stdio.h>
#include <curl/curl.h>
#include <iconv.h>
#include "sina_weather.h"
//根据key获取xml中对应的值
char * get_xml_key_value (const char * xml, char * key)
{
char * head;
char * tail;
char stamp[1024];
char * value;
sprintf(stamp, "<%s>", key);
if ((head = strstr (xml, stamp)) == NULL)
return NULL;
head = head + strlen (stamp);
while (isspace (head[0]))
head ++;
sprintf(stamp, "</%s>", key);
if ((tail = strstr (head, stamp)) == NULL)
return NULL;
while (isspace(tail[-1]) && (tail > head))
tail --;
if (tail > head){
value = calloc (tail - head + 1, 1);
memcpy (value, head, tail - head);
value[tail - head] = 0x00;
}
return value;
}
//UTF-8转GB2312
int utf8_to_gb2312 (const char * src, char * dest_buf)
{
iconv_t con;
int src_length = strlen (src);
if( (con = iconv_open ("gb2312", "utf-8")) ==0 )
return FALSE;
memset (dest_buf, 0, src_length);
if(-1 == iconv (con, &src, &src_length, &dest_buf, &src_length))
return FALSE;
iconv_close (con);
return TRUE;
}
//将一个GB2312字符串转换为它的十六进制字符串(每个字符用%隔开)
char * str_convert_to_gb2312_hex (const char * src)
{
if (src == NULL)
return NULL;
int length = strlen (src) - 2;
int index = 0;
int num = 0;
char * gb_str = calloc (((length) + 1), 1);
char * hex_str = calloc ((length * 3) + 1, 1);
utf8_to_gb2312 (src, gb_str);
while ((index / 3) < length) {
hex_str[index] = '%';
sprintf (&hex_str[index + 1], "%02X", gb_str[index / 3] & 0xff);
index = index + 3;
}
hex_str[length * 3] = '\0';
return hex_str;
}
void convert_to_gb2312_clean (char * str)
{
free (str);
}
//curl下载时的回调函数,当下载到数据时会调用此函数进行操作
size_t down_data_callback (void * ptr, size_t size, size_t nmemb, void * user_buf)
{
strcat (user_buf, ptr);
return size * nmemb;
}
uint64_t curl_get_content (const char * url, char * content)
{
if ((url == NULL) || (content == NULL))
return;
CURL * curl = curl_easy_init ();
curl_easy_setopt (curl, CURLOPT_URL, url); //设置curl的目标url地址
curl_easy_setopt (curl, CURLOPT_TIMEOUT, 15); //下载超时时间
curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); //屏蔽其他信号
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, down_data_callback); //设置下载回调函数
curl_easy_setopt (curl, CURLOPT_WRITEDATA, content); //设置下载数据保存缓冲区(此参数会传到down_data_callback的user_buf)
CURLcode retval = curl_easy_perform (curl);
if (retval == CURLE_OK) {
double down_length = 0;
curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &down_length);
curl_easy_cleanup (curl);
return (uint64_t)down_length;
}
curl_easy_cleanup (curl);
return 0;
}
struct weather_info * weather_info_get_from_xml (const char * xml, struct weather_info * w_info)
{
if ((xml == NULL) || (w_info == NULL))
return w_info;
char * str_data = NULL;
w_info->city = get_xml_key_value (xml, "city");
w_info->status1 = get_xml_key_value (xml, "status1");
w_info->status1 = get_xml_key_value (xml, "status1");
str_data = get_xml_key_value (xml, "temperature1");
w_info->temperature1 = (int) atoi (str_data);
free (str_data);
str_data = get_xml_key_value (xml, "temperature2");
w_info->temperature2 = (int) atoi (str_data);
free (str_data); //此处省略N多,基本都同上操作.
}
struct weather_info * get_weather_info_from_sina (const char * city_name)
{
if (city_name == NULL)
return NULL;
CURL * curl = NULL;
CURLcode retval = 0;
char xml_data[4096];
char url[1024] = SINA_WEATHER_URL_HEAD;
char * city_hex = str_convert_to_gb2312_hex (city_name);
struct weather_info * sina_w_info = calloc (sizeof (struct weather_info), 1);
strcat (url, city_hex);
strcat (url, SINA_WEATHER_URL_TAIL);
convert_to_gb2312_clean (city_hex);
uint64_t down_size = curl_get_content (url, xml_data);
if (down_size == 0) {
free (sina_w_info);
return NULL;
}
weather_info_get_from_xml (xml_data, sina_w_info);
return sina_w_info;
}
CURL:
或许有些同学不太清楚怎么在c中使用curl库,其实使用的方法很简单,就简单说明一下,在ubuntu下安装curl库命令
apt-get install libcurl-nss-dev
在编译时需要加入的选项:
-lcurl
使用curl下载时常规的代码顺序为:
CURL * curl = curl_easy_init (); //初始化
curl_easy_setopt (curl, CURLOPT_URL, url); //具体设置代表的意思在http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
curl_easy_setopt (curl, CURLOPT_TIMEOUT, 15);
curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, down_data_callback);
curl_easy_setopt (curl, CURLOPT_WRITEDATA, content);
CURLcode retval = curl_easy_perform (curl); //执行操作
if (retval == CURLE_OK) {
//添加所需要处理的代码 ........
}
curl_easy_cleanup (curl); //清空
在使用curl下载时,会设置CURLOPT_WRITEFUNCTION,此设置主要是为了在下载有数据过来时,调用down_data_callback这个回调函数,并将数据保存到content这个缓冲区中.
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, down_data_callback);
curl_easy_setopt (curl, CURLOPT_WRITEDATA, content);
size_t down_data_callback (void * ptr, size_t size, size_t nmemb, void * user_buf)
{
strcat (user_buf, ptr);
return size * nmemb;
}