一只简单的网络爬虫(基于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 * ...
随机推荐
- Linux网络基础,路由的追踪
一.traceroute traceroute [-46ndFT] [-f<存活数值>] [-g<网关>] [-i(--interface)<device>] [- ...
- 20 java 基础回顾--中阶引入
一.数据类型 基本数据类型(共:四类八种) 整数 byte short int long 浮点 float double 字符 char 布尔 boolean 引用数据类型(new的数据) Stude ...
- ftl中几个特殊的用法
@ 注意${}为变量的渲染显示,即先计算后打印出来,而<>里面为定义等操作符的定义 ,而首尾2个<>中间部分一般为计算打印部分 @数据模型中如果不是以map数据来封装的,而是直 ...
- AJ学IOS(23)UI之控制器管理
AJ分享,必须精品 控制器以及view的多种创建方式 控制器view的加载 通过storyboard创建 1:先加载storyboard⽂件(Test是storyboard的⽂文件名) UIStory ...
- 【Java】封装、继承、多态
封装 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装.隐藏起来的方法. 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代 ...
- 分屏神器PoweToys
win+~调用设置分屏界面,shift+软件拖到分屏位置
- UML(续)
活动图 活动图定义 活动图描述了在一个过程中,顺序的/并行的活动及其之间的关系 应用于商业过程.工作流(业务过程).复杂算法的建模 活动图是顶点和弧的集合 活动节点 动作 流 对象值 注解和约束等 建 ...
- JMeter分布式压测-常见问题之(Server failed to start: java.rmi.server.ExportException: Listen failed on port: 0; nested exception )
问题描述: 在Linux环境启动jmeter-server时抛出了如下异常: 问题描述: 1.可能监听的端口被占用,修改端口号2.Server相关的rmi配置需要调整 解决方案: 在目录/apache ...
- Python 类学习的一些Tips
这里不详细介绍类,只总结一些小萌新在学习python 类时会有的一些疑点. 类的私有性 在python中,属性和方法的访问权限只有两种,公开的,和私有的.在给属性命名时用两个“__”下划线作为开头,就 ...
- 【mybatis xml】数据层框架应用--Mybatis 基于XML映射文件实现数据的CRUD
使用MyBatis框架进行持久层开发 MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架. MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索. MyBa ...