小兔网

由于需要一些新闻的信息,比如标题、时间、作者、内容和图片等信息。之前听说过数据采集,所以想尝试一下,结果花了大半天的时间才基本上完成了。当然了,bug肯定还是有的。

下面介绍我实现新闻采集的过程:

首先,肯定要找一个新闻网站,我选择的是中国新闻网,最好是选择有新闻列表的页面(比如:http://www.chinanews.com/scroll-news/gn/2015/1021/news.shtml)。选择有新闻列表的页面,可以抓取多个页面的url。然后通过这些url去获取详细页面的标题、时间等信息。

然后,获取新闻列表的url。先要分析该页面,通过字符串函数(substr、strpos等)截取列表部分;再通过正则表达式获取各个子页面的连接。

最后,分析子页面的结构,通过字符串函数截取包括标题(时间等)的代码,通过正则表达式得到想要的信息。其中包括将数据插入数据库以及下载相应的图片到本地。

下面附上我的代码,里面有详细的说明。

<?php
set_time_limit(0);
header("Content-Type:text/html;charset=utf-8");
$str = file_get_contents("http://www.chinanews.com/scroll-news/gn/2015/1021/news.shtml");

//用字符串函数实现,具体见 function sub_string($str,$start,$end)
//截取父页面列表的开头和结尾
$start = '<div class="content_list">';
$end = '<div id="footerAd"';
//得到的结果
$need = sub_string($str,$start,$end);


$re = '/<a.*href=("|\')(.+)\1.*>.+<\/a>/U';
// U:非贪婪匹配 , \1反向引用第一个括号中的内容

//$a是正则匹配获取的全部内容
//$a[0] 是获取的每一个匹配
//$a[1] 是第一个匹配子组(对应的是("|\'))
//$a[2] 是第二个匹配子组(对应的就是(.+))
preg_match_all($re, $need, $a);

// 连接数据库
$link = mysql_connect("localhost:3306","root","root");
if(!$link)
die("连接数据库失败");
if(!mysql_select_db("rain"))
die("选择数据库失败");

foreach ($a[2] as $value) {
//暂时只发现了连接的两种情况
//1.没有主域名,如:/gn/2015/11-07/7610858.shtml
if(strpos($value,"http://www.chinanews.com/") === false)
{
//这里是用“.”,刚开始用的加号,一直没找出来问题
$newvalue = "http://www.chinanews.com" . $value;

//获取子页面
$sub_str = file_get_contents("{$newvalue}");
}

//2.完整url连接,如:http://www.chinanews.com/gn/2015/11-07/7610858.shtml
if (preg_match("/^http:\/\/www\.chinanews\.com\/[a-z]{2}\/\d{4}.*/", $value) )
{
//获取子页面
$sub_str = file_get_contents("{$value}");
}

//将整个字符串转码为utf-8
$sub_str = iconv("GBK", "UTF-8", $sub_str);

//获取标题
$title = sub_title($sub_str) ;

//作者
preg_match('/<a.*>(.*)<\/a>/', sub_author($sub_str),$sub_a);
$author = $sub_a[1];

//内容
$content = sub_content($sub_str);
$content = mysql_escape_string($content);

//日期
$time = sub_time($sub_str);
//将中文 年 月 换成 -
preg_match('/\d{4}[\x{4e00}-\x{9fa5}]\d{2}[\x{4e00}-\x{9fa5}]\d{2}/u', $time,$sub_a);
$time = preg_replace("/[\x{4e00}-\x{9fa5}]/u", "-", $sub_a[0]);

//日期
$picture = sub_picture($sub_str);
if(isset($picture))
{
preg_match('/<img.*src=("|\')(.*)\1.*alt.*\/>/', $picture,$pic);
$picture = $pic[2];
//获取图片名称
$pic_name = substr($picture, strrpos($picture, "/")+1) ;

//存放图片的路径
$path = "./";
//写入图片
file_put_contents($path . $pic_name, file_get_contents($picture));
//更改存入数据库的文件名
$picture = $pic_name;
}

//插入语句
$sql = "insert into rain_news values(null,1,1,'{$title}','{$time}','{$picture}','{$author}','{$content}')";

//如果出错,显示错误信息
if(!mysql_query($sql))
{
echo mysql_error();
echo "<hr>";
}
}


//在原字符串中截取指定开头和结尾间的内容返回
function sub_string($str,$start,$end)
{
$s = strpos($str, $start);
$e = strpos($str, $end);
$need = substr($str, $s , $e - $s);
return $need;
}
//以下函数只适应中新网的新闻详细页面的信息获取
//截取标题
function sub_title($str)
{
$start = '<h1 style="display:block; position:relative; text-align:center; width:624px;clear:both">';
$end = '</h1>';
$s = strpos($str, $start) + strlen($start);
$e = strpos($str, $end);
return substr($str, $s , $e - $s);
}//<span id="author_baidu">作者:

//截取作者
function sub_author($str)
{
$start = '<span id="source_baidu">';
$end = '<span id="author_baidu">';
$s = strpos($str, $start) + strlen($start);
$e = strpos($str, $end);
return substr($str, $s , $e - $s);
}

//获取新闻内容
function sub_content($str)
{
$start = '<div class="left_zw" style="position:relative">';
$end = '<table border=0 cellspacing=0 cellpadding=0 align=left style="padding-right:10px;">';
$s = strpos($str, $start) + strlen($start);
$e = strpos($str, $end);
return substr($str, $s , $e - $s);
}
//获取时间
function sub_time($str)
{
$start = '<div class="left-t" style="padding-left:6px;">';
$end = '<div class="bshare-custom" style="width:185px;">';
$s = strpos($str, $start) + strlen($start);
$e = strpos($str, $end);
return substr($str, $s , $e - $s);
}

//获取图片信息
function sub_picture($str)
{
$start = '<div class=left_ph>';
$end = '<div class=left_pt>';
$s = strpos($str, $start) + strlen($start);
$e = strpos($str, $end);
if($e === false)
{
return null;
}
return substr($str, $s , $e - $s);
}