原文链接: PHP爬虫编写 - Tansoz 探索者
一、PHP 是什么东西?
PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。——百度百科的描述。
二、爬虫有什么用?
爬虫有什么用?先说一下爬虫是什么东西,我认为爬虫就是一个网络信息收集程序,也许我自己的理解有错误,也请大家给我指正。既然爬虫是一个网络信息收集程序,那就是用来收集信息,并且收集的信息是在网络上面的。如果还是不太清楚爬虫有什么用,我就举几个爬虫应用的例子:搜索引擎就需要爬虫收集网络信息供人们去搜索;大数据的数据,数据从哪里来?就是可以通过爬虫在网络中爬取(收集)而来。
三、通常听到爬虫会想到 Python,但为什么我用 PHP,而不用 Python 呢?
- Python 我说实话,我不会 Python。( Python 我真不会,想知道可能你要去百度一下,因为 Python 我真不会。)
- PHP 写东西我一直都是认为,你只要想出算法程序就已经出来了,不用考虑太多数据类型的问题。
- PHP 的语法和其他编程语言差不多,就算你开始不会 PHP,你也可以马上上手。
- PHP的语法和其他编程语言差不多,就算你开始不会 PHP,你也可以马上上手。的想法有错。)
- 本人其实也是初学 PHP,想通过写一些东西提高自己的水平。(下面可能有些代码会让你觉得不够规范,欢迎指正,谢谢。)
四、PHP爬虫第一步
PHP爬虫第一步,第一步......第一步当然就是搭建 PHP 的运行环境,没有环境PHP又怎么能运行呢?就像鱼不能离开水一样。(我见识还不够,可能我举的鱼的例子不够好,请见谅。)在Windows的系统下我使用 WAMP,而在Linux的系统下我使用 LNMP 或者 LAMP。
WAMP:Windows Apache Mysql PHP
LAMP:Linux Apache Mysql PHP
LNMP:Linux Nginx Mysql PHP
Apache 和 Nginx 是 Web 服务器软件。
Apache或Nginx、Mysql 以及 PHP 这些都是 PHP Web 的基本配置环境。网上有 PHP Web 环境的安装包,这些安装包使用很方便,不需要每给东西安装以及配置。但如果你对这些集成安装包担心安全问题,你可以到这些程序的官网下载然后在网上找配置教程就可以了。(说真的,我真心的不会单独去弄,我觉得很麻烦。)
五、 PHP爬虫第二步
(感觉自己废话好多,应该马上来一段代码!!!)
<?php
// 爬虫核心功能:获取网页源码
$html = file_get_contents("https://www.baidu.com/index.html");
// 通过 php 的 file_get_contents 函数获取百度首页源码,并传给 $html 变量
echo $html;
// 输出 $html
?>
爬虫网络核心功能已经写出来了,为什么说就这么几行代码就把爬虫核心功能写出来了呢?我猜有人已经明白了吧,其实是因为爬虫是数据获取的程序,就上面几行代码其实已经能够获取数据了,所以爬虫的核心功能已经写出来了。可能有人会说“你这个也太菜了吧!有什么用?”,虽然我是很菜,但请别说出来,让我好好装个X。(又说了两行废话,不好意思。)
其实爬虫是用来干嘛,主要看你想让它来干嘛。就像我前些日子为了好玩写了一个搜索引擎网站出来,当然网站很菜,结果排序没有规律,很多都查不到。我的搜索引擎的爬虫就是要写一个适合于搜索引擎的爬虫。所以为了方便我也就用写搜索引擎的爬虫为目标讲解。当然了,我搜索引擎的爬虫还是不够完善,不完善的地方都是要你们自己去创造,去完善。
六、 搜索引擎爬虫的限制
搜索引擎的爬虫有时候不是不能那个网站的页面获取页面源码,而是有robot.txt文件,有该文件的网站,就代表站主不希望爬虫去爬取页面源码。(不过如果你就是想要获取的话,就算有也一样会去爬吧!)
我搜索引擎的爬虫其实还有很多不足而导致的限制,例如可能因为无法运行 JS 脚本所以无法获取页面源码。又或者网站有反爬虫的机制导致不能获取到页面源码。有反爬虫机制的网站就如:知乎,知乎就是有反爬虫的机制的网站。
七、以弄搜索引擎爬虫为例,准备写该爬虫需要的东西
- PHP 编写基础
- 正则表达式(你也可以使用Xpath,对不起,我不会使用)
- 数据库的使用(本文使用 MySql 数据库)
- 运行环境(只要有能运行 PHP 网站的环境和数据库就OK)
八、搜索引擎获取页面源码并获取页面的标题信息
<?PHP
// 通过 file_get_contents 函数获取百度页面源码
$html = file_get_contents("https://www.baidu.com/index.html");
// 通过 preg_replace 函数使页面源码由多行变单行
$htmlOneLine = preg_replace("/\r|\n|\t/","",$html);
// 通过 preg_match 函数提取获取页面的标题信息
preg_match("/<title>(.*)<\/title>/iU",$htmlOne,$titleArr);
// 由于 preg_match 函数的结果是数组的形式
$title = $titleArr[1];
// 通过 echo 函数输出标题信息
echo $title;
?>
报错误示例:
Warning: file_get_contents("https://https://127.0.0.1/index.php") [function.file-get-contents]: failed to open stream: Invalid argument in E:\website\blog\test.php on line 25
https是SSL加密协议,如果出现获取页面时报上面的错误,代表你的 PHP 可能少了OpenSSL 的模块,你可以到网上查找解决办法。
九、搜索引擎爬虫的特点
虽然我没见过像“百度”,“Google”它们的爬虫,可是我自己通过猜测,以及在实际爬去过程当中遇到的一些问题,自己总结的几个特点。(可能有不对的地方,或者欠缺的地方,欢迎指正,谢谢。)
- 通用性
通用性是因为我认为搜索引擎的爬虫一开始并不是针对哪一个网站制定的,所以要求能爬取到的网站尽可能的多,这是第一点。而第二点,就是获取网页的信息就是那些,一开始不会因为某些个别特殊小网站而放弃某些信息不提取,举个例子:一个小网站的一个网页meta标签里没有描述信息(description)或者关键词信息(keyword),就直接放弃了描述信息或者关键词信息的提取,当然如果真的某一个页面没有这些信息我会提取页面里的文字内容作为填充,反正就是尽可能达到爬取的网页信息每个网页的信息项都要一样。这是我认为的搜索引擎爬虫的通用性,当然我的想法可能是错误的。(我说得可能不是太好,我一直在学习。)
- 不确定性
不确定性就是我的爬虫获取什么网页我是控制不够全面的,只能控制我能想到的情况,这也是因为我写的算法就是这样的原因,我的算法就是爬取获取到的页面里的所有链接,再去爬去获取到的这些链接,其实是因为搜索引擎并不是搜某一些东西,而是尽可能的多,因为只有更多的信息,才能找到一个最贴切用户想要的答案。所以我就觉得搜索引擎的爬虫就要有不确定性。(我自己再看了一遍,也有点觉得自己说得有点让自己看不懂,请见谅,欢迎指正,提问,谢谢了!)
下面的视频是我的搜索网站的使用视频,而搜到的信息就是通过自己写的 PHP 爬虫获取到的。(这个网站我已经不再继续维护了,所以有欠缺之处,请见谅。)
十、到现在可能出现的问题
- 获取的源码出现乱码
<?PHP
// 乱码解决办法,把其他编码格式通过 mb_convert_encoding 函数统一转为 UTF-8 格式
$html = mb_convert_encoding($html,'UTF-8','UTF-8,GBK,GB2312,BIG5');
// 还有一种因为gzip所以出现乱码的,我会在以后讲
?>
2. 获取不到标题信息
<?PHP
// 获取不到标题信息解决办法,首先判断是否能获取到页面源码
// 如果能获取到但还是不能获取到标题信息
// 我猜测的问题是:因为我教的是使用正则表达式获取的,源码没有变成一行,获取起来就会出现问题
$htmlOneLine=preg_replace("/\r|\n|\t/","",$html);
?>
3.获取不到页面源码
<?PHP
// 像新浪微博你可能获取到的是“Sina Visitor System”
// 解决办法添加header信息
$opts = array(
'http'=>array(
'method'=>"GET",
"timeout"=>20,
'header'=>"User-Agent: Spider \r\n",
)
);
$context = stream_context_create($opts);
$html = file_get_contents($domain,0,$context,0,150000);
// 这样就能获取到新浪微博的页面了
?>
十一、获取一个网页时的处理思路
我们先不去想很多网页,因为很多网页也就是一个循环。
- 获取页面源码
- 通过源码提取页面的什么信息
- 提取的信息要怎么处理
- 处理后放不放进数据库
十二、按照十一的思路的代码
<?php
// 一、获取源码
// 假设我们要获取淘宝首页
$html = file_get_content("https://www.taobao.com");
// 二、提取标题和文本
// 三、提取信息处理
// 处理页面源码,多行变单行
$htmlOneLine = preg_replace("/\r|\n|\t/","",$html);
// 获取标题信息
preg_match("/<title>(.*)<\/title>/iU",$htmlOneLine,$titleArr);
// 保留标题信息
$titleOK = $titleArr[1];
// 获取页面中的文本信息
// 处理前面不需要的head标签
$htmlText = preg_replace("/<html>(.*)<\/head>/","",$htmlOneLine);
// 处理style和script标签及内容
$htmlText = preg_replace("/<style(.*)>(.*)</style>|<script(.*)>(.*)</script>/iU","",$htmlText);
// 处理多余标签
$htmlText = preg_replace("/<(\/)?(. )>/","",$htmlText);
// 四、保存到数据库
// 略
?>
十三、PHP 保存页面的图片思路
- 获取页面源码
- 获取页面的图片链接
- 使用函数保存图片
十四、保存图片示例代码
<?php
// 使用file_get_contents()函数获取图片
$img = file_get_contents("http://127.0.0.1/photo.jpg");
// 使用file_put_contents()函数保存图片
file_put_contents("photo.jpg",$img);
?>
十五、gzip解压
本来我以为自己写的爬虫已经写得差不多了,就是除了反爬虫的网站难以爬取外,应该是都可以爬了。但有一天我去尝试爬去bilibili的时候出现问题了,我发现怎么我数据库里面的都是乱码,而且标题什么的都没有,好奇怪!后来我才知道原来是因为GZIP的压缩,原来我直接使用file_get_content函数获取的页面是未经过解压的页面,所有都是乱码!好了,然后我知道问题出现在那里了,接下来就是想解决办法了。(其实那时候的我是完全不知道怎么解决解压gzip的,都靠搜索引擎,哈哈哈哈哈)。
我得到了两个解决办法:
- 在 request header 那里告诉对方服务器,我这爬虫(不。。。应该是我这浏览器)不支持gzip的解压,就麻烦你不要压缩,直接把数据发给我吧!
// 这是request header(请求头)
$opts = array(
'http'=>array(
'method'=>"GET",
"timeout"=>20,
'header'=>"User-Agent: Spider \r\n".
"accept-encoding:"
)
);
// 我把accept-encodeing(能接收编码)设为空,代表不接受gzip
2. 如果告诉了对方服务器本浏览器(爬虫)不支持解压gzip,可他还是继续发gzip的数据给你,那就没有办法了,只好默默的在搜索引擎找php解压gzip的解压函数——gzdecode()。
// 废话:好久没更新了,因为最近又在重复造轮子,不过好开心 -- 2019.01.14
十六、子链接的获取
网页爬虫就好比自动的网页源码另存为操作,可是如果真的是一个一个网址手动输入给爬虫去爬取的话,那还倒不如人手另存为呢!所以在这里就得解析到那些子链接,一个网页其实会有很多的a标签,而这些a标签的href属性值就会是一个子链接。(这句怎么感觉有一点废话呢?应该大家都知道吧!)通过很多种工具的解析得到了原始的子链接(我没用html的工具,就是用了正则表达式及工具)为什么叫原始数据,因为这些子链接有很多不是URL链接,又或者是一些不完整的链接,又或者你是要爬一个网站,不要爬虫跑出去的时候,还得去除一些并非那个网站的链接,防止爬虫跑出去。下面我列举一些原始子链接。(其实就是我喷到的坑。。。)
http://wxample.com/
./index.html
index.html
//example.com/index.html
javascript:;
../index.html
http://example2.com/index.html
你可以看得到这里有很多种链接,有完整的绝对路径的链接,也有不完整的相对路径链接,有的还跑来了javascript,如果是相对路径或javascript的链接,直接给爬虫去爬,肯定让爬虫一脸懵,因为不完整或根本就不是一个链接。所以就需要对子链接补全或者丢弃。
十七、子链接的处理
上面废话了一大堆后,接下来还是继续废话。好了,处理其实就是去重、丢弃以及补全。
- 对于相同的子链接丢弃不保存
- 对于相对路径链接补全
- 对于不是链接的链接丢弃(不是链接的链接是什么东西?自己都觉得的奇怪。。。。)
对于第一种我就不多说了,相信大家都知道该怎么做。
对付第二种的方法我就写一个能够运行相对路径的方法出来就OK了,通过父链接来进行相对路径转绝对路径。(有空补代码给大家。。。不好意思了)
对于第三种也正则匹配一下就完了。
# 如果有问题就发评论,让大家一起解决,谢谢。
未完待续。。。