nginx/openresty 之 access_by_lua 识别搜索引擎蜘蛛

当网站想对蜘蛛和正常用户应用不同的限流策略、或者代理到不同的服务器。
识别蜘蛛,一般是白名单及反查IP的方式。像百度不公布蜘蛛的 IP 段,而360 不支持反查 IP,公布了 IP 段。

本篇实现一下对蜘蛛和正常用户应用不同的限流策略。
现在在站点的 server 中已配置好三个不同的内部重定向 location:(以下限流配置仅做示例)

http{
    limit_req_zone $binary_remote_addr zone=req_x_perip:10m rate=2r/s;#单IP速率
    limit_conn_zone $server_name zone=conn_perserver:10m;#并发
    limit_req_status 444;
}
server {
    ..

    # 爬虫
    location @spider {
        limit_conn conn_perserver 10;
        ..
    }
    # 用户
    location @default {
        limit_req zone=req_x_perip;
        limit_conn conn_perserver 10;
        ..
    }
    # 静态图片等资源
    location @resource {
        ..
    } 
}

首先,在入口处定义 access_by_lua,即 nginx 的访问阶段由 lua 脚本处理。

location / {
    access_by_lua_file conf/lua/default.access.lua;
    try_files $uri $uri/ @default;
    ..
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
    try_files $uri @resource;
}

default.access.lua

local filterspider = require("/usr/local/openresty/nginx/conf/lua/filterspider")
filterspider:testip()

然后在 filterspider.lua 中,如果是爬虫则内部重定向到前边定义的 location @spider ,其中运行了两个 shell,分别判断白名单和反查 IP,如果是,则返回 1 :

local _M = {}
function _M.testip()
    local shell = require("resty.shell")

    local from = ngx.re.find(ngx.var.http_user_agent, "(?i)spider|bot", "isjo")
    if (from) then
        -- IP
        IP = ngx.var.remote_addr
        if (IP == nil) then
            ngx.exit(500)
        end

        -- 如果在信任ip名单中
        local ok, stdout, stderr, reason, status = shell.run(
            "sh /usr/local/openresty/nginx/conf/lua/sh/istrustip.sh " .. IP)
        if ok then
            if (tonumber(stdout) == 1) then
                return ngx.exec("@spider")
            end
        end

        -- 反查ip是否搜索引擎
        local ok, stdout, stderr, reason, status = shell.run(
            "sh /usr/local/openresty/nginx/conf/lua/sh/isspider.sh " .. IP)

        if ok then
            if (tonumber(stdout) == 1) then
                return ngx.exec("@spider")
            end
        end

    end

end
return _M

白名单的 shell 脚本 istrustip.sh

basepath='/usr/local/openresty/nginx/conf/lua/sh'
echo $1 | grep -f $basepath/trust_ips.txt | awk '{if($0) print 1}'

trust_ips.txt

^42.236.101.*
^42.236.102.*
^42.236.103.*
^42.236.10.*
..

反查IP 的 shell 脚本 isspider.sh

# 反查IP 45.152.11.106.in-addr.arpa domain name pointer shenmaspider-106-11-152-45.crawl.sm.cn.
basepath='/usr/local/openresty/nginx/conf/lua/sh'

hostname=`host $1 | grep -f $basepath/spider.txt | awk '{print $5}'`

if [ ! -z $hostname ];
then
    # 反查域名 shenmaspider-106-11-152-45.crawl.sm.cn has address 106.11.152.45
    hostip=`host $hostname | awk '{print $4}'`

    if [ "$hostip" = "$1" ];
    then
        echo 1
        exit
    fi
fi

echo 0

spider.txt

sm.cn
baidu.com
baidu.jp
sogou.com
bytedance.com
msn.com
googlebot.com
本作品采用《CC 协议》,转载必须注明作者和本文链接
welcome come back
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
76
粉丝
20
喜欢
123
收藏
299
排名:373
访问:2.4 万
私信
所有博文
社区赞助商