[爬虫] URL提取
来源:互联网 发布:知峰竹纤维内衣 编辑:程序博客网 时间:2024/05/23 17:11
参考 论文: 一种可扩展的高效链接提取模型的实现与验证
作为一个爬虫,最基本的便是能够从各个页面中提取URL,这里介绍我实现的提取器。基于可扩展性,我首先实现了一个不针对任何元素的HTML元素提取,即可以提取所有的元素,并通过HOOK的模型,再根据具体需要实现不同的HOOK来获得针对性的信息。当然爬虫的HOOK便是获取URL。
HOOK的接口大致如下:
// [消息通知接口]
void begin_parser()
void begin_element()
void end_parser()
void end_element()
// [具体处理接口]
void get_element_name(...)
void get_attr_name(...)
void get_attr_value(...)
基础提取器的过程大致如下:
获得元素名 hook.get_element_name(...)
获得属性名 hook.get_attr_name(...)
获得属性值 hook.get_attr_value(...)
获得属性名 hook.get_attr_name(...)
获得属性值 hook.get_attr_value(...)
……
如此循环直到分析完页面。
由于考虑了可扩展性,或多火或少的会对性能有些影响,比如我们要实现一个完全针对性的URL分析,则可以减少多次的元素,属性等的提取,此过程还有多次无意义的字符判断。因此我改变了 hook 的接口,把 get_element_name() 的返回值设置为 bool 值,这样提取器便可以通过hook的接口得到不同的hook程序对不同的元素信息的关心情况,做出适当的处理,在我的实现中,该函数返回true,表示关心该元素的情况,false反之,如果是不关心的话,提取器直接跳到下一个元素进行分析,这样无疑提高了分析器的性能,使得它同针对性的提取器性能差距缩小。还有上面的begin_parser() begin_element()等通知函数是为了让不同的hook能知道当前提取器的工作情况,以方便处理获得的数据信息。
不过在页面分析前,有必要对页面数据进行一次预处理,比如爬虫时对脚本,注释的过滤,因此我在提取器中加入一个HTML过滤器,可根据不同需要来设置不同的过滤器,在本爬虫中,我派生一个过滤器 html_filter 用于把脚本和注释进行过滤。
我的爬虫中的URL提取器大致就是这样,下面是部分代码 :
加入这两个宏纯粹是为了代码紧凑些。一个用于判断是否分析到数据流的结尾,另一个加入多少有点SB,如果flag为真,返回ret_code。HOHO
#define return_if_end(rs) do{ if(rs == rs_end_of_file) return rs_end_of_file;}while(0)
#define return_if(flag,ret_code) do{ if(flag) return ret_code;}while(0)
const string cauc_html_parser::m_brank = " ";
void cauc_html_parser::set_stream(istream& is)
{
this->istream_it = istream::_Iter(is);
}
bool cauc_html_parser::handle_by_hook(Command cmd)
{
if(!m_hook)
return false;
switch(cmd)
{
case cm_element_name:
return m_hook->get_element_name(get_cur_word(1));
break;
case cm_attr_name:
return m_hook->get_attr_name(get_cur_word(1));
break;
case cm_attr_value:
{
return m_hook->get_attr_value(get_cur_word(1));
}
break;
case cm_text:
return m_hook->get_text(get_cur_word(1));
break;
}
return true;
}
cauc_html_parser::Result cauc_html_parser::ignored_brank()
{
while( m_brank.find(get_cur_letter()) != string::npos)
return_if_end(advance());
return rs_ok;
}
cauc_html_parser::Result cauc_html_parser::advance()
{
istream_it++;
if(istream_it != istream_end)
{
m_cur_letter = *istream_it;
m_curWord += *istream_it;
return rs_ok;
}
return rs_end_of_file;
}
void cauc_html_parser::set_hook(cauc_hook* hook,bool del)
{
m_delete_hook = del;
m_hook = hook;
}
cauc_html_parser::Result cauc_html_parser::advance_to(const string& split)
{
do
{
return_if_end(advance());
}while(split.find(get_cur_letter()) == string::npos);
return rs_ok;
}
bool cauc_html_parser::is_quot()
{
return is_oneof("'/"");
}
bool cauc_html_parser::is_oneof(const string& flag)
{
const string& lt = get_cur_letter();
return flag.find(lt) != string::npos;
}
string& cauc_html_parser::get_cur_word(size_t back_offset)
{
if(back_offset && back_offset < m_curWord.length())
m_curWord = m_curWord.substr(0,m_curWord.length() - back_offset);
return m_curWord;
}
void cauc_html_parser::begin_word()
{
m_curWord = "";
m_curWord += get_cur_letter();
}
string cauc_html_parser::get_cur_letter()
{
return m_cur_letter;
}
void cauc_html_parser::parser()
{
/*
* [通知hook程序开始分析网页]
**/
if(m_hook)
m_hook->begin_parser();
while(this->get_next() != rs_end_of_file)
{
this->get_text();
}
/*
* [通知 hook 程序结束分析网页]
**/
if(m_hook)
m_hook->end_parser();
}
cauc_html_parser::Result cauc_html_parser::get_next()
{
/*
* [通知 hook 程序 开始获取元素]
**/
if(m_hook)
m_hook->begin_element();
return_if_end(ignored_brank());
/*
* [获得元素名]
**/
if(! is_oneof("<") )
return_if_end(advance_to("<"));
return_if_end(advance());
return_if_end(ignored_brank());
begin_word();
return_if_end(advance_to(">"+m_brank));
/*
* [hook处理]
**/
return_if(!handle_by_hook(cm_element_name),rs_ok);
return_if_end(ignored_brank());
if(is_oneof(">"))
return_if_end(advance());
/*
* [获得属性信息]
**/
do {
return_if_end(get_attribute());
return_if_end(ignored_brank());
} while(!is_oneof(">"));
/*
* [通知 hook 程序 结束获取元素]
**/
if(m_hook)
m_hook->end_element();
return_if_end(advance());
return rs_ok;
}
/*
* [属性]
**/
cauc_html_parser::Result cauc_html_parser::get_attribute()
{
do {// [属性名]
return_if_end(ignored_brank());
return_if_end(get_attr_name());
} while(!is_oneof("="));
return_if(is_oneof(">"),rs_ok); // [如果是'>'说明后面没有属性值]
return_if_end(advance()); // [跳过'=']
return_if_end(get_attr_value()); // [获得属性值]
return_if_end(ignored_brank()); // [跳过空格]
return rs_ok;
}
/*
* [获取属性名]
**/
cauc_html_parser::Result cauc_html_parser::get_attr_name()
{
begin_word();
return_if_end(advance_to(m_brank + ">="));
/*
* [hook处理]
**/
return_if(!handle_by_hook(cm_attr_name),rs_ok);
return rs_ok;
}
/*
* [获取属性值]
**/
cauc_html_parser::Result cauc_html_parser::get_attr_value()
{
return_if_end( ignored_brank() );
if(is_oneof("'/""))
{
string quot = get_cur_letter();
return_if_end( advance() );
begin_word();
if(!is_oneof("'/""))
{
return_if_end( advance_to(quot) );
}
/*
* [hook处理]
**/
return_if(!handle_by_hook(cm_attr_value),rs_ok);
return_if_end(advance());
}
else
{
begin_word() ;
if(!is_oneof(">"))
return_if_end( advance_to( m_brank + ">") );
/*
* [hook处理]
**/
return_if(!handle_by_hook(cm_attr_value),rs_ok);
}
return rs_ok;
}
/*
* [获取正文]
**/
cauc_html_parser::Result cauc_html_parser::get_text()
{
begin_word() ;
if(!is_oneof("<"))
return_if_end( advance_to("<") );
handle_by_hook(cm_text);
return rs_ok;
}
下面是 html_filter 的实现,实现过程没有回溯,因此数据流只要具有前向迭代器功能皆可。
不过有一点现在觉得浪费了很多的时间,就是先把数据流全部过滤后得到新的数据流再交给提取器处理,由于原先认为这样使结构比较清晰,不至于把提取器实现得太过于……(恶劣)。 但是最近由于调试时发现提取器花费时间挺长的,便重新考虑了一下,有了新的模型,当然是基于把过滤器象hook处理程序一样挂到基础的提取器中,减少遍历数据一次。 当然还没实现()。下次再更新……
//[ 5月5号 更新]
今天把提取器重新实现了一下,就是完成了之前的想发,对大于100K的页面提取速度提高将近一倍……:)。
其中过滤器接口是:
void filter(istream::__Iter& start, const istream::_Iter& end, TypeBuffer& buffer);
实现要求: 如果从流中读取了字符,且不属于过滤范围,则把读取的字符按流中的顺序保存到 buffer 中。过滤后 start 迭代器的位置为紧接着过滤掉的最后一个位置。end 参数是用于标记过滤范围,一般而言是流的结束标记。即 istream::_Iter()默认的构造对象。
过滤器在提取器中在两个地方使用,一个是一开始分析页面时调用,一个是在 advance()中调用。
在更新的提取器中我暂时使用 std::deque<char> 作为缓冲,即保存那些被过滤器错误读取的字符,然后在advance() 函数中做相应的更改即可。(先判断缓冲中是否存在字符)
还加入了一个辅助结构,用于处理消息通知,这里主要是 begin_parser ,begin_element...,他们一般在函数开始和函数结束的时候调用,但是由于函数中间有可能有多个 return ,因此会导致一些错误或者不匹配的通知。于是我想到了临时对象的特点,他会在生命周期结束时调用他的析构函数,这正好可以用来处理我的情况,于是该结构如下:
struct notify_helper
{
enum notify_flag
{
nf_parser = 0,
nf_element
};
notify_helper(hook*& hook,notify_flag flag)
: m_hook(hook), m_flag(flag)
{
if(!m_hook)
return;
switch(m_flag)
{
case nf_parser:
m_hook->begin_parser(); break;
case nf_element:
m_hook->begin_element(); break;
}
}
~notify_helper()
{
if(!m_hook)
return;
switch(m_flag)
{
case nf_parser:
m_hook->end_parser(); break;
case nf_element:
m_hook->end_element(); break;
}
}
notify_flag m_flag;
hook*& m_hook;
};
然后在函数开始的时候,比如开始分析的函数中,生成一个对象即可:
{
notify_helper helper(m_hook, notify_helper::nf_parser);
……
}
这样不管函数中有多少个return 都无所谓,因为只要函数结束,该对象生命器结束,自动会调用析构函数。
待写...
- [爬虫] URL提取
- python-获取提取网页url爬虫学习(1)
- (1)php爬虫---提取优酷动漫的url
- url提取方法
- url正则表达式提取
- 文本提取url
- 文本提取url
- php提取网页url
- NSString 中提取URL
- 提取网页中的URL
- url 中提取数据
- 网路爬虫--url索引
- 搜索引擎爬虫,抓取url
- 网页爬虫抓取URL
- url 爬虫 1
- Python 爬虫APP URL
- python爬虫(中)--提取
- 提取 URL 的正则表达式
- 测试贴
- 天下搜索,又一个3721?
- 我的五一生活(一)
- 如何用正确的方法来写出质量好的软件的75条体会 (转载)
- 汤姆·霍尔的快速成为游戏开发者的秘诀
- [爬虫] URL提取
- 钱转大写
- THC温湿度测控系统---系统分析与设计
- 人与钱
- win2003+php5安装说明
- 统计下先....
- 记住一句话
- 制作自己的Win XP SP2光盘
- 透过局域网架设Internet FTP服务器