背景
一般有價值的並保有數據的網站或接口很容易被爬蟲,爬蟲會占用大量的流量資源,接下來我們參考歷史經驗,探索如何在.Net Core中利用UserAgent+rDNS雙解析方案來正確識別並且反爬蟲。
新建網絡爬蟲識別項目
在終端命令行中,基於DotNet-Cli
的new
命令新建名為WebBotRecognition
的webapi
項目,並且不需要https
,它將自動創建一個net5.0
的網絡接口項目。
dotnet new webapi -o WebBotRecognition --no-https
cd WebBotRecognition
切換到項目目錄
code .
用Visual Studio Code來打開當前目錄。
於是,我們便完成一個演示項目創建。
執行命令,先運行起來。
dotnet watch run
基於DotNet-Cli
的run
命令把項目中的模板示例先運行起來,確保一切正常,這里攜帶watch
參數來確保后面熱更新。
好了,接下來,我們刪掉自帶的Controller那些東西。
嘗試解析請求來源的User Agent
User Agent中文名為用戶代理,簡稱UA,它是一個特殊字符串頭,使得服務器能夠識別客戶使用的操作系統及版本、CPU類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。
在.Net Core中我們可以基於UAParser
組件包來實現對User Agent字符串的識別和解析。
安裝UAParser
dotnet add package UAParser
基於DotNet-Cli
的add package
命令在當前項目中安裝並添加UAParser
組件。
新建識別接口測試
在項目的Controllers
目錄下新建名為RecognizeController
的控制器,繼承自MVC中的ControllerBase
,並且設置好路由地址和新建一個名為GetUserAgent
的方法。
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace WebBotRecognition.Controllers
{
/// <summary>
/// 識別
/// </summary>
[Route("api/[controller]")]
public class RecognizeController : ControllerBase
{
/// <summary>
/// 獲取UserAgent
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetUserAgent")]
public async Task<JsonResult> GetUserAgent()
{
return new JsonResult(null);
}
}
}
利用UAParser組件來解析來源User Agent
/// <summary>
/// 獲取UserAgent
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetUserAgent")]
public async Task<JsonResult> GetUserAgent()
{
// 定義解析結果信息對象
ClientInfo clientInfo = null;
// 嘗試從頭部里面獲取User-Agent字符串
if(Request.Headers.TryGetValue("User-Agent", out var requestUserAgent) && !string.IsNullOrEmpty(requestUserAgent))
{
// 獲取UaParser實例
var uaParser = Parser.GetDefault();
// 解析User-Agent字符串
clientInfo = uaParser.Parse(requestUserAgent);
}
return new JsonResult(clientInfo);
}
這里我們可以從Request.Headers
里面通過TryGetValue
的方式相對安全的讀取到User Agent
字符串。
先獲取一個UaParser實例,然后基於它直接解析RequestUserAgent
即可。
我們通過PostMan調用來看看返回結果。
Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62
{
"string": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62",
"os": {
"family": "Windows",
"major": "10",
"minor": null,
"patch": null,
"patchMinor": null
},
"device": {
"isSpider": false,
"brand": "",
"family": "Other",
"model": ""
},
"userAgent": {
"family": "Edge",
"major": "90",
"minor": "0",
"patch": "818"
},
"ua": {
"family": "Edge",
"major": "90",
"minor": "0",
"patch": "818"
}
}
結果很清晰,如果我們需要進一步通過結果去判斷,就可以按這個結構來了。
我們多看幾組。
Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0
{
"string": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0",
"os": {
"family": "Windows",
"major": "7",
"minor": null,
"patch": null,
"patchMinor": null
},
"device": {
"isSpider": false,
"brand": "",
"family": "Other",
"model": ""
},
"userAgent": {
"family": "IE",
"major": "9",
"minor": "0",
"patch": null
},
"ua": {
"family": "IE",
"major": "9",
"minor": "0",
"patch": null
}
}
Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.1
{
"string": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.1",
"os": {
"family": "Mac OS X",
"major": "10",
"minor": "7",
"patch": "0",
"patchMinor": null
},
"device": {
"isSpider": false,
"brand": "Apple",
"family": "Mac",
"model": "Mac"
},
"userAgent": {
"family": "Chrome",
"major": "17",
"minor": "0",
"patch": "963"
},
"ua": {
"family": "Chrome",
"major": "17",
"minor": "0",
"patch": "963"
}
}
嘗試解析請求來源的Ip及反查域名
通常常規的搜索引擎的爬蟲機器在爬取你的網站的時候,是有固定爬蟲ip的,我們反爬蟲,其實不想誤傷他們,畢竟有些網站還是需要依賴SEO來提高曝光率的,但是偽裝成主流搜索引擎爬蟲的User-Agent的成本極低,那么我們能進一步判斷和區分呢?
這里,我們可以用到一個叫rDNS
的機制,大型的搜索引擎提供商,比如谷歌、百度以及微軟均會有固定的IP地址作為爬蟲的IP,並且會將這些IP做rDNS解析。可以通過查詢某個IP的rDNS解析記錄,來判斷這個IP到底是不是真的來自谷歌或者百度。
反向DNS(rDNS或RDNS)是從IP地址查找域名的域名服務(DNS)。 常規的DNS請求將解析給定域名的IP地址; 因此名稱為“ reverse”。反向DNS也稱為反向DNS查找和反向DNS。
反向DNS請求通常用於過濾垃圾郵件。 垃圾郵件發送者可以使用所需的任何域名輕松地設置發送電子郵件地址,包括銀行或受信任組織等合法域名。
接收電子郵件服務器可以通過使用反向DNS請求檢查發送IP地址來驗證傳入消息。 如果電子郵件是合法的,則rDNS解析器應與電子郵件地址的域匹配。 這種技術的缺點是某些合法的郵件服務器末端沒有正確的rDNS記錄設置才能正確響應,因為在許多情況下,它們的ISP必須設置這些記錄。
以Google
的爬蟲ip(66.249.66.1
)為例,我們可以通過nslookup
命令來反溯其域名。
嘗試在NetCore中獲取真實來源Ip
據說(暫時只是看網上說)在.Net Core中如果要獲取真實來源Ip,默認是不行的,但是可以基於一個官方的Microsoft.AspNetCore.HttpOverrides
包來實現,接下來我們安裝下這個包。
https://www.nuget.org/packages/Microsoft.AspNetCore.HttpOverrides/
dotnet add package Microsoft.AspNetCore.HttpOverrides
然后在項目的Startup.cs
文件的ConfigureServices
方法中添加ForwardedHeadersOptions
的配置支持。
public void ConfigureServices(IServiceCollection services)
{
// 添加Forwarded Headers Middleware配置
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebBotRecognition", Version = "v1" });
});
}
並且在Startup.cs
文件的Configure
方法中啟用ForwardedHeaders
中間件。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebBotRecognition v1"));
}
// Forwarded Headers Middleware
app.UseForwardedHeaders();
...
}
接下來,我們就可以獲取到來源真實IP地址了。
/// <summary>
/// 獲取真實客戶端來源Ip
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetRealClientIpAddress")]
public async Task<JsonResult> GetRealClientIpAddress()
{
// 通過Connection的RemoteIpAddress獲取IPV4的IP地址
var requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
return new JsonResult(requestIp);
}
通過真實來源Ip反向查找爬蟲域名
前面我們已經拿到了真實的請求IP,然后我們可以借助.Net中的DNS
類來反向解析Ip地址來模擬實現nslookup
命令的效果。
/// <summary>
/// 獲取rDNS反向域名
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetrDnsHostName")]
public async Task<JsonResult> GetrDnsHostName(string requestIp)
{
if(string.IsNullOrEmpty(requestIp))
{
// 通過Connection的RemoteIpAddress獲取IPV4的IP地址
requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
}
IPHostEntry iPHostEntry;
try
{
// 根據真實請求Ip反向解析請求Ip的域名
iPHostEntry = await Dns.GetHostEntryAsync(requestIp);
}
catch
{
iPHostEntry = null;
}
// 獲取請求的域名地址
var requestHostName = iPHostEntry?.HostName;
return new JsonResult(requestHostName);
}
通過requestIp
我們可以傳入指定Ip,這里我們用GoogleBot的Ip做個實驗,發現就能反向查找得到*.googlebot.com
這樣的域名。
這里注意,如果Ip反向查找不出來,會拋異常,我們要通過try-catch
來穩住這個情況。
結合User-Agent和rDNS技術來判斷真實的爬蟲
我們可以通過UaParser
來解析是否為爬蟲的User Agent
,同時輔助使用是否為爬蟲的來源Ip來判斷,這樣就比較穩妥。
Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
requestIp=66.249.66.1
/// <summary>
/// 是否來自真實的爬蟲機器人
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("IsRealSpiderBot")]
public async Task<bool> IsRealSpiderBot(string requestIp)
{
// 定義解析結果信息對象
ClientInfo clientInfo = null;
// 嘗試從頭部里面獲取User-Agent字符串
if(Request.Headers.TryGetValue("User-Agent", out var requestUserAgent) && !string.IsNullOrEmpty(requestUserAgent))
{
// 獲取UaParser實例
var uaParser = Parser.GetDefault();
// 解析User-Agent字符串
clientInfo = uaParser.Parse(requestUserAgent);
}
if(string.IsNullOrEmpty(requestIp))
{
// 通過Connection的RemoteIpAddress獲取IPV4的IP地址
requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
}
IPHostEntry iPHostEntry;
try
{
// 根據真實請求Ip反向解析請求Ip的域名
iPHostEntry = await Dns.GetHostEntryAsync(requestIp);
}
catch
{
iPHostEntry = null;
}
// 獲取請求的域名地址
var requestHostName = iPHostEntry?.HostName;
// 當反向解析域名不為空且UserAgent解析結果為爬蟲時,才真正為爬蟲機器人
var isRealSpiderBot = !string.IsNullOrEmpty(requestHostName) && (clientInfo?.Device?.IsSpider ?? false);
return isRealSpiderBot;
}
這里我們還是以Google Bot
的ip為例,調用下,結果符合預期。
常見的搜索引擎爬蟲的User Agent
1. GoogleBot
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
2. Bingbot
Mozilla/5.0 (compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm)
3. Slurp Bot
Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)
4. DuckDuckBot
DuckDuckBot/1.0; (+http://duckduckgo.com/duckduckbot.html)
5. Baiduspider
Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
6. Yandex Bot
Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)
7. Sogou Spider
Sogou Pic Spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou head spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou Orion spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou-Test-Spider/4.0 (compatible; MSIE 5.5; Windows 98)
8. Exabot
Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Exabot-Thumbnails)
Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)
9. Facebook external hit
facebot
facebookexternalhit/1.0 (+http://www.facebook.com/externalhit_uatext.php)
facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)
10. Alexa crawler
ia_archiver (+http://www.alexa.com/site/help/webmasters; crawler@alexa.com)