通过user-agent来源判断阻止爬虫访问网站,并防止生成[ error ] NULL日志
一、TP5.0通过行为(Behavior)拦截爬虫并避免生成 [ error ] NULL 错误日志
1. 创建行为类(拦截爬虫)
在 application/common/behavior 目录下新建BlockBot.php ,用于识别并拦截爬虫请求:
<?php
namespace app\common\behavior;use think\Response;class BlockBot
{// 爬虫User-Agent特征列表protected $botPatterns = ['/bot/i', '/spider/i', '/curl/i', '/wget/i', '/python/i', '/scrapy/i', '/crawl/i', '/httpclient/i',// 下面是自己添加的爬虫'/toutiao/i','/zhanzhang.toutiao.com/i','/dataforseo/i','/dataforseo.com/i','/dataforseo-bot/i','/semrush/i','/www.semrush.com/i','/YisouSpider/i',];// 白名单(搜索引擎合法爬虫)protected $allowPatterns = ['/googlebot/i', '/bingbot/i', '/baiduspider/i', '/Sogou web spider/i'];public function run(){$request = request();$userAgent = $request->header('user-agent', '');$path = $request->path();// 白名单放行foreach ($this->allowPatterns as $pattern) {if (preg_match($pattern, $userAgent)) {trace("放行{$pattern}爬虫: UA={$userAgent}, Path={$path}", 'info');return;}}// 黑名单拦截foreach ($this->botPatterns as $pattern) {if (preg_match($pattern, $userAgent)) {// 静默记录日志(不触发错误)trace("Blocked Bot: UA={$userAgent}, Path={$path}", 'info');// 静默拦截(不记录错误日志)$this->silentBlock();}}}/*** 静默拦截逻辑*/private function silentBlock(){// 返回404页面(或自定义响应)$response = Response::create()->code(404)->data('Access Denied')->header(['Content-Type' => 'text/plain']);// 终止后续执行throw new \think\exception\HttpResponseException($response);}}
2. 注册行为到请求事件
在 application/tags.php 中绑定行为到 app_init事件(应用初始化):
return [// 应用初始化'app_init' => ['app\\common\\behavior\\BlockBot', //爬虫拦截],
];
3. 自定义异常处理(防止错误日志)
(1) 创建异常处理类
在 application/common/exception 下新建 ExceptionHandler.php,覆盖默认错误处理:
<?php
namespace app\common\exception;use think\exception\Handle;
use think\exception\RouteNotFoundException;
use think\exception\ValidateException;
use think\Response;class ExceptionHandler extends Handle
{public function render(\Exception $e){// 拦截路由不存在错误(常见于爬虫探测)if ($e instanceof RouteNotFoundException) {return $this->silentResponse(404);}// 拦截参数验证错误(如分页参数过大)if ($e instanceof ValidateException) {return $this->silentResponse(400);}// 其他错误静默记录(可选)trace("Silent Error: " . $e->getMessage(), 'error');return parent::render($e);}/*** 静默响应(不记录日志)*/private function silentResponse($code){return Response::create()->code($code)->data('')->header(['Content-Type' => 'text/plain']);}
}
(2) 配置异常处理
在 application/config.php 中指定自定义异常处理器:
// 异常处理配置
'exception_handle' => 'app\common\exception\ExceptionHandler',
4. Nginx层优化(可选)
在服务器配置中拦截部分爬虫并静默处理:
server {listen 80;server_name yourdomain.com;# 拦截爬虫User-Agent并静默处理if ($http_user_agent ~* (bot|spider|python|curl|wget)) {access_log off; # 不记录访问日志return 444; # 静默关闭连接}# 不记录404错误日志error_page 404 = /404;location = /404 {internal;access_log off;}# ThinkPHP伪静态规则location / {if (!-e $request_filename) {rewrite ^(.*)$ /index.php?s=$1 last;break;}}
}
二、TP5.1或TP6.0通过中间件(middleware)拦截爬虫并避免生成 [ error ] NULL 错误日志
以TP5.1演示:
1. 创建拦截中间件(核心逻辑)
在 application/common/middleware 下新建 BlockBot.php,实现 双重防护:
<?php
namespace app\common\middleware;use think\Response;class BlockBot
{// 爬虫User-Agent特征列表protected $botPatterns = ['/bot/i', '/spider/i', '/curl/i', '/wget/i', '/python/i', '/scrapy/i', '/crawl/i', '/httpclient/i'];// 白名单(搜索引擎合法爬虫)protected $allowPatterns = ['/googlebot/i', '/bingbot/i', '/baiduspider/i'];public function handle($request, \Closure $next){$userAgent = $request->header('user-agent', '');$path = $request->pathinfo();// 放行白名单爬虫foreach ($this->allowPatterns as $pattern) {if (preg_match($pattern, $userAgent)) {return $next($request);}}// 拦截黑名单爬虫foreach ($this->botPatterns as $pattern) {if (preg_match($pattern, $userAgent)) {// 静默记录日志(不触发错误)trace("Blocked Bot: UA={$userAgent}, Path={$path}", 'info');// 直接返回404或403,避免后续逻辑执行return response('', 404)->header(['Content-Type' => 'text/html; charset=utf-8']);}}return $next($request);}
}
2. 注册中间件(全局生效)
修改 application/config.php 配置,确保中间件在最优先执行:
// 中间件配置
'middleware' => ['app\common\middleware\BlockBot', // 添加此行到最前面// ...其他中间件
],
3. 防止生成 [ error ] NULL 日志
(1) 自定义错误处理(覆盖ThinkPHP5默认行为)
在 application/config.php 中配置:
// 错误处理配置
'exception_handle' => 'app\common\exception\ExceptionHandler',
创建 application/common/exception/ExceptionHandler.php:
<?php
namespace app\common\exception;use think\exception\Handle;
use think\exception\RouteNotFoundException;
use think\exception\ValidateException;
use think\Response;class ExceptionHandler extends Handle
{public function render(\Exception $e){// 拦截路由不存在错误(常见于爬虫探测)if ($e instanceof RouteNotFoundException) {return $this->silentResponse(404);}// 拦截参数验证错误(如分页过大)if ($e instanceof ValidateException) {return $this->silentResponse(400);}// 其他错误按需处理(此处静默记录)trace("Silent Error: " . $e->getMessage(), 'error');return parent::render($e);}/*** 静默响应(不记录日志)*/private function silentResponse($code){return Response::create()->code($code)->data('')->header(['Content-Type' => 'text/plain']);}
}
(2) 配置日志过滤
修改 application/config.php 忽略部分错误类型:
// 日志配置
'log' => ['type' => 'File','level' => ['error', 'sql'],'apart_level' => ['error', 'sql'],'ignore_error' => [// 忽略路由不存在错误(避免生成 [ error ] NULL 日志)'think\exception\RouteNotFoundException',],
],
4. Nginx层优化(双重防护)
server {listen 80;server_name yourdomain.com;# 拦截爬虫User-Agent并静默处理if ($http_user_agent ~* (bot|spider|python|curl|wget)) {access_log off; # 不记录访问日志return 444; # 静默关闭连接}# FastAdmin伪静态规则location / {if (!-e $request_filename) {rewrite ^(.*)$ /index.php?s=$1 last;break;}}# 不记录404错误日志error_page 404 = /404.html;location = /404.html {internal;access_log off;}
}