今天看啥
热点:

最近,按照boss要求,把文章全部down下来,一篇篇来肯定不行,所以弄了个脚本,批量抓取一下,要准确匹配文章体,踢出掉不用的垃圾标签,比如广告之类的,好好的研究了一下,

用snoopy+simple_html_dom这两个国外的php库整出一个批量下载文档的工具:


比如抓取 pcpop的 这篇文章:http://www.bkjia.com/jQuery/367100.html

结尾为:367100.html ,367100_1.shtml, 367100_2.shtml。。。一直到 367100_11.shtml
总共分了12页。保存的时候采用加一,这样存储后就变为bkjia_1.htm,bkjia_2.htm。。。bkjia_12.htm
方便读取。因为bkjia文档结构还算比较清晰,尤其是有html注释可以直接用正则提取出里面的文章体,其他内容可以舍掉。
正则条件:/<!--Content Begin-->([\s\S]*)<!--Content End-->/i
假如没有明显的这种注释,就没有办法使用正则表达式来匹配,比如你知道文章是在一个div里面:
<div id="article_body">文章内容</div>,这是没有办法使用正则匹配的,因为不知道结尾有多少个</div>,而且html标签里面大量的div,所以语义标签还是很重要,这种div无意义的标签加大搜索抓取难度了~~~
html5新增加了<article></article>标签,估计是为了方便搜索抓取。
现在得用html_dom分析办法来分析html文档,用类似jquery的办法提取出文档内容:
用simple_html_dom提供的方法:支持dom,这里我写了两个条件,假如没办法使用正则提取文章内容,就是用这个包提供的html文档分析提取,当然这种效率远远低于正则匹配,所以首先是正则。


simple_html_dom 下载:http://simplehtmldom.sourceforge.net/
用法就跟sourceforge上面的列子那样简单:

// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');

$html->find('div', 1)->class = 'bar';

$html->find('div[id=hello]', 0)->innertext = 'foo';

echo $html; // Output: <div id="hello">foo</div><div id="world" class="bar">World</div>
如果提取 discuz帖子:
每篇帖子保存一篇文章:
foreach($html->find('td.t_msgfont') as $e)
{
echo $e->innertext;
}

如果文档里的img用的是相对路径,则需要转换为绝对路径,不过如果图片禁止外链,那么就只有把图片下载到本地了,然后将图片的 img标签给替换掉。

include 'Snoopy.class.php';
include 'simple_html_dom.php';
$snoopy= new Snoopy;


function local_img($html,$pre_dir,$pre_im,$url_pre){
global $base_dir,$snoopy;

$img = str_get_html($html)->find('img');
$d_x = $base_dir . $pre_dir.'/images/';
if(!file_exists($d_x))
{
@mkdir($d_x,0777);
}
$i=1;
$code = gencode(4); //产生4为随机数,为防止图片重名。
foreach($img as $e)
{
$im_o = $im_url = $e->src ;
if($im_o)
{
if(strpos($im_url, 'http:')===false)
{
$im_url = substr($url_pre,0,strpos($url_pre,'/')) .'/' .$im_url;
}
$snoopy->fetch($im_url);
$im_ext = substr($im_o,strrpos($im_o,'.')+1);
$rand_str= time() .'_'.$code .'_'.$i;
$im_name = $pre_im . '_'.$rand_str .'.'.$im_ext ;
$re_name = 'articles/'.$pre_dir.'/images/'.$im_name;
$im_file = $d_x .$im_name;
file_put_contents($im_file,$snoopy->results);
$html=eregi_replace('<img([^>]*)src="'. $im_o .'"([^>]*)>','<p class="oppo_image"><img src="'.$re_name.'" alt="'.$re_name.'" /></p>' ,$html);
echo '下载图片:--' ,$im_o ,'--------',$re_name,'-------成功 <br>';
$i++;
}
}
return $html;
}

如果图片的路径比较特别 比如:
<img src="images/001.jpg" />
<img src="../images/001.jpg" />
<img src="/images/001.jpg" />
<img src="http://files.image.com/images/001.jpg" />
绝对路径最好,直接提取然后下载图片,如果图片是相对路径,要根据具体情况转换为绝对url路径,比较麻烦。

discuz帖子里面的 img标签就更加猥琐了,因为src不是真实的地址:

<img src="images/common/none.gif" file="uploadfile/month_0908/20090824_3464fbbfd184fcd2e2c36VRqCv7TXhrt.jpg" thumbImg="1" id="aimg_307464" onmouseover="showMenu(this.id, false, 2)" alt="DSCN9892.jpg" />

file才是真实的地址,正则只需做小小的改动就可以。

批量抓取网页的效果:为了防止超时,每次处理完一个网页后,用js跳转到下一个网页,直到 其始编号不再小于结束编号。
pcpop的地址还算比较规则,抓取起来相对容易。一次就可以批量搞定。

不过sina的地址就不友好了:

http://tech.sina.com.cn/mobile/n/2009-08-17/15223358619.shtml
文章的分页这样:
15223358619.shtml ,15223358619_2.shtml ,15223358619_3.shtml 。。。。15223358619_6.shtml
sina把第一页的1省略掉了,针对sina做个特别处理。
输入起始地址:15223358619.shtml 。
开始自增为 1,结束6,
当自增变量为1时,不做链接,直接用原始地址下载,i>1后,正常连接。
sina的文章结构就不清晰,不好提取。有个:
<!-- 正文内容 begin -->
这里是文章开始,却没加结束注释。
不过结束处有:
<!--wapdump begin-->
也可用来写正则,里面的 分页什么的要统统过滤掉。
script和style的标签过滤,要过滤里面的东西,所以 strip_tags是没有 用的,过滤后会出现赤裸裸的代码
这就是为什么现在那么多文章采集网站里面会突然多出很多css源码,没过滤干净,哈哈。

function strip_selected_tags($text, $tags = array())
{
$args = func_get_args();
$text = array_shift($args);
$tags = func_num_args() > 2 ? array_diff($args,array($text)) : (array)$tags;
foreach ($tags as $tag){
if(preg_match_all('/<'.$tag.'[^>]*>([\s\S]*)<\/'.$tag.'>/iU', $text, $found)){
if($tag=='script' || $tag=='style'){
$text = str_replace($found[0],'',$text);
}else{
$text = str_replace($found[0],$found[1],$text);
}
}
}

return $text;
}

评论暂时关闭