今天在使用scrapy爬取网站的时候,因为网站的容量有限,不能拉取图片进行存储。但是源站的图片又存在着反盗链的限制,所以利用OpenResty做了一个图片代理的接口,实现了破解反盗链
的功能。(如果你要问我为什么使用OpenResty来做,我会告诉你因为网站就是用OpenResty开发的)
lua-resty-http
在OpenResty上面使用代理时,一般情况下会用proxy_pass或者lua-resty-http。当我们使用上面2种方式去实现的时候,会发现其不会解析域名的信息,从而造成无法发起http请求。这里面介绍一下使用动态DNS解析域名信息,然后发起HTTP请求。
首先需要在OpenResty安装目录下面添加lua-resty-http的依赖库:http.lua
和http_headers.lua
。将这2个文件放在resty下面即可。
下面介绍一下具体的实现,首先提供一个工具类
http_dns.lua
local http = require "resty.http"
local resolver = require "resty.dns.resolver"
local _M = {}
_M._VERSION="0.1"
function _M:http_request_with_dns( url, param )
-- get domain
local domain = ngx.re.match(url, [[//([\S]+?)/]])
domain = (domain and 1 == #domain and domain[1]) or nil
if not domain then
ngx.log(ngx.ERR, "get the domain fail from url:", url)
return {status=ngx.HTTP_BAD_REQUEST}
end
-- add param
if not param.headers then
param.headers = {}
end
param.headers.Host = domain
-- get domain ip
local domain_ip, err = self:get_domain_ip_by_dns(domain)
if not domain_ip then
ngx.log(ngx.ERR, "get the domain[", domain ,"] ip by dns failed:", err)
return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
end
-- http request
local httpc = http.new()
local temp_url = ngx.re.gsub(url, "//"..domain.."/", string.format("//%s/", domain_ip))
local res, err = httpc:request_uri(temp_url, param)
if err then
return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
end
-- httpc:request_uri 内部已经调用了keepalive,默认支持长连接
-- httpc:set_keepalive(1000, 100)
return res
end
-- 根据域名获取IP地址
function _M:get_domain_ip_by_dns( domain )
-- 这里写死了google的域名服务ip,要根据实际情况做调整(例如放到指定配置或数据库中)
local dns = "8.8.8.8"
local r, err = resolver:new{
nameservers = {dns, {dns, 53} },
retrans = 5, -- 5 retransmissions on receive timeout
timeout = 2000, -- 2 sec
}
if not r then
return nil, "failed to instantiate the resolver: " .. err
end
local answers, err = r:query(domain)
if not answers then
return nil, "failed to query the DNS server: " .. err
end
if answers.errcode then
return nil, "server returned error code: " .. answers.errcode .. ": " .. answers.errstr
end
for i, ans in ipairs(answers) do
if ans.address then
return ans.address
end
end
return nil, "not founded"
end
return _M
上面提供了请求的入口,提供一个URL和param参数即可。
下面提供了获取图片的HTTP接口,用以提供给自己的网站使用。
proxy.lua
local req = require "dispatch.req"
local httpdns = require "libs.http_dns"
local _M = {}
_M._VERSION="0.1"
-- 获取类型列表
function _M:picture()
local args = req.getArgs()
local imgUrl = args['imgUrl']
local param = {}
param['method'] = "GET"
param['headers'] = {
['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
['Accept-Language'] = 'zh-CN,zh;q=0.9',
['Referer'] = imgUrl
}
local res = httpdns:http_request_with_dns(imgUrl, param)
ngx.say(res.body)
end
return _M
到这里图片代理接口的工作就结束了,下面还需要对响应头做一些设定。
header_filter_by_lua_file lua/filter/header_filter.lua;
header_filter.lua
-- 设置响应头信息
local uri = ngx.var.request_uri
local result = string.find(uri, "example.com") -- 发现代理链接,则更改响应头信息
if result == nil then -- 普通请求
ngx.header["Content-Type"] = "text/html;charset=UTF-8"
else -- 代理请求
ngx.header["Content-Type"] = "image/jpeg"
end
最后在页面端使用如下方式就可以使用代理接口了。
/proxy/picture?imgUrl=http://image.example.com/image.jpg
参考:《OpenResty最佳实践》
链接:http://moguhu.com/article/detail?articleId=82