php抓取网页指定内容(在PHP中如何运用cURLcURL检查错误的语句(上))

优采云 发布时间: 2021-12-08 16:09

  php抓取网页指定内容(在PHP中如何运用cURLcURL检查错误的语句(上))

  CURL 是一种使用 URL 语法规则来传输文件和数据的工具。它支持多种协议和选项,如HTTP、FTP、TELNET等,可以提供与URL请求相关的各种详细信息。最好的部分是 PHP 还支持 cURL 库。

  本文将介绍cURL的一些高级特性以及如何在PHP中使用。

  1 为什么使用卷曲?

  是的,我们可以通过其他方式获取网页内容。很多时候,因为想偷懒,直接使用了简单的PHP file_get_contents() 函数:

  $content=file_get_contents("http://www.awaimai.com");$lines=file("http://www.awaimai.com");readfile(http://www.awaimai.com);

  然而,这种方法缺乏灵活性和有效的错误处理。而且,你不能用它来完成一些困难的任务,比如处理:coockies、验证、表单提交、文件上传等等。

  2 启用卷曲

  首先,我们首先要确定PHP是否启用了这个库。您可以使用 phpinfo() 函数获取此信息。如果在网页上看到如下输出,则说明cURL库已开启。

  

  如果 curl 没有开启,那么你需要开启这个库。如果是windows平台下就很简单了,需要修改php.ini文件的设置,找到php_curl.dll,把前面的分号去掉。如下:

  #取消下面的注释extension=php_curl.dll

  如果是Linux服务器,则需要重新编译PHP,编译时在configure命令中添加--with-curl参数。

  3 基本结构

  在学习更复杂的函数之前,我们先来看看在 PHP 中创建 cURL 请求的基本步骤:

  初始化设置选项执行并获取结果释放cURL句柄

  实现代码如下:

  // 1. 初始化$ch=curl_init();// 2. 设置选项curl_setopt($ch, CURLOPT_URL,"http://www.awaimai.com"); //设置要抓取的页面地址curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //抓取结果直接返回(如果为0,则直接输出内容到页面)curl_setopt($ch, CURLOPT_HEADER, 0); // 不需要页面的HTTP头// 3. 执行并获取HTML文档内容,可用echo输出内容$output = curl_exec($ch);// 4. 释放curl句柄curl_close($ch)

  第二步(也就是curl_setopt())是最重要的,所有的奥秘都在这里。有一长串要设置的 cURL 参数,可以指定 URL 请求的详细信息。可能很难一口气读完和理解,所以今天我们只尝试更常用和更有用的选项。

  4 检查错误

  您可以在 cur_exec() 之后添加一个语句来检查错误(尽管这不是必需的):

  $output = curl_exec($ch);if($output===FALSE) { echo"cURL Error:". curl_error($ch);}

  请注意,我们在比较时使用 === FALSE 而不是 == FALSE。因为我们必须区分空输出和布尔值 FALSE,后者才是真正的错误。

  5 获取信息

  使用 curl_getinfo() 获取 curl 执行后请求的相关信息。当然,这也是一个可选设置:

  curl_exec($ch);$info = curl_getinfo($ch);echo\'获取\'.$info[\'url\'] . \'耗时\'. $info[\'total_time\'] . \'秒\';

  返回的数组收录以下信息:

  6 基于浏览器的重定向

  在第一个示例中,我们将提供一段代码来检测服务器是否具有基于浏览器的重定向。例如,某些网站 会根据是否是移动浏览器甚至用户来自哪个国家/地区来重定向网页。

  我们使用 CURLOPT_HTTPHEADER 选项来设置发出的 HTTP 标头,包括用户代理信息和默认语言。然后看看这些特定的 网站 是否会将我们重定向到不同的 URL。

  //测试用的URL$urls=array("http://www.bbc.com","http://www.baidu.com","http://www.ubuntu.com");//测试用的浏览器信息$browsers=array("standard"=> array("user_agent"=>"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)","language"=>"en-us,en;q=0.5"),"iphone"=> array("user_agent"=>"Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3","language"=>"en"),"french"=> array("user_agent"=>"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)","language"=>"fr,fr-FR;q=0.5"));foreach($urlsas$url) { echo"URL:$url\n<br />";foreach($browsersas$test_name=>$browser) { $ch=curl_init();// 设置 urlcurl_setopt($ch, CURLOPT_URL, $url);//设置浏览器的特定headercurl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: {$browser[\'user_agent\']}","Accept-Language: {$browser[\'language\']}"));//页面内容我们并不需要curl_setopt($ch, CURLOPT_NOBODY, 1);// 只需返回HTTP headercurl_setopt($ch, CURLOPT_HEADER, 1);//返回结果,而不是输出它curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$output = curl_exec($ch); curl_close($ch);//有重定向的HTTP头信息吗?if(preg_match("!Location: (.*)!", $output,$matches)) { echo"$test_name: redirects to $matches[1]\n<br />"; } else{echo"$test_name: no redirection\n<br />"; } } echo"\n\n<br /><br />";}

  首先,我们创建一组需要测试的网址,然后指定一组需要测试的浏览器信息。最后,通过循环测试各种 URL 和浏览器匹配的可能情况。

  因为我们指定了 CURLOPT_NOBODY 选项,所以返回的输出内容只收录 HTTP 头信息(存储在 $output 中)。使用一个简单的规则,我们检查这个头信息是否收录单词 Location:。

  运行此代码应返回以下结果:

  RL: http://www.bbc.comstandard: no redirection iphone: no redirection french: no redirection URL: http://www.baidu.comstandard: redirects to https://www.baidu.com/iphone: no redirection french: redirects to https://www.baidu.com/URL: http://www.ubuntu.comstandard: redirects to http://www.ubuntu.com/index_kyliniphone: redirects to http://www.ubuntu.com/index_kylinfrench: redirects to http://www.ubuntu.com/index_kylin

  7 使用POST方法发送数据

  在发起 GET 请求时,数据可以通过“查询字符串”传递给一个 URL。例如,在Bing中搜索时(鉴于Google需要FQ,使用Bing代替),搜索关键字是URL的查询字符串的一部分

  http://www.bing.com?q=awaimai.com

  您可以使用 PHP 脚本模拟这种 URL 请求。首先新建一个可以接受并显示POST数据的文件,我们命名为post_output.php,脚本内容为:

  print_r($_POST);

  接下来,编写一个 PHP 脚本来执行 cURL 请求:

  $url ="http://localhost/post_output.php";$post_data=array("foo"=>"bar","query"=>"Nettuts","action"=>"Submit");$ch=curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//我们在POST数据哦!curl_setopt($ch, CURLOPT_POST, 1);//加上POST变量curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo$output;

  执行代码后,应该得到如下结果:

  Array( [foo] => bar [query] => Nettuts [action] =>Submit)

  此脚本向 post_output.php 发送 POST 请求,并在此页面上返回 $_POST 变量。我们使用 cURL 捕获了这个输出。

  8 文件上传

  上传文件与之前的 POST 非常相似。因为所有的文件上传表单都是通过POST方式提交的。首先新建一个页面来接收文件,命名为upload_output.php,页面内容:

  print_r($_FILES);

  以下是实际执行文件上传任务的脚本,名为upload.php,内容如下:

  $url ="http://localhost/upload_output.php";$post_data=array("foo"=>"bar", //要上传的本地文件地址"upload"=>"@C:/wamp/www/test.zip");$ch=curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo$output;

  如果需要上传文件,只需要指定上传的文件路径并作为POST变量传递即可,但记得在前面加上@符号。执行此脚本应获得类似于以下内容的输出:

  Array( [upload] =>Array ( [name] => test.zip [type] => application/octet-stream [tmp_name] => C:\Windows\php1BB4.tmp [error] => 0[size]=> 487235))

  9 cURL 批处理(多cURL)

  cURL 还有一个高级功能:批处理句柄。此功能允许您同时或异步打开多个 URL 连接。以下是示例代码:

  //创建两个cURL资源$ch1=curl_init();$ch2=curl_init();//指定URL和适当的参数curl_setopt($ch1, CURLOPT_URL,"http://lxr.php.net/");curl_setopt($ch1, CURLOPT_HEADER, 0);curl_setopt($ch2, CURLOPT_URL,"http://www.php.net/");curl_setopt($ch2, CURLOPT_HEADER, 0);//创建cURL批处理句柄$mh=curl_multi_init();//加上前面两个资源句柄curl_multi_add_handle($mh,$ch1);curl_multi_add_handle($mh,$ch2);//预定义一个状态变量$active=null;//执行批处理do{$mrc = curl_multi_exec($mh,$active);}while($mrc==CURLM_CALL_MULTI_PERFORM);while($active&&$mrc== CURLM_OK) { if(curl_multi_select($mh) != -1) { do{$mrc = curl_multi_exec($mh,$active); } while($mrc== CURLM_CALL_MULTI_PERFORM); }}//关闭各个句柄curl_multi_remove_handle($mh,$ch1);curl_multi_remove_handle($mh,$ch2);curl_multi_close($mh);

  在这里您需要做的就是打开多个 cURL 句柄并将它们分配给一个批处理句柄。然后你只需要等待它在 while 循环中完成执行。

  在这个例子中有两个主要循环。第一个 do-while 循环反复调用 curl_multi_exec()。该函数是非阻塞的,但会尽可能少地执行。它返回一个状态值。只要这个值等于常量CURLM_CALL_MULTI_PERFORM,就意味着还有一些紧急的工作要做(例如,发送对应URL的http头信息)。换句话说,我们需要不断调用函数,直到返回值发生变化。

  以下 while 循环仅在 $active 变量为真时才会继续。该变量之前作为第二个参数传递给 curl_multi_exec(),表示批处理句柄中是否存在活动连接。接下来,我们调用 curl_multi_select(),它在活动连接(例如接受服务器响应)出现之前被“阻塞”。该函数执行成功后,我们将进入另一个do-while循环,继续下一个URL。

  下面我们来看看如何在实践中使用这个功能:

  9.1 个 WordPress 链接检查器

  想象一下,您有大量的 文章 博客,而这些 文章 收录大量外部 网站 链接。一段时间后,由于某种原因,其中相当多的链接失败了。要么统一,要么整个网站都是功夫网……

  下面我们来创建一个脚本来分析所有这些链接,找出无法打开或404的网站/webpages,并生成报告。

  请注意,以下并不是真正可用的WordPress插件,只是一个具有独立功能的脚本,仅供演示,谢谢。

  好的,让我们开始吧。首先,从数据库中读取所有这些链接:

  // 配置 MySQL 数据库$db_host = \'localhost\';$db_user = \'root\';$db_pass = \'\';$db_name = \'wordpress\';$excluded_domains=array(\'localhost\', \'www.mydomain.com\');$max_connections = 10;//初始化一些变量$url_list=array();$working_urls=array();$dead_urls=array();$not_found_urls=array();$active=null;// 连到 MySQLif(!mysql_connect($db_host,$db_user,$db_pass)) { die(\'Could not connect: \' . mysql_error());}if(!mysql_select_db($db_name)) { die(\'Could not select db: \' . mysql_error());}//找出所有含有链接的文章$sql ="SELECT post_content FROM wp_posts WHERE post_content LIKE \'%href=%\' AND post_status = \'publish\' AND post_type = \'post\'";$res=mysql_query($sql) or die(mysql_error());while($d=mysql_fetch_assoc($res)) { //用正则匹配链接if(preg_match_all("!href=\"(.*?)\"!",$d[\'post_content\'],$matches)) { foreach($matches[1]as$url) { //剔除排除的域名$tmp=parse_url($url);if(in_array($tmp[\'host\'],$excluded_domains)) { continue; } // 保存 URL$url_list[]=$url; } }}//移除重复链接$url_list=array_values(array_unique($url_list));if(!$url_list) { die(\'No URL to check\');}

  我们首先配置数据库,一系列要排除的域名($excluded_domains),以及最大同时连接数($max_connections)。然后,连接数据库,获取文章和收录的链接,采集到一个数组中($url_list)。

  下面的代码有点复杂,我分小步详细解释一下:

<p>// 1. 批处理器$mh=curl_multi_init();// 2. 加入需批量处理的URLfor($i = 0; $i

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线