一只简单的网络爬虫(基于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 * ...
随机推荐
- mybatis源码分析--如何加载配置及初始化
简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository ...
- Array(数组)对象-->数组的删除
1.数组的删除: 用delete操作符删除特定的元素 删除元素的位置只是被留空了,为undefined值 举例:删除下面数组中的第二个元素 var arr = [1,2,3,4,5]; /*删除第二个 ...
- NumPy学习2:创建数组
1.使用array创建数组 b = array([2, 3, 4])print bprint b.dtype 2.把序列转化为数组 b = array( [ (1.5,2,3), (4,5,6) ] ...
- 使用docker搭建selenium grid 分布式环境
本文章只做docker搭建selenium grid 分布式环境步骤说明,对于selenium grid中的参数.流程.原理等不做说明.selenium grid的详细情况可查看官方文档https:/ ...
- Google 浏览器 离线包下载方式
最近因工作需要,需要安装google浏览器,并且安装在系统固定目录,用360软件管理下载后发现默认安装在C:\Users\administrator\AppData\Local\Google\Chro ...
- posix系统线程调度-设置线程优先级
#include <thread> #include <mutex> #include <iostream> #include <chrono> #in ...
- 21-Java-Hibernate框架(一)
一.Hibernate了解 Hibernate框架是Java持久层的框架,是Gavin King发明的,2001年发布的,JBoss公司的产品,2003年进入市场. Hibernate是基于对象来操作 ...
- 图2-4TCP状态转换图
这张图中描述了TCP中11个状态的转换. 光看这个图我一开始是蒙蔽的,也没有写服务器和客户端的操作流程.不过回头一想,是状态转换,也就是从一个状态到另外一个状态发生了什么,什么条件触发的.这样是不是好 ...
- SQL语句加锁分析
背景 MySQL中SQL加锁的情况十分复杂,不同隔离级别.不同索引类型.索引是否命中的SQL加锁各不相同. 然而在分析死锁过程当中,熟知各种情况的SQL加锁是分析死锁的关键,因此需要将MySQL的各种 ...
- JavaScript的运行机制!!!很重要很重要!!!!!!请看大神操作!
https://juejin.im/post/59e85eebf265da430d571f89