抓取一个网页内容非常容易,常见的方式有curl、file_get_contents、socket以及文件操作函数file、fopen等。
下面使用SOCKET下的fsockopen()函数访问Web服务器最常用的80端口,通过获取80端口的数据,并进行分析,来模拟网络爬虫的工作方法。
1、实现SOCKET模拟网络爬虫主要包括以下几个部分:
- 使用SOCKET获取指定页的内容。
- 使用get_meta_tags()函数分析网页的META、TITLE等标签内容。
- 解析TITLE、链接或网页内容,可以使用正则表达式来取得需要的内容。
SOCKET爬虫实现代码,完整代码如下:
简单爬虫实现代码和使用方法
class Spider
{
private $_url = "";//定义用于保存URL的变量
private $_sites = "";//定义用于保存网站相关内容的变量
/**
* 构造函数,用于初始化变量
* @param $url
*/
public function __construct($url)
{
$this->_url = $url;
}
/**
* 开始爬页面
*/
public function start()
{
$content = $this->socketOpen($this->_url);//使用socketOpen()方法链接指定的服务器
$this->_sites["meta"] = $this->getMeta($content);//使用getMeta()方法获取meta信息
$this->_sites["title"] = $this->getTitle($content);//使用getTitle()方法获取title信息
$this->_sites["detail"] = $this->getDetail($content);//使用getDetail()方法获取内容信息
$this->_sites["links"] = $this->getLinks($content);//使用getLinks()方法获取内容链接信息
}
/**
* 获取网页meta
* @param $content
* @return array
*/
protected function getMeta($content)
{
$file = "metaCache";//向于保存缓存文件的名称
file_put_contents($file, $content);//将缓存保存到缓存文件中
$meta = get_meta_tags($file);//使用get_meta_tags()取得内容的meta信息
return $meta;//返回meta信息
}
/**
* 获取body内容
* @param $contents
* @return string
*/
protected function getDetail($contents)
{
preg_match('/<body(.*?)>(.+)<\/body>/s', $contents, $matches);//使用正则表达式处理内容
//var_dump($matches);die;
$body = $this->StripHTML($matches[2]);//去掉特殊HTML字符
$body = strip_tags($body);//清除内容中的特殊标签
return mb_substr($body, 0, 400);//返回内容的前400个字符
}
/**
* 获取网页标题
* @param $contents
* @return mixed
*/
protected function getTitle($contents)
{
preg_match('/<title>(.+)<\/title>/s', $contents, $matches);//使用正则表达式处理内容
return $matches[1];//返回处理结果中的标题部分
}
/**
* 获取页面超链接
* @param $content
* @return mixed
*/
protected function getLinks($content)
{
$pat = '/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/i';//处理链接的正则表达式
preg_match_all($pat, $content, $m);//使用正则表达式处理链接
return $m;
}
/**
* 抓取页面内容
* @param $url
* @return bool|string
*/
protected function socketOpen($url)
{
$fp = fsockopen($url, 80, $errno, $errstr, 30);//使用fsockopen()建立SOCKET链接
if($fp === false){
echo "连接远程服务器失败:$errstr($errno)<br/>\n";
return false;
}else{
$out = "GET / HTTP/1.1\r\n";//创建要发送的头文件信息
$out .= "Host: ".$url."\r\n";//指定头文件信息中的主机内容
$out .= "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\r\n";
$out .= "Connection: keep-alive\r\n\r\n";
fwrite($fp, $out);//使用fwrite()函数发送请求
$contents = "";
while(!feof($fp)){//使用while循环读取返回的数据
$contents .= fgets($fp, 1024);
}
fclose($fp);//关闭句柄
return $contents;//返回获取的内容
}
}
/**
* 去掉HTML中不相关的代码
* @param $string
* @return mixed
*/
protected function StripHTML($string)
{
$pattern=array(
"'<script[^>]*?>.*?</script>'si",
"'<style[^>]*?>.*?</style>'si"
);//建立正则表达式
$replace=array(
"",
""
);//建立替换字符数组
return preg_replace($pattern, $replace, $string);//替换内容中HTML并返回替换后的内容
}
/**
* 打印出抓取到的数据
*/
public function show()
{
echo "<pre>";
print_r($this->_sites);//显示保存到$_sites公共变量中的内容
echo "</pre>";
}
/**
* 过滤分析数据中的超链接
*/
public function filterLinks()
{
$realLinks = "";
$links = $this->_sites["links"][2];//获取保存链接的数组元素
//遍历数组,清除不规范链接
foreach($links as $v){//遍历链接数据
//只保存链接
if(preg_match('/^http:\/\//', $v) || preg_match('/^https:\/\//', $v)){
$realLinks[] = $v;
}
}
//去除重复的链接
$realLinks = array_unique($realLinks);
echo "<pre>";
print_r($realLinks);//显示过滤后的链接
echo "</pre>";
}
}
//域名
$domainName = 'www.163.com';
//使用Web爬虫的方法
$spider = new Spider($domainName);//实例化spider类,并设置需要抓取的网站
$spider->start();//开始抓取数据
//$spider->show();//显示抓取的内容
$spider->filterLinks();
2、执行后结果
3、执行完成,在文件所在目录下会有个metaCache
文件,用文本编辑器打开如下
在获取超链接以后,就可以再使用Web爬虫类对这些链接进行下一步的数据抓取。具体的实现代码可以使用无限循环来实现。
4、注意
- 上述例子暂时不能爬去https的网站,这个待去探索
上述例子如需要爬取像
http://news.163.com/17/1225/14/D6GQU683000189FH.html
这样的链接,还需要着手扩展下上述代码中socketOpen
函数(设置下请求头信息,详细情况见另一篇博文使用SOCKET获取网页的内容),
参考资料
- 1.[PHP实例精通 (编程实例大讲堂)] 宫垂刚
- 2.PHP: fsockopen - Manual