从v0.15.0版本开始,在编译时,可以添加curl模块,从而可以执行抓取数据相关的操作。只要在make命令后面加入USE_CURL=yes即可。

    页面导航:

项目下载地址:

    zenglServer源代码的相关地址:https://github.com/zenglong/zenglServer  当前版本对应的tag标签为:v0.15.1

zenglServer v0.15.0 - v0.15.1:

    从v0.15.0版本开始,在编译时,可以添加curl模块,从而可以执行抓取数据相关的操作。只要在make命令后面加入USE_CURL=yes即可。

    当然,要使用curl模块,前提是系统中安装了底层的curl开发库。

    如果是ubuntu系统,可以通过 sudo apt-get install curl libcurl3 libcurl3-dev 来安装curl相关的库和开发头文件等:

zengl@zengl-ubuntu:~$ sudo apt-get install curl libcurl3 libcurl3-dev

    如果是centos系统,则可以通过 yum install curl curl-devel 来安装相关的底层库:

[root@localhost ~]# yum install curl curl-devel

    要同时使用mysql,magick,pcre,以及curl模块,可以使用 make USE_MYSQL=yes USE_MAGICK=6 USE_PCRE=yes USE_CURL=yes 命令:

[parallels@localhost zenglServer]$ make USE_MYSQL=yes USE_MAGICK=6 USE_PCRE=yes USE_CURL=yes
...................................................
gcc -g3 -ggdb -O0 -std=c99 main.c http_parser.c module_request.c module_builtin.c module_session.c dynamic_string.c multipart_parser.c resources.c client_socket_list.c json.c randutils.c md5.c debug.c main.h http_parser.h common_header.h module_request.h module_builtin.h module_session.h dynamic_string.h multipart_parser.h resources.h client_socket_list.h json.h randutils.h md5.h debug.h module_mysql.c module_mysql.h  module_magick.c module_magick.h module_pcre.c module_pcre.h module_curl.c module_curl.h zengl/linux/zengl_exportfuns.h  -o zenglServer zengl/linux/libzengl.a crustache/libcrustache.a -lpthread -lm -DUSE_MYSQL `mysql_config --cflags --libs`  -D USE_MAGICK=6 `pkg-config --cflags --libs Wand` -DUSE_PCRE `pcre-config --cflags --libs` -DUSE_CURL `curl-config --cflags --libs`

mysql module is enabled!!!
magick module is enabled!!!
pcre module is enabled!!!
curl module is enabled!!!
[parallels@localhost zenglServer]$

    和curl模块相关的C源码位于module_curl.c文件中:

/*
 * module_curl.c
 *
 *  Created on: Nov 20, 2018
 *      Author: zengl
 */

............................................................................

/**
 * curlEasyInit模块函数,执行curl初始化,并返回一个用于执行curl操作的指针,
 * 返回的指针是my_curl_handle_struct类型的,该类型对应的结构体中又封装了一个CURL类型的指针(用于执行实际的底层的curl操作的),
 * 该模块函数在创建了my_curl_handle_struct类型的指针后,还会将其存储到资源列表中,
 * 这样其他的curl模块函数在接收到该类型的指针后,就可以根据资源列表来判断是否是有效的指针了,
 * 同时,如果在脚本中忘了手动清理该指针的话,在脚本结束时,也会自动根据资源列表来清理掉指针。
 * 该模块函数相关的示例代码,请参考curlEasyPerform模块函数的注释部分
 *
 * 此模块函数在底层,会先通过curl_global_init库函数来进行curl的初始化操作,如果在同一脚本中已经初始化过了,则会跳过去,不会重复进行初始化,
 * 接着会通过curl_easy_init的库函数来创建一个CURL类型的指针,并将该指针封装到自定义的my_curl_handle_struct类型的结构中,
 * 最后将my_curl_handle_struct类型的指针返回,之所以做一个封装,是为了兼容旧的curl库版本而做的兼容处理。
 * curl_global_init库函数的官方地址为:https://curl.haxx.se/libcurl/c/curl_global_init.html
 * curl_easy_init库函数的官方地址为:https://curl.haxx.se/libcurl/c/curl_easy_init.html
 */
ZL_EXP_VOID module_curl_easy_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	st_curl_global_init();
	CURL * curl_handle = curl_easy_init();
	write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] curl_easy_init: %x\n", curl_handle); // debug
	my_curl_handle_struct * my_curl_handle = zenglApi_AllocMem(VM_ARG, sizeof(my_curl_handle_struct));
	memset(my_curl_handle, 0, sizeof(my_curl_handle_struct));
	my_curl_handle->curl_handle = curl_handle;
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)my_curl_handle, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_set_member(&(my_data->resource_list), my_curl_handle, st_curl_easy_cleanup_callback);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "curlEasyInit add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
	}
}

/**
 * curlEasyCleanup模块函数,清理和my_curl_handle_struct指针相关的资源
 * 该模块函数的第一个参数必须是有效的my_curl_handle_struct指针,该指针由curlEasyInit模块函数返回,
 * 当不需要执行curl相关的抓取操作时,可以手动通过该模块函数将指针相关的资源给清理掉,
 * 如果没有手动清理的话,在脚本退出时也会自动进行指针的清理操作。
 * 该模块函数的示例代码,请参考curlEasyPerform模块函数的注释部分
 *
 * my_curl_handle_struct指针指向的结构中,又封装了一个CURL类型的指针,该指针是用于执行实际的curl操作的,
 * 该模块函数在底层,会通过curl_easy_cleanup库函数将CURL类型指针相关的资源给清理掉,
 * 和curl_easy_cleanup库函数相关的官方地址为:https://curl.haxx.se/libcurl/c/curl_easy_cleanup.html
 */
ZL_EXP_VOID module_curl_easy_cleanup(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: curlEasyCleanup(curl_handle): integer");
	............................................................................
}

/**
 * curlEasySetopt模块函数,设置curl抓取相关的选项,例如:抓取的目标地址,需要使用的用户代理等
 * 该模块函数的第一个参数必须是有效的my_curl_handle_struct指针,该指针由curlEasyInit模块函数返回,
 * 第二个参数是字符串类型的选项名称,暂时只支持以下几个选项:
 * 'URL':表示需要抓取的目标地址
 * 'USERAGENT':需要设置的用户代理
 * 'FOLLOWLOCATION':当抓取到重定向页面时,是否进行重定向操作
 * 'SSL_VERIFYPEER':是否校验SSL证书
 * 'TIMEOUT': 设置超时时间
 * 第三个参数是需要设置的具体的选项值,当第二个参数是'URL','USERAGENT'时,选项值必须是字符串类型,表示需要设置的url地址,用户代理等,
 * 当第二个参数是'FOLLOWLOCATION'时,选项值必须是整数类型,表示是否进行重定向操作,默认是0,即不进行重定向,需要进行重定向的,可以将选项值设置为1
 * 当第二个参数是'SSL_VERIFYPEER'时,选项值必须是整数类型,表示是否校验SSL证书,默认是1,即需要进行校验,如果不需要校验,可以将选项值设置为0
 * 当第二个参数是'TIMEOUT'时,选项值必须是整数类型,表示需要设置的超时时间
 * 具体的例子,请参考curlEasyPerform模块函数的注释部分
 *
 * 该模块函数最终会通过curl_easy_setopt库函数去执行具体的操作,
 * 该库函数的官方地址为:https://curl.haxx.se/libcurl/c/curl_easy_setopt.html
 */
ZL_EXP_VOID module_curl_easy_setopt(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 3)
		zenglApi_Exit(VM_ARG,"usage: curlEasySetopt(curl_handle, option_name, option_value): integer");
	............................................................................
}

/**
 * curlEasyPerform模块函数,它会使用curl库执行具体的抓取操作
 * 该模块函数的第一个参数必须是有效的my_curl_handle_struct指针,该指针由curlEasyInit模块函数返回,
 * 第二个参数content必须是引用类型,用于存储抓取到的具体数据,
 * 第三个参数size也必须是引用类型,用于存储抓取到的数据的字节大小,该参数是可选的,
 * 该模块函数如果执行成功,会返回0,如果执行失败,则返回相应的错误码,可以使用curlEasyStrError模块函数,来获取错误码对应的字符串类型的错误描述,
 * 例如:
	use builtin, curl, request;
	def TRUE 1;
	def FALSE 0;

	rqtSetResponseHeader("Content-Type: text/html; charset=utf-8");
	curl_handle = curlEasyInit();
	curlEasySetopt(curl_handle, 'URL', 'https://www.example.com/');
	curlEasySetopt(curl_handle, 'USERAGENT', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0');
	curlEasySetopt(curl_handle, 'FOLLOWLOCATION', TRUE);
	curlEasySetopt(curl_handle, 'SSL_VERIFYPEER', FALSE);
	curlEasySetopt(curl_handle, 'TIMEOUT', 30);
	ret = curlEasyPerform(curl_handle, &content, &size);
	if(ret == 0)
		print 'size: ' + size;
		print 'content: ' + content;
	else
		print 'error: ' + curlEasyStrError(ret);
	endif
	curlEasyCleanup(curl_handle);

	上面脚本中会先通过curlEasyInit模块函数获取my_curl_handle_struct类型的指针,
	接着,通过curlEasySetopt模块函数来设置需要抓取的目标地址,以及设置useragent用户代理等,
	在设置好后,最后通过curlEasyPerform模块函数去执行抓取操作,如果返回的ret为0,则content变量将包含抓取的数据,size变量则会包含抓取数据的字节大小,
	如果返回的ret不为0,则会将返回值传递给curlEasyStrError模块函数,以获取具体的错误描述信息。

	该模块函数在底层最终会通过curl_easy_perform库函数去执行具体的抓取操作,
	该库函数的官方地址为:https://curl.haxx.se/libcurl/c/curl_easy_perform.html
 */
ZL_EXP_VOID module_curl_easy_perform(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 2)
		zenglApi_Exit(VM_ARG,"usage: curlEasyPerform(curl_handle, &content[, &size]): integer");
	............................................................................
}

/**
 * curlEasyStrError模块函数,根据其他模块函数返回的整数类型的错误码,返回相应的错误描述
 * 该模块函数的第一个参数是整数类型的错误码,返回的结果是字符串类型的错误信息
 * 该模块函数底层会通过curl_easy_strerror库函数来获取具体的错误信息
 * curl_easy_strerror库函数的官方地址:https://curl.haxx.se/libcurl/c/curl_easy_strerror.html
 */
ZL_EXP_VOID module_curl_easy_strerror(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: curlEasyStrError(errornum): string");
	............................................................................
}

/**
 * curlVersion模块函数,用于获取当前所使用的curl库的版本信息
 * 该模块函数除了会返回curl库的版本信息,还会将与其相关的组件的版本信息也显示出来(例如:OpenSSL等)
 * 该模块函数返回的结果,类似如下:
 * libcurl/7.29.0 NSS/3.34 zlib/1.2.11 libidn/1.28 libssh2/1.4.3
 * 该模块函数底层会通过curl_version的库函数去执行具体的操作
 * curl_version库函数的官方地址:https://curl.haxx.se/libcurl/c/curl_version.html
 */
ZL_EXP_VOID module_curl_version(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, (char *)curl_version(), 0, 0);
}

/**
 * curl模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄(对应的C函数)
 */
ZL_EXP_VOID module_curl_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID)
{
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlEasyInit",module_curl_easy_init);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlEasyCleanup",module_curl_easy_cleanup);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlEasySetopt",module_curl_easy_setopt);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlEasyPerform",module_curl_easy_perform);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlEasyStrError",module_curl_easy_strerror);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"curlVersion",module_curl_version);
}

    在my_webroot的v0_15_0目录中,增加了两个测试脚本:test.zl以及test_with_pcre.zl,其中,test.zl脚本主要利用上面这些curl模块函数来抓取并显示网页内容。

    而test_with_pcre.zl脚本则是在test.zl脚本代码的基础上,在抓取到网页数据后,结合pcre正则表达式模块,将网页中的title标题给提取了出来,从而实现了一个简单的采集操作。

    test.zl脚本的代码如下:

use builtin, curl, request;
def TRUE 1;
def FALSE 0;

rqtSetResponseHeader("Content-Type: text/html; charset=utf-8");

print 'curl version: ' + curlVersion();

curl_handle = curlEasyInit();
curlEasySetopt(curl_handle, 'URL', 'https://www.example.com/');
// curlEasySetopt(curl_handle, 'URL', 'https://www.baidu.com/');
curlEasySetopt(curl_handle, 'USERAGENT', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0');
curlEasySetopt(curl_handle, 'FOLLOWLOCATION', TRUE);
curlEasySetopt(curl_handle, 'SSL_VERIFYPEER', FALSE);
curlEasySetopt(curl_handle, 'TIMEOUT', 30);
ret = curlEasyPerform(curl_handle, &content, &size);
if(ret == 0)
	print 'size: ' + size;
	print 'content: ' + content;
	// print 'content: ' + bltHtmlEscape(content);
else
	print 'error: ' + curlEasyStrError(ret);
endif
curlEasyCleanup(curl_handle);

    上面脚本中,先通过curlEasyInit模块函数返回了一个用于执行curl操作的指针,后面的curlEasySetopt,curlEasyPerform,curlEasyCleanup这些模块函数都需要接收这个指针作为第一个参数。

    接着,脚本中使用curlEasySetopt模块函数设置了curl抓取相关的选项。

    其中,URL选项设置了需要访问的url地址,上面代码中,设置的地址为:https://www.example.com/

    USERAGENT选项设置了用户代理信息,上面代码中,设置的用户代理为:Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0

    FOLLOWLOCATION选项设置了当遇到重定向页面时,需要执行重定向操作,代码中的第三个参数为TRUE即整数1,表示需要重定向。

    SSL_VERIFYPEER选项设置了不需要校验SSL证书,代码中的第三个参数为FALSE即整数0,表示不校验证书。

    TIMEOUT选项设置了超时时间,代码中设置的超时时间为30秒。

    在设置了curl抓取相关的选项后,代码中就通过curlEasyPerform模块函数去执行具体的抓取操作,当该模块函数的返回值为0时,就表示执行成功,抓取的数据会存储在content变量中,抓取到的数据的字节大小会存储在size变量中。

    curlEasyPerform模块函数的第三个size参数是可选的,也就是说,没提供size变量作为参数的话,模块函数就不会去设置抓取的数据大小。此外,该模块函数的第二个和第三个参数都必须是引用类型,才能获取到抓取的数据信息,所以代码中就将content和size变量的引用传给了模块函数。

    如果curlEasyPerform模块函数的返回值不为0,则表示抓取失败,此时,返回值会是一个错误码,可以通过curlEasyStrError模块函数将错误码对应的具体错误信息获取出来。

    在curl抓取完毕后,代码最后通过curlEasyCleanup模块函数手动清理掉了curl_handle这个用于curl操作的指针。如果没有手动清理的话,zenglServer在脚本执行结束时,也会自动执行指针的清理操作。

    test.zl脚本的执行结果如下:

test.zl脚本在浏览器中的执行结果

    test_with_pcre.zl脚本的内容如下:

use builtin, curl, request, pcre;
def TRUE 1;
def FALSE 0;

rqtSetResponseHeader("Content-Type: text/html; charset=utf-8");

print 'curl version: ' + curlVersion();

curl_handle = curlEasyInit();
curlEasySetopt(curl_handle, 'URL', 'https://www.example.com/');
// curlEasySetopt(curl_handle, 'URL', 'https://www.baidu.com/');
curlEasySetopt(curl_handle, 'USERAGENT', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0');
curlEasySetopt(curl_handle, 'FOLLOWLOCATION', TRUE);
curlEasySetopt(curl_handle, 'SSL_VERIFYPEER', FALSE);
curlEasySetopt(curl_handle, 'TIMEOUT', 30);
ret = curlEasyPerform(curl_handle, &content, &size);
if(ret == 0)
	print 'size: ' + size + '<br/>';
	ret = pcreMatch('<title>(.*?)</title>', content, &results, 'is');
	if(!ret)
		print 'no match';
	else
		print 'title: ' + results[1];
	endif
else
	print 'error: ' + curlEasyStrError(ret);
endif
curlEasyCleanup(curl_handle);

    上面脚本中使用curl抓取网页数据的代码,和test.zl是一致的,在抓取到网页数据后,接着就通过pcre正则表达式模块里的pcreMatch模块函数,将title标签里的标题信息给提取并显示了出来。

    test_with_pcre.zl脚本的执行结果如下:

test_with_pcre.zl脚本在浏览器中的执行结果

    以上就是当前版本的相关内容,就到这里,休息,休息一下。

结束语:

    只要还在路上,就要坚持创业的精神

—— 《创业时代》

 

上下篇

下一篇: zenglServer v0.16.0 增加curlSetPostByHashArray,curlSetHeaderByArray模块函数,调整进程名称等

上一篇: zenglServer v0.14.0 正则表达式模块

相关文章

zenglServer v0.18.0 直接在命令行中执行脚本

zenglServer v0.6.0 session会话

zenglServer v0.10.1 添加bltInt,bltFloat,bltHtmlEscape模块函数,使用v1.8.1版本的zengl语言库

zenglServer v0.9.0 pydebugger 远程调试

zenglServer v0.17.0 设置精简日志模式,设置允许上传文件大小,日志分割等

zenglServer v0.5.0 设置响应头, 获取cookie