如何解决000webhost上PHP天气爬虫的SSL证书验证错误?
问题详情
本地运行正常的PHP天气爬虫,部署到000webhost(站点:https://overprotective-gard.000webhostapp.com/?city=china)后出现以下SSL相关错误:
Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed in /storage/ssd4/367/20650367/public_html/index.php on line 17
Warning: file_get_contents(): Failed to enable crypto in /storage/ssd4/367/20650367/public_html/index.php on line 17
Warning: file_get_contents(https://www.weather-forecast.com/locations/london/forecasts/latest): failed to open stream: operation failed in /storage/ssd4/367/20650367/public_html/index.php on line 17
原代码如下:
<?php $weather = ""; $error = ""; // if($_GET["city"]){ // better way --> if(array_key_exists('city',$_GET)) { $city = str_replace(' ','',$_GET['city']); $file_headers = @get_headers("https://www.weather-forecast.com/locations/".$city."/forecasts/latest"); if($file_headers[0] == 'HTTP/1.1 404 Not Found'){ $error = "City couldn't be found"; } else{ $forcastPage = file_get_contents("https://www.weather-forecast.com/locations/".$city."/forecasts/latest"); $pageArray = explode('3 days)</div><p class="b-forecast__table-description-content"><span class="phrase">',$forcastPage); if(sizeof($pageArray) >1){ $secondPageArray = explode('</span></p></td>',$pageArray[1]); if(sizeof($secondPageArray)>1){ $weather = $secondPageArray[0]; } else{ $error = "City couldn't be found"; } } else{ $error = "City couldn't be found"; } } } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Weather Scraper</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous"> <style> html{ background: url(sunset.avif) no-repeat center center fixed; background-size: cover; } body{ background:none; } .container{ text-align:center; margin-top: 100px; width:450px; } input{ margin:20px 0; } #weather{ margin-top: 15px; } </style> </head> <body> <div class="container"> <h1>What's the weather?</h1> <form> <div class="mb-3"> <label for="city" class="form-label">Enter the name of a city</label> <input type="text" name="city" class="form-control" id="city" placeholder="Eg. London , Tokyo" value = "<?php if(array_key_exists('city',$_GET)) { echo $_GET["city"]; } ?>"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> <div id="weather"> <?php if($weather){ echo '<div class="alert alert-success" role="alert"> '.$weather.' </div>'; } else if($error){ echo '<div class="alert alert-danger" role="alert"> '.$error.' </div>'; } ?> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script> </body> </html>
错误原因
000webhost服务器上的PHP环境缺少可信根证书,导致file_get_contents和get_headers在请求HTTPS站点时无法验证目标服务器的SSL证书,从而触发SSL操作失败。
解决方案
替换file_get_contents和get_headers为cURL请求,手动禁用SSL证书验证(仅适用于此类爬虫场景,生产环境不推荐),同时优化错误处理逻辑。
修改后的完整代码
<?php $weather = ""; $error = ""; if(array_key_exists('city',$_GET)) { $city = str_replace(' ','',$_GET['city']); $url = "https://www.weather-forecast.com/locations/".$city."/forecasts/latest"; // 用cURL替代get_headers检查HTTP状态码 $ch = curl_init($url); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if($httpCode == 404){ $error = "City couldn't be found"; } else{ // 用cURL获取页面内容 $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $forcastPage = curl_exec($ch); curl_close($ch); if($forcastPage === false){ $error = "Failed to fetch weather data"; } else { $pageArray = explode('3 days)</div><p class="b-forecast__table-description-content"><span class="phrase">',$forcastPage); if(sizeof($pageArray) >1){ $secondPageArray = explode('</span></p></td>',$pageArray[1]); if(sizeof($secondPageArray)>1){ $weather = $secondPageArray[0]; } else{ $error = "City couldn't be found"; } } else{ $error = "City couldn't be found"; } } } } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Weather Scraper</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous"> <style> html{ background: url(sunset.avif) no-repeat center center fixed; background-size: cover; } body{ background:none; } .container{ text-align:center; margin-top: 100px; width:450px; } input{ margin:20px 0; } #weather{ margin-top: 15px; } </style> </head> <body> <div class="container"> <h1>What's the weather?</h1> <form> <div class="mb-3"> <label for="city" class="form-label">Enter the name of a city</label> <input type="text" name="city" class="form-control" id="city" placeholder="Eg. London , Tokyo" value = "<?php if(array_key_exists('city',$_GET)) { echo htmlspecialchars($_GET["city"]); } ?>"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> <div id="weather"> <?php if($weather){ echo '<div class="alert alert-success" role="alert"> '.htmlspecialchars($weather).' </div>'; } else if($error){ echo '<div class="alert alert-danger" role="alert"> '.htmlspecialchars($error).' </div>'; } ?> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script> </body> </html>
额外优化说明
- 用cURL替代原生的
file_get_contents和get_headers,通过CURLOPT_SSL_VERIFYHOST和CURLOPT_SSL_VERIFYPEER参数禁用SSL证书验证,解决服务器证书缺失问题。 - 新增
htmlspecialchars转义用户输入与输出内容,防范XSS攻击,提升代码安全性。 - 添加cURL请求失败的判断逻辑,增强错误处理的全面性。
内容的提问来源于stack exchange,提问作者Saransh Singh

