C语言模仿post请求并解析返回的json字符串

1. 需求

最近有一个需求,需要C语言判断某个域名是否备案,并爬取页面内容,过滤是否存在某个关键字;需求是很简单,可是用C语言如何实现呢?废话不多说,直接展示代码(已测试)

2. C语言代码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <string.h>
#include <json/json.h>
#include <iconv.h>

struct MemoryStruct 
{
    char *memory;
    size_t size;
};

//字符集转换核心函数,必须手动包含头文件  
int code_convert(char *from_charset,char *to_charset,char *inbuf,size_t inlen,char *outbuf,size_t outlen){
  iconv_t cd;
  int rc;
  char **pin = &inbuf;
  char **pout = &outbuf;
  cd = iconv_open(to_charset,from_charset);
  if(cd==(iconv_t) -1){
    printf("字符集打开失败\n");
    return -1;
  }
  memset(outbuf,0,outlen);
  if(iconv(cd,pin,&inlen,pout,&outlen)==-1){
     //changlq change 20160606
     iconv_close(cd);
     return -1;
  }
  iconv_close(cd);
  return 0;
}

//gbk->utf8对外接口, #以后必须把字符及定义为宏变量
int g2u(char *inbuf,char *outbuf){
  return code_convert("gbk","utf8",inbuf,(size_t)strlen(inbuf),outbuf,(size_t)(strlen(inbuf)*4));
}

//解析json格式数据,//code:0-已备案,请求执行成功;1-未备案,请求执行失败
int print_json_str( char *_json_str ){
	//json解析{"code":"0","msg":"success","data":{"name":"test","sex":"man"}}
  char *json_msg=_json_str;
  printf("changlq_test:[%s]\n",json_msg);
  
  struct json_object *result_json=NULL;
  struct json_object *tmp_obj=NULL;
  
  result_json = json_tokener_parse(json_msg);
  json_object_object_get_ex(result_json, "code",&tmp_obj);
  int _code = json_object_get_int(tmp_obj);     //0-已备案,请求执行成功;1-未备案,请求执行失败
  /* 已将所有参数解析,但此处仅展示code返回码,如需使用其它参数,打开此处注释即可
  if (_code == 0) {
      json_object_object_get_ex(result_json, "msg",&tmp_obj);
      const char *_msg = json_object_get_string(tmp_obj);
      json_object_object_get_ex(result_json, "data",&tmp_obj);
      struct json_object *_tmp_obj=NULL;
      json_object_object_get_ex(tmp_obj, "name",&_tmp_obj);
      const char *data_name = json_object_get_string(_tmp_obj);
      json_object_object_get_ex(tmp_obj, "sex",&_tmp_obj);
      const char *data_sex = json_object_get_string(_tmp_obj);

      printf("code[%d] msg[%s] data.name[%s] data.sex[%s]\n", _code, _msg, data_name,data_sex);
      
  }
  */
  //20200326 changlq add 内存泄漏相关排查(valgrind)
  json_object_put(result_json);
  json_object_put(tmp_obj);
  return _code;
}

//curl回调函数,由于访问一次可能调用多次回调函数,所以此处需要拼接字符串
size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
    //一次返回可能对应多次调用,此处用于拼接完整版的返回信息
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)data;

    mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory) 
    {
        memcpy(&(mem->memory[mem->size]), ptr, realsize);
        mem->size += realsize;
        mem->memory[mem->size] = 0;
    }
    return realsize;
}

//模仿post请求,传入端口用于拼接url,调用php接口时使用(需要传递的参数按格式拼接好后,调用curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, sp_para)设置即可)
//返回接口调用状态,即php-api函数的执行情况//0-已备案,请求执行成功;1-未备案,请求执行失败
//传入参数:php服务器ip  通信端口  相对路径uri  需要传输给php的参数(以按照格式拼接完成)
int send_post(char *sp_ip,int sp_port,char *sp_path,char *sp_para){
	  //拼接url
	  char *full_url=(char *)malloc(2048);
	  memset(full_url,0x0,2048);
	  if( sp_port==443 ){ //https
	  	sprintf(full_url,"https://%s:%d/%s",sp_ip,sp_port,sp_path);
	  }else{ //默认http
	  	sprintf(full_url,"http://%s:%d/%s",sp_ip,sp_port,sp_path);
	  }
	  printf("post_url:[%s]\n",full_url);
	  CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
    if(CURLE_OK != res)
    {
        printf("curl init failed\n");
        return -1;
    }

    CURL *pCurl = NULL;
    pCurl = curl_easy_init();

    if( NULL == pCurl)
    {
        printf("Init CURL failed...\n");
        curl_global_cleanup();
        return -1;
    }


    //请求的url地址
    curl_easy_setopt(pCurl, CURLOPT_URL, full_url ); //需要获取的URL地址
    //post方式发送请求不设置的话默认使用get方法发送请求
    curl_easy_setopt(pCurl, CURLOPT_POST, 1); 
    //post请求参数设置,get方法的参数可以在url中体现,post的参数必须在此处设置,参数格式如下"name=daniel&project=curl"
    curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, sp_para);
    // https请求 不验证证书和hosts
    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0);    
    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0);
    //请求超时时长(秒)
    curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 3L);
    //设置连接超时时长(秒)
    curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10L);  
    //调用过程中输出更多的关于调用操作的详细信息(屏幕输出)
//    curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1L); //启用时会汇报所有的信息
    
    //传递调用完成时的回调函数
    curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);  //得到请求结果后的回调函数
    struct MemoryStruct oDataChunk;  //请求结果的保存格式
    oDataChunk.memory=(char *)malloc(1);
    oDataChunk.size=0;
    //回调函数第四个参数
    curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, &oDataChunk);
    
    //允许URL地址的重定向
    curl_easy_setopt(pCurl, CURLOPT_FOLLOWLOCATION, 1L);//允许重定向
    //告诉libcurl在输出请求体时包含头部信息
    curl_easy_setopt(pCurl, CURLOPT_HEADER, 0L); 
    //关闭中断信号响应,如果是多线程,请将该参数置为1。这个选项用于unix环境下的多线程应用仍然可以使用各种timeout选项,而不会引发信号中断致使程序退出。
    curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, 1L); 

    struct curl_slist *pList = NULL;
    /* 缺省的头信息 */
    pList = curl_slist_append(pList,"User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12");
    pList = curl_slist_append(pList,"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    pList = curl_slist_append(pList,"Accept-language: zh-cn,zh;q=0.5");
    pList = curl_slist_append(pList,"Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7");
    //使用模拟的header头设置HTTP请求的头信息
    curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pList);

    //用于执行CURL对象,是一个阻塞类型的操作
    res=-1;
    res = curl_easy_perform(pCurl);  //执行请求
    if( res != CURLE_OK ){ //curl_easy_perform执行失败
    	printf("curl_easy_perform error,err_msg:[%ld]\n",res);
    }

    long res_code=-1;
    res=-1;
    res=curl_easy_getinfo(pCurl, CURLINFO_RESPONSE_CODE, &res_code);

    //正确响应后,请求转写成本地文件的文件
    int ret_flag=-1;
    if( res == CURLE_OK )
    {
    	if( res_code == 200 || res_code == 201 ){
        ret_flag=print_json_str(oDataChunk.memory);
      }  
    }
    
    free(oDataChunk.memory);
    free(full_url);
    curl_slist_free_all(pList); 
    curl_easy_cleanup(pCurl);
    curl_global_cleanup();
    return ret_flag;
}



//模仿get请求,传入端口用于判断http还是https,不用于拼接url,爬去页面时使用(需要传递的参数按照格式拼接到url后面即可)
//返回是否返现关键字//0-发现关键字;1-未发现
//传入参数:需要爬取的url  目标端口  关键字
int send_get(char *sg_domain_uri,int sg_port,char *sg_key_word){
	  //拼接url
	  char *full_url=(char *)malloc(2048);
	  memset(full_url,0x0,2048);
	  if( sg_port==443 ){ //https
	    sprintf(full_url,"https://%s",sg_domain_uri);
	  }else{ //默认http
	  	sprintf(full_url,"http://%s",sg_domain_uri);
	  }
	  printf("get_url:[%s]\n",full_url);
	  
	  CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
    if(CURLE_OK != res)
    {
        printf("curl init failed\n");
        return -1;
    }

    CURL *pCurl = NULL;
    pCurl = curl_easy_init();

    if( NULL == pCurl)
    {
        printf("Init CURL failed...\n");
        curl_global_cleanup();
        return -1;
    }


    //请求的url地址
    curl_easy_setopt(pCurl, CURLOPT_URL, full_url ); //需要获取的URL地址
    //post方式发送请求不设置的话默认使用get方法发送请求
//    curl_easy_setopt(pCurl, CURLOPT_POST, 1); 
    //post请求参数设置,get方法的参数可以在url中体现,post的参数必须在此处设置,参数格式如下"name=daniel&project=curl"
//    curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, sp_para);
    // https请求 不验证证书和hosts
    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0);    
    curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0);
    //请求超时时长(秒)
    curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 3L);
    //设置连接超时时长(秒)
    curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10L);  
    //调用过程中输出更多的关于调用操作的详细信息(屏幕输出)
//    curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1L); //启用时会汇报所有的信息
    
    //传递调用完成时的回调函数
    curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);  //得到请求结果后的回调函数
    struct MemoryStruct oDataChunk;  //请求结果的保存格式
    oDataChunk.memory=(char *)malloc(1);
    oDataChunk.size=0;
    //回调函数第四个参数
    curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, &oDataChunk);
    
    //允许URL地址的重定向
//    curl_easy_setopt(pCurl, CURLOPT_FOLLOWLOCATION, 1L);//允许重定向
    //告诉libcurl在输出请求体时包含头部信息
//    curl_easy_setopt(pCurl, CURLOPT_HEADER, 0L); 
    //关闭中断信号响应,如果是多线程,请将该参数置为1。这个选项用于unix环境下的多线程应用仍然可以使用各种timeout选项,而不会引发信号中断致使程序退出。
    curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, 1L); 

    struct curl_slist *pList = NULL;
    /* 缺省的头信息 */
    pList = curl_slist_append(pList,"User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12");
    pList = curl_slist_append(pList,"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    pList = curl_slist_append(pList,"Accept-language: zh-cn,zh;q=0.5");
    pList = curl_slist_append(pList,"Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7");
    //使用模拟的header头设置HTTP请求的头信息
    curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pList); 

    //用于执行CURL对象,是一个阻塞类型的操作
    res=-1;
    res = curl_easy_perform(pCurl);  //执行请求
    if( res != CURLE_OK ){ //curl_easy_perform执行失败
    	printf("curl_easy_perform error,err_msg:[%ld]\n",res);
    }

    long res_code=-1;
    res=-1;
    res=curl_easy_getinfo(pCurl, CURLINFO_RESPONSE_CODE, &res_code);

    //正确响应后,请求转写成本地文件的文件
    int ret_flag=-1;
    if( res == CURLE_OK )
    {
    	if( res_code == 200 || res_code == 201 ){
        //printf("changlq_get:[%s]\n",oDataChunk.memory);
        //页面内容爬取到oDataChunk.memory变量之后需要做如下操作
        //0.统一变量和关键字编码格式,然后过滤关键字
        char in_str[100],out_str[400];
	      memset(in_str,0x0,sizeof(in_str));
	      memset(out_str,0x0,sizeof(out_str));
	      strcpy(in_str,sg_key_word);
	      int rc=g2u(in_str,out_str);
        if(rc == 0){ 
        	//1.根据关键字过滤网页
        	if(strstr(oDataChunk.memory,out_str)){
            //printf("data_test:[%s]\n",oDataChunk.memory);
            printf("found keyword\n");
            ret_flag=0;//发现关键字
            //2.如果页面存在关键字,需要将页面内容以utf8的格式存储到数据库中,并返回存在标志
            
          }else{
          	printf("not found\n");
          	ret_flag=1;//没有发现关键字,此处默认网页的字符集和库的都是utf8,如果后续需要判断的话,建议将关键字转为gbk再过滤一遍
          	
          }
        }else{
        	printf("iconv error\n");
        }
        
        /*
        //1.无需转码,根据关键字过滤网页
        if(strstr(oDataChunk.memory,sg_key_word)){
          //printf("data_test:[%s]\n",oDataChunk.memory);
          printf("found keyword\n");
          ret_flag=0;//发现关键字
          //2.如果页面存在关键字,需要将页面内容以utf8的格式存储到数据库中,并返回存在标志
          
        }else{
        	printf("not found\n");
        	ret_flag=1;//没有发现关键字,此处默认网页的字符集和库的都是utf8,如果后续需要判断的话,建议将关键字转为gbk再过滤一遍
        } 
        */
      }  
    }
    
    free(oDataChunk.memory);
    free(full_url);
    curl_slist_free_all(pList); 
    curl_easy_cleanup(pCurl);
    curl_global_cleanup();
    return ret_flag;
}


//gcc send_get_msg.c  -o send_get_msg -lcurl -ljson
int main()
{
	//查询是否备案的php接口:http://192.168.9.168:80/index.php,需要出入参数domain  (此接口为模拟接口,非正式接口)
    char ip[255] = "192.168.9.168";
    int  port=80;
    char para[255] = "domain=www.baidu.com";
	  
	//查询域名是否备案
	int ret_flag=send_post(ip,port,"index.php",para);
	if(ret_flag==0){
       printf("已备案\n");
    }else{
       printf("未备案\n"); 
    }
	printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
	//爬取www.baidu.com,并判断页面中是否有“百度”关键字(由于我本地用的是GBK,而浏览器是utf-8字符集,所以过滤关键字的时候我增加了转码字符串的动作,即将第三个参数“百度”字符串由GBK转为UTF-8,如果本地就是utf-8的,无需后续转码,两种方式都已给出)
	send_get("www.baidu.com",443,"百度");
    return 0;
}

效果展示:
在这里插入图片描述

GitHub 加速计划 / js / json
41.72 K
6.61 K
下载
适用于现代 C++ 的 JSON。
最近提交(Master分支:1 个月前 )
960b763e 3 个月前
8c391e04 6 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐