2.3 下载网页包含的所有图片
2.2节介绍了在PHP程序中实现下载远程图片的解决方法。在网页中一个个收集图片URL挺麻烦,能否一次性将网页中的所有图片都下载下来呢?本节将介绍如何在PHP程序中实现这个功能。我们知道像IE,Firefox等Web浏览器中已经提供了将网页所有内容都保存下来的功能,现实中可能并没有多少需要将网页包含的所有图片下载下来的地方,但是通过本节相关实例的介绍,不仅从中可以学习得到许多关于正则表达式和采集的相关知识,也可以掌握关于图像处理的相关知识,作为一个挺有意思的实例,还是很值得研究的。
下载到本地的图片一览:
本例要点
要实现下载网页中包含的所有图片,在操作上实际上分为如下两个步骤。
第一步,读入网页内容,并将其保存为字符串,然后从网页中把所有<img ...>用正则匹配方法全部分离出来。
在这一步中,我们使用file_get_contents函数读取网页内容,使用preg_match_all函数(此函数的第一参数里传入正则表达式)匹配出网页中所有的<img ...>,最后将取得的图片URL保存在数组中。关于preg_match_all函数以及正则表达式的相关知识,在补充资料里有说明。
第二步,循环处理图片URL的数组,进行图片下载处理。
此处,使用GetImage类来进行具体的图片下载工作,GetImage类中提供了两种方法来进行图片下载:一种是利用传统的GD库的图像函数(诸如图像读取函数ImageCreateFromXXX,以及图像保存函数ImageXXX,XXX部分是图像的种类名,如JPEG等),另一种就是利用cURL库。cURL库是一个非常强大的工具,后面的补充资料部分会有详细的介绍。
目录结构
代码
提交给自己(getAllPic.php)处理。
单击“提交”按钮后,才有后面的图片下载处理。
分析网页的URL,取出网页所属的主机(host)URL,因为很多图片URL是相对路径,必须在前加上主机URL。
18行到20行读取网页内容。在使用file_get_contents函数时,后面的两个参数是可选参数,例如如果使用代理服务器来连接Internet的话,那就必须带上$context参数,代理服务器的具体设置在18行的选项变量$opts中,如$opts =array('http'=> array('proxy'=> 'tcp://www-proxy:10080', 'request_fulluri'=> true))来设置代理服务器选项。一般情况下后两个参数是可以省略的。
$reg中设置正则表达式。关于PHP中使用的正则表达式见补充资料。
函数preg_match_all负责在字符串$message中找出所有匹配正则表达式的字符串,并将结果保存在数组$matches中。如果匹配不成功,则数组的长度为0。
取出所有匹配结果。$matches为多维数组,维数的多少与正则表达式中的小括弧“()”的多少有关,此处只出现了一对,则为2维数组。$matches[0]中始终保存的是匹配的整字符串,而$matches[1]则保存的是匹配正则表达式第一对括弧所匹配的内容,此处即为图片的URL。
如果图片为相对路径,则将其转换为全路径。有两种相对路径,第一种是,如果第一个字符为“/”,那是相对与host来说的相对路径,此时只需要加上host的URL即可转化为完全的URL。第二种是,相对于当前页面来说的相对路径,必须加上当前URL(除了文件名外,即变量$baseurl中的值)。
调用array_unique函数过滤重复的图片URL。
实例化图片下载类GetImage。
在source属性中设置下载目标图片的URL,save_to中设置图片的保存目录。调用download方法开始下载,默认为使用cURL库进行下载,以$Gimg->download('gd')的形式调用download方法时,则使用图像函数方式进行图片下载。
定义图片下载类的属性。$source中保存远程图片的URL,$save_to的初值为本地图片的保存目录。$set_extension为是否指定扩展名的标志,$quality为下载后图片的品质(默认为100%)。
定义图片下载方法。参数为使用的方式标志,默认为使用cURL库进行图片下载。
调用GD库中的GetImageSize函数,取得远程图片的信息,并从其中的mime元素中分离出图片的类型名$type。
根据图片类型的不同,决定不同的图像生成函数ImageCreateFromXXX,图像保存函数ImageXXX,以及图片扩展名。
50行到56行根据“指定扩展名标志”set_extension属性来,合成本地图片文件名。
生成本地图片文件路径。
图片下载方式为curl时,调用LoadImageCURL进行图片的下载。
下载方式为gd时,调用与图片类型对应的GD图像进行图片下载。函数ImageCreateFromXXX用于读入远程图片文件,ImageXXX用于在新的文件中保存图像数据。
定义利用cURL库进行图片下载的方法。参数为远程图片的URL。关于cURL库的使用见后面的补充资料。
初始化curl,读入远程图片文件。
新建本地图片文件。
组合curl的各个参数,包括本地文件句柄,头信息有无标志,递归设置(启用时会将服务器返回的“Location:”放在header中递归时返回给服务器),设置超时限制。
设置curl选项。
执行请求。
关闭curl句柄以及文件句柄。
补充
引入外部文件的4个方法
PHP中提供了4个引入外部文件的方法。
将外部文件包含进来的做法很相似,但还是有些微妙的区别的,请见表2-1。
表2-1 引入外部文件的函数
更有效的外部文件引用
像履历功能这种整个应用系统的共通功能,不用每个页面都调用包含(require_once)命令,这样的文件可以在php.ini的auto_prepend_file参数中进行声明。只用在php.ini加入下面的一行:
这样,在各脚本程序执行前先执行上述脚本,各个脚本程序再不用进行包含的动作了。另外,.php各脚本执行后自动执行auto_append_file中设定的脚本。
关于cURL库的使用
cURL的主要用途为命令行自动化工具,如模拟一个使用者在浏览器端访问一个网站,自动登录,或者自动提取网页中的内容或二进制文件或数据。
首先,需要在php.ini中打开php_curl.dll或php_curl.so的扩展:
然后重启apache。
我们可以通过其他办法获取网页内容。大多数时候,我因为想偷懒,都直接用简单的PHP函数:
或者
或者
但是,这种做法缺乏灵活性和有效的错误处理。而且,你也不能用它完成一些高难度任务——比如处理coockies、验证、表单提交、文件上传等。cURL是一种功能强大的库,支持很多不同的协议、选项,能提供URL请求相关的各种细节信息。
基本结构
在学习更为复杂的功能之前,先来看一下在PHP中建立cURL请求的基本步骤:依次为初始化,设置变量,执行并获取结果,释放cURL句柄。下面的例子是cURL的基本用法,用于获取远程网页的HTML。
第二步(也就是curl_setopt())最为重要,一切玄妙均在此。有一长串cURL参数可供设置,它们能指定URL请求的各个细节。要一次性全部看完并理解可能比较困难,所以今天我们只试一下那些更常用也更有用的选项。
检查错误
你可以加一段检查错误的语句(虽然这并不是必需的):
请注意,比较的时候我们用的是“===FALSE”,而非“==FALSE”。因为我们得区分空输出和布尔值FALSE,后者才是真正的错误。
获取信息
这是另一个可选的设置项,能够在cURL执行后获取这一请求的有关信息。
返回的数组中包括了以下信息:
使用cURL库后可以实现很多有用的功能,如浏览器的重定向,还有在本节实例中的下载功能等,在不一一介绍了,下面罗列些其他有用的cURL选项及例子,有兴趣的朋友可以参考。其他有用的cURL选项及例子如下。
HTTP认证
如果某个URL请求需要基于HTTP的身份验证,你可以使用下面的代码:
复制内容到剪贴板代码:
FTP上传
PHP自带有FTP类库,但你也能用cURL:
翻墙术
你可以用代理发起cURL请求:
回调函数
可以在一个URL请求过程中,让cURL调用某指定的回调函数。例如,在内容或者响应下载的过程中立刻开始利用数据,而不用等到完全下载完。
这个回调函数必须返回字串的长度,不然此功能将无法正常使用。
在URL响应接收的过程中,只要收到一个数据包,这个函数就会被调用。
正则表达式
PHP关于正则表达式的定义是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的模式分割、匹配、查找及替换操作。
PHP中有两套正则函数,两者功能差不多,分别为:一套是由PCRE(Perl Compatible Regular Expression)库提供的。使用“preg_”为前缀命名的函数,本节中就使用了其中的preg_match_all函数,可以参考本书最后的附录;一套由POSIX(Portable Operating System Interface of Unix)扩展提供的。使用以“ereg_”为前缀命名的函数;(POSIX的正则函数库,自PHP 5.3以后,就不在推荐使用,从PHP6以后,就将被移除)。
关于如何写正则表达式,由于篇幅的限制,在不再做一一介绍,大家可以参考一些有关书籍或网络资料。