一、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;
}
}