一只简单的网络爬虫(基于linux C/C++)————配置文件设计及读取
一般来说linux下比较大型的程序都是以配置文件作为参数介质传递的,该爬虫也采用配置文件的方式来获取参数,配置文件格式大致如下:
max_job_num=1
#seeds=https://www.baidu.com
#seeds=http://bbs.scut.edu.cn/classic/
logfile=spiderq.log
以“=”作为分割符,左边为key,右边为valve,主要包含的参数内容为:最大的任务数,种子,日志文件,日志等级,深度,模块路径,支持的文件类型,等,以“#”开头的就作为注释。
配置文件结构体如下:
Config * initconfig()
{
Config *conf = (Config *)malloc(sizeof(Config));
conf->max_job_num = 10;
conf->seeds = NULL;
conf->include_prefixes = NULL;
conf->exclude_prefixes = NULL;
conf->logfile = NULL;
conf->log_level = 0;
conf->max_depth = INF;
conf->make_hostdir = 0;
conf->module_path = NULL;
conf->stat_interval = 0;
//conf->modules
return conf;
}
是一个全局的结构体,若以类的方式实现则可以设计成单例类,里面的参数主要保存从配置文件中读取到的东西。加载配置文件后,配置文件的参数设置被保存到了Config结构体的,供后面的函数使用
加载配置文件的函数如下:
void loadconfig(Config *conf)
{
FILE *fp = NULL;
char buf[MAX_CONF_LEN+1];
int argc = 0;
char **argv = NULL;
int linenum = 0;
char *line = NULL;
const char *err = NULL;
if ((fp = fopen(CONF_FILE, "r")) == NULL) //打开配置文件
{
SPIDER_LOG(SPIDER_LEVEL_ERROR, "Can't load conf_file %s", CONF_FILE);
}
while (fgets(buf, MAX_CONF_LEN+1, fp) != NULL)//获取一行到buf
{
linenum++;
line = strim(buf);//去除空格,在qstring中
//过滤#
if (line[0] == '#' || line[0] == '\0') continue;
//count: 分割后的字符串长度
//limit: 分割多少次
argv = strsplit(line, '=', &argc, 1);
if (argc == 2) {
if (strcasecmp(argv[0], "max_job_num") == 0) {
conf->max_job_num = atoi(argv[1]);
}
//extern char *strdup(char *s);
//功 能: 将串拷贝到新建的位置处
//strdup()在内部调用了malloc()为变量分配内存,
//不需要使用返回的字符串时,需要用free()释放相应的内存空间,
//否则会造成内存泄漏
//strcasecmp(忽略大小写比较字符串)
else if (strcasecmp(argv[0], "logfile") == 0) {
conf->logfile = strdup(argv[1]);
} else if (strcasecmp(argv[0], "include_prefixes") == 0) {
conf->include_prefixes = strdup(argv[1]);
} else if (strcasecmp(argv[0], "exclude_prefixes") == 0) {
conf->exclude_prefixes = strdup(argv[1]);
} else if (strcasecmp(argv[0], "seeds") == 0) {
conf->seeds = strdup(argv[1]);
} else if (strcasecmp(argv[0], "module_path") == 0) {
conf->module_path = strdup(argv[1]);
} else if (strcasecmp(argv[0], "load_module") == 0) {
conf->modules.push_back(strdup(argv[1]));
} else if (strcasecmp(argv[0], "log_level") == 0) {
conf->log_level = atoi(argv[1]);
} else if (strcasecmp(argv[0], "max_depth") == 0) {
conf->max_depth = atoi(argv[1]);
} else if (strcasecmp(argv[0], "stat_interval") == 0) {
conf->stat_interval = atoi(argv[1]);
} else if (strcasecmp(argv[0], "make_hostdir") == 0) {
conf->make_hostdir = yesnotoi(argv[1]);
} else if (strcasecmp(argv[0], "accept_types") == 0) {
conf->accept_types.push_back(strdup(argv[1]));
} else {
err = "Unknown directive"; goto conferr;
}
} else {
err = "directive must be 'key=value'"; goto conferr;
}
}
return;
conferr:
SPIDER_LOG(SPIDER_LEVEL_ERROR, "Bad directive in %s[line:%d] %s", CONF_FILE, linenum, err);
}
下面介绍几个函数:
(1)fgets函数是用来获取行的,该函数可读取文件中的一行,并且会包含换行符
(2)strim函数用于去除空格,其实现如下:参数是行指针
//去除空格
char * strim(char *str)
{
char *end, *sp, *ep;
size_t len;
sp = str;
end = ep = str+strlen(str)-1;
//从行首开始
while(sp <= end && isspace(*sp)) sp++;
//从行尾开始
while(ep >= sp && isspace(*ep)) ep--;
//计算长度
len = (ep < sp) ? 0 : (ep-sp)+1;
sp[len] = '\0';
return sp;
}
(3)strsplit函数用于分割字符串,实现方法如下:
//切割字符串,函数内申请,函数外释放
//count: 分割后的字符串长度
//limit: 分割多少次
char ** strsplit(char *line, char delimeter, int *count, int limit)
{
char *ptr = NULL, *str = line;
char **vector = NULL;
*count = 0;
//strchr函数原型:extern char *strchr(const char *s,char c);查找字符串s中首次出现字符c的位置。
while((ptr = strchr(str, delimeter)))
{
*ptr = '\0';
vector = (char **)realloc(vector,((*count)+1)*sizeof(char *));
vector[*count] = strim(str);//等号前面部分去除空格后放入vector
str = ptr+1;//此时str指向等号后面部分(会继续查找后面的等号)
(*count)++;
if (--limit == 0) break;
}
if (*str != '\0')
{
vector = (char **)realloc(vector,((*count)+1)*sizeof(char *));
vector[*count] = strim(str);//分割空格
(*count)++;
}
return vector;
}
(4)strcasecmp函数是忽略大小写比较字符串,该函数用来比较配置文件的key值
定义函数 int strcasecmp (const char *s1, const char *s2);
函数说明 strcasecmp()用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异。
返回值 若参数s1和s2字符串相等则返回0。s1大于s2则返回大于0 的值,s1 小于s2 则返回小于0的值。
(5)strdup函数
extern char *strdup(char *s);
功 能: 将串拷贝到新建的位置处,strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏
一只简单的网络爬虫(基于linux C/C++)————配置文件设计及读取的更多相关文章
- 一只简单的网络爬虫(基于linux C/C++)————开篇
最近学习开发linux下的爬虫,主要是参考了该博客及其他一些网上的资料.网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息 ...
- 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型
Linux常用的并发模型 Linux 下设计并发网络程序,有典型的 Apache 模型( Process Per Connection ,简称 PPC ), TPC ( Thread Per Conn ...
- 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载
插件在软件设计中有很大的好处,可以方便地扩展各种功能,使用插件技术能够在分析.设计.开发.项目计划.协作生产和产品扩展等很多方面带来好处: (1)结构清晰.易于理解.由于借鉴了硬件总线的结构,而且各个 ...
- 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP
socket相关 建立连接 网络通信中少不了socket,该爬虫没有使用现成的一些库,而是自己封装了socket的相关操作,因为爬虫属于客户端,建立套接字和发起连接都封装在build_connect中 ...
- 一只简单的网络爬虫(基于linux C/C++)————守护进程
守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动, ...
- 一只简单的网络爬虫(基于linux C/C++)————读取命令行参数及日志宏设计
linux上面的程序刚开始启动的时候一般会从命令行获取某些参数,比如以守护进程运行啊什么的,典型的例子就是linux下的man,如下图所示 实现该功能可以使用getopt函数实现,该函数在头文件uni ...
- 一只简单的网络爬虫(基于linux C/C++)————利用正则表达式解析页面
我们向一个HTTP的服务器发送HTTP的请求后,服务器会返回可能一个HTML页面(当然也可以是其他的资源),我们可以利用返回的HTML页面,在其中寻找其他的Url,例如我们可以这样在浏览器上查看一下H ...
- 一只简单的网络爬虫(基于linux C/C++)————线程相关
爬虫里面采用了多线程的方式处理多个任务,以便支持并发的处理,把主函数那边算一个线程的话,加上一个DNS解析的线程,以及我们可以设置的max_job_num值,最多使用了1+1+max_job_num个 ...
- 一只简单的网络爬虫(基于linux C/C++)————Url处理以及使用libevent进行DNS解析
Url处理 爬虫里使用了两个数据结构来管理Url 下面的这个数据结构用来维护原始的Url,同时有一个原始Url的队列 //维护url原始字符串 typedef struct Surl { char * ...
随机推荐
- 单线程IP扫描解析
扫描代码: private void Button_Click(object sender, RoutedEventArgs e) { a5.Items.Clear(); string str = t ...
- kafka的基本体系结构
使用场景 大数据:数据量和速率激增,数据类型越来越复杂 应用开发:消息引擎,应用解耦,分布式存储,流处理 Kafka的体系结构 topic : 主题(消息的逻辑分类) 客户端: 细分为生产者(朝主题发 ...
- Linux c++ vim环境搭建系列(4)——vim插件安装配置使用
4. 插件 主要是c++相关的. ~/.vimrc文件在GitHub上有:https://github.com/whuwzp/vim_config 以下内容参考: https://github.com ...
- Angular input / ion-input ion-searchbar 实现软件盘换行 改 搜索 并且触发搜索方法 Android iOS适用
Angular 实现软件盘 换行 改 搜索 并且除非 搜索方法: Form 必须有 action="javascript: return true;” input / ion-in ...
- numpy basic sheatsheet
NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库.NumPy 通常与 SciPy(Scien ...
- 数据结构之循环队列Demo
循环队列 比较简单,循环队列主要是判断队满.队空.有效元素个数 画图说明: 假设:队的长度为5(0-4) 但是实际maxsize为6,需要一个预留空间(不存储元素)做计算 继续添加3个元素后: 出队一 ...
- 数据结构(C语言版)---栈
1.栈:仅在表尾进行插入和删除操作的线性表.后进先出LIFO. 1)表尾端(允许插入和删除的一端)为栈顶,表头端(不允许插入和删除的一端)为栈底. 2)入栈:插入元素的操作.出栈:删除栈顶元素 3)栈 ...
- file_put_contens小trick
file_put_contents tricks 0x01 trick1 来自于P神的实例: <?php $text = $_GET['text']; if(preg_match('[<& ...
- Unity 游戏框架搭建 2019 (三十二、三十三) 类的命名 & 代码文件命名
昨天我们完成了第八个示例的第二个 MenuItem 菜单顺序的调整. 我们今天再往下接着调整. 我们来看下接下来的 MenuItem 代码如下: [MenuItem("QFramework/ ...
- 最通俗易懂的Redis发布订阅及代码实战
发布订阅简介 除了使用List实现简单的消息队列功能以外,Redis还提供了发布订阅的消息机制.在这种机制下,消息发布者向指定频道(channel)发布消息,消息订阅者可以收到指定频道的消息,同一个频 ...