Spider又叫WebCrawler或者Robot,是一个沿着链接漫游Web 文档集合的程序。它一般驻留在服务器上,通过给定的一些URL,利用HTTP等标准协议读取相应文档,然后以文档中包括的所有未访问过的URL作为新的起点,继续进行漫游,直到没有满足条件的新URL为止。WebCrawler的主要功能是自动从Internet上的各Web 站点抓取Web文档并从该Web文档中提取一些信息来描述该Web文档,为搜索引擎站点的数据库服务器追加和更新数据提供原始数据,这些数据包括标题、长度、文件建立时间、HTML文件中的各种链接数目等
1. 搜索策略
① IP 地址搜索策略
先赋予爬虫一个起始的IP地址,然后根据IP地址递增的方式搜索本IP地址段后的每一个WWW地址中的文档,它完全不考虑各文档中指向其它Web 站点的超级链接地址。优点是搜索全面,能够发现那些没被其它文档引用的新文档的信息源;缺点是不适合大规模搜索。
② 深度优先搜索策略
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。优点是能遍历一个Web 站点或深层嵌套的文档集合;缺点是因为Web结构相当深,,有可能造成一旦进去,再也出不来的情况发生。
③ 宽度优先搜索策略
在宽度优先搜索中,先搜索完一个Web 页面中所有的超级链接,然后再继续搜索下一层, 直到底层为止。例如,一个HTML 文件中有三个超链,选择其中之一并处理相应的HTML文件,然后不再选择第二个HTML文件中的任何超链, 而是返回并选择第二个超链,处理相应的HTML文件,再返回,选择第三个超链并处理相应的HTML文件。一旦一层上的所有超链都己被选择过,就可以开始在刚才处理过的HIML 文件中搜索其余的超链。这就保证了对浅层的首先处理。当遇到一个无穷尽的深层分支时,不会导致陷进WWW 中的深层文档中出现出不来的情况发生。宽度优先搜索策略还有一个优点,即它能在两个HTML文件之间找到最短路径。宽度优先搜索策略通常是实现爬虫的最佳策略,因为它容易实现,而且具备大多数期望的功能。但是如果要遍历一个指定的站点或者深层嵌套的HTML文件集,用宽度优先搜索策略则需要花费比较长的时间才能到达深层的HTML文件。综合考虑以上几种策略和国内信息导航系统搜索信息的特点,国内一般采用以宽度优先搜索策略为主、线性搜索策略为辅的搜索策略。对于某些不被引用的或很少被引用的HTML文件,宽度优先搜索策略可能会遗漏这些孤立的信息源,可以用线性搜索策略作为它的补充。
④ 专业搜索引擎的爬虫策略
目前,专业搜索引擎网络爬虫通常采用“最好优先”原则访问WEB,即为快速、有效地获得更多的与主题相关的页面(简称“回报”),每次选择“最有价值”的链接进行访问。由于链接包含于页面之中,而通常具有较高价值的页面包含的链接也具有较高的价值,因而对链接价值的评价有时也转换为对页面价值的评价。
⑤ 爬虫的设计中应该注意的问题
第一个问题是URL地址的标准化:在WWW上,一个URL地址可以有多种表示方法,可以用IP地址表示,也可以用域名来表示。为了避免爬虫重复访问同一地址。第二个问题是避免掉进网络陷阱:网络上的链接情况比较复杂,一些静态的网页可能构成闭环回路。为了避免爬虫在一条循环路线上反复抓取,在把URL加入待搜索地址列表之前都要检查是否已在待搜索的地址列表中出现过。对于动态网页,爬虫应该忽略所有带参数的URL。第三个问题:对于拒绝访问的页面,爬虫应该遵从“漫游拒绝访问规则”。
CREATE TABLE `grab_history` ( `id` bigint(20) NOT NULL auto_increment, `file_url` varchar(1024) default NULL, `url_md5` varchar(40) default NULL, PRIMARY KEY (`id`), KEY `file_url_md5` (`url_md5`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='grab history'; CREATE TABLE `pic_gallery` ( `id` int(11) NOT NULL auto_increment, `file_url_md5` varchar(100) NOT NULL, `file_url` varchar(1024) default NULL, `file_data` longblob, `file_type` varchar(100) default NULL, `file_name` varchar(255) default NULL, `file_size` int(11) default NULL, PRIMARY KEY (`id`), KEY `file_url_md5` (`file_url_md5`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `web_page` ( `id` bigint(20) NOT NULL auto_increment, `page_url_md5` varchar(100) NOT NULL, `page_url` varchar(1024) default NULL, `page_content` longblob, `page_pic` varchar(1024) default NULL, `page_length` int(11) default NULL, `grab_time` datetime default NULL, PRIMARY KEY (`id`), KEY `page_url_md5` (`page_url_md5`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='网络爬虫数据存储表';
抓取内容插入数据库 function put_web_page($url, $content, $filesize) { $sql = "insert into web_page (page_url_md5, page_url, page_content, page_length, grab_time) values ('".md5($url)."','$url','$content',$filesize,'".date("Y-m-d H:i:s", time())."')"; } 抓取图片内容插入数据库 function put_web_pic($url, $content, $filename, $filesize, $ref_page) { $sql = "insert into pic_gallery (file_url_md5, file_url, file_data, file_name, file_size) values ('".md5($url)."','$url','$content','$filename',$filesize)"; } 抓取url历史插入数据库 function add_history($url, $md5) { $sql = "insert into grab_history (file_url, url_md5) values ('$url', '$md5');"; } function get_grab_history(&$oldhistory, $subkey) { $sql = "select id, file_url,url_md5 from grab_history where url_md5 like '$subkey%'"; $result = mysql_query($sql); $num = mysql_num_rows($result); $i; for ($i = 0; $i < $num; $i++) { $url = mysql_result($result, $i, 1); $md5 = mysql_result($result, $i, 2); //$oldhistory[$url] = $url; $oldhistory[$md5] = $url; //if (count($oldhistory) % 1000 == 0) { // $id = count($oldhistory); // echo("the size of history is $id!\n"); //} } }
计算网页中的url路径信息
class web_site_info { var $web_site; var $web_dir; var $web_filename; //解析URL function web_site_info($url) { $temp = ereg_replace("http://", "", $url); $sp = split('[/]', $temp); $sc = count($sp); $i; echo("$url\n"); $this->web_site = $sp[0]; if ($sc == 1) { return; } if ($sc == 2) { $this->web_filename = $sp[1]; }else { for ($i = 1; $i < $sc -1; $i++) { if ($i > 1) { $this->web_dir = $this->web_dir . "/"; } $this->web_dir = $this->web_dir . $sp[$i]; } $this->web_filename = $sp[$sc-1]; } } //分析网页中的url,构建正确的url function calc_path($url_path) { $ret = ""; $temp = ""; $url = trim($url_path); $pos = strncmp($url, "http://", 7); if ($pos == 0) { return $url; } $pos = strncmp($url, "../", 3); if ($pos == 0) { $ret = $this->web_site ."/" .$this->web_dir; $ret = dirname($ret); $ret = "http://" .$ret ."/"; $temp = ereg_replace("../", $ret, $url); return $temp; } $pos = strncmp($url, "./", 2); if ($pos == 0) { $ret = "http://" .$this->web_site ."/"; if (strlen($this->web_dir) > 0) $ret = $ret .$this->web_dir ."/"; $temp = ereg_replace("./", $ret, $url); return $temp; } $pos = strncmp($url, "/", 1); if ($pos == 0) { $ret = "http://" .$this->web_site .$url; return $ret; } $ret = "http://" .$this->web_site ."/"; if (strlen($this->web_dir) > 0) { $ret = $ret .$this->web_dir ."/"; } $ret = $ret .$url; return $ret; } //获取url中的路径名 function get_save_path() { $ret; if (strlen($this->web_dir)) { $ret = $this->web_site."\\".$this->web_dir; }else { $ret = $this->web_site; } $ret = ereg_replace("/", "\\", $ret); return $ret; } //获取url中的文件名 function get_save_filename() { $ret = $this->get_save_path(); if (strlen($this->web_filename) == 0) { $ret = $ret."\\index.html"; }else { $ret = $ret ."\\".$this->web_filename; } $ret = ereg_replace("/", "\\", $ret); return $ret; } }
抓取网页
<?php class web_crawl_job { var $m_level; var $m_url; var $url_info; var $sub_job; var $page_images; var $dbc; var $m_max_deep; function get_save_filename() { return $this->url_info->get_save_filename(); } function sub_job_count() { return count($this->sub_job); } function do_sub_job() { $i; global $global_download; //计算总任务数 $count = count($this->sub_job); for ($i = 0; $i < $count; $i++) { $url = $this->url_info->calc_path($this->sub_job[$i]); if ($global_download->have_key($url)) { echo "have downloaded: ".$url ."\n"; continue; } $pos = strpos($url, "http://"); if ($pos === false) { echo "error url: $url\n"; }else { //$global_download->add_key($url); $sub = new web_crawl_job($url, $this->m_level + 1, $this->dbc, $this->m_max_deep); unset($sub); } sleep(2); } } //下载图片 function down_page_pic() { $i; global $global_download; $count = count($this->page_images); for ($i = 0; $i < $count; $i++) { $url = $this->url_info->calc_path($this->page_images[$i]); if ($global_download->have_key($url)) { echo "have downloaded: ".$url ."\n"; continue; } $pos = strpos($url, "http://"); if ($pos === false) { echo "error image url: $url\n"; }else { echo $url."\n"; //$global_download[$url] = $url; $global_download->add_key($url); $content = file_get_contents($url); if (strlen($content) == 0) { continue; } if (strlen($content) == 0) { $retry_count = 0; while (strlen($content) == 0) { if ($retry_count++ >= 2) { break; } sleep(1); $content = file_get_contents($url); } } $this->dbc->put_web_pic($url, addslashes($content), basename($url), strlen($content), $this->m_url); } } } function web_crawl_job($url, $level, $db_in_op, $m_dp) { $this->m_level = $level; $this->m_url = $url; $this->url_info = new web_site_info($url); $this->dbc = $db_in_op; $this->m_max_deep = $m_dp; global $global_download; echo "My Level is:" .$level ."\n"; //检查内容是否为空 $content = file_get_contents($url); if (strlen($content) == 0) { $retry_count = 0; while(strlen($content) == 0) { sleep(10); $content = file_get_contents($url); $retry_count = $retry_count + 1; if ($retry_count > 3) { $global_download->add_key($url); return; } } } $md5_str; //取出页面中的url放到任务里面 //分析链接 $reg = "#<a[^>]+href=(['\"])(.+)\\1#isU"; preg_match_all($reg, $content, $m); foreach($m[2] as $src) { $this->sub_job[] = $src; } //分析图片 $reg = "#<img[^>]+src=(['\"])(.+)\\1#isU"; preg_match_all($reg, $content, $m); foreach($m[2] as $src) { $this->page_images[] = $src; } $db_in_op->put_web_page($url, addslashes($content), strlen($content)); $this->down_page_pic(); $global_download->add_key($url); if ($this->m_level < $this->m_max_deep and count($this->sub_job) > 0) { //执行子任务 $this->do_sub_job(); } } } ?> //检查链接是否被抓取过 class web_grab_history { var $m_db_op; var $m_oldhistory; var $m_newhistory; var $m_subkey; function __construct($db_op) { $this->m_db_op = $db_op; //$db_op->get_grab_history($this->m_oldhistory); } //保存链接历史 function save_history() { foreach($this->m_newhistory as $md5 => $url) { $this->m_db_op->add_history($url, $md5); } } //从数据库查询出链接历史 function load_subkey($subkey) { $this->m_subkey[$subkey] = $subkey; $this->m_db_op->get_grab_history($this->m_oldhistory, $subkey); } function __destruct() {} function have_key($url) { $ret = false; $md5 = md5($url); $subkey = $md5[0] .$md5[1] .$md5[2]; if (strstr($url, "rar") > 0) { return true; } //先从内存中查询链接 if (count($this->m_subkey) > 0 && array_key_exists($subkey, $this->m_subkey) == true) { }else { $this->load_subkey($subkey); } if (count($this->m_oldhistory)) { //$ret |= array_key_exists($url, $this->m_oldhistory); $ret |= array_key_exists($md5, $this->m_oldhistory); } if ($ret == true) { return $ret; } if (count($this->m_newhistory)) { //$ret |= array_key_exists($url, $this->m_newhistory); $ret |= array_key_exists($md5, $this->m_newhistory); } return $ret; } //添加链接历史 function add_key($url) { $md5 = md5($url); //$this->m_newhistory[$url] =$url; $this->m_newhistory[$md5] = $url; if (count($this->m_newhistory) > 400) { // } } }
//入口 function print_use() { echo "Usage:\nphp -f deepth spider.php url\n"; } if ($argc == 1) { print_use(); die; } $global_grab_deep = (int)$argv[1]; $url = $argv[2]; $db_op = new my_insert(); //获取抓取DB链接 $global_download = new web_grab_history($db_op); //开始抓取 $tt = new web_crawl_job($url, 1, $db_op, $global_grab_deep); //保存抓取历史 $global_download->save_history(); echo "Mission Complished!\n";