最近一直在用django寫一個個人音樂在線播放平臺。
其中在網頁數據保護方面,我採取了很多種的反爬蟲措施,所以在本篇文章中,我從源碼和實際操作上給大家分析下我所使用的反爬蟲及其對應的破解技巧。
首先我們聲明的是,爬蟲和反爬蟲沒有高低之分,雖然總有一種方法能突破你的安全保護。
爬蟲就像是一個釘子,反爬則是一扇鐵窗。釘子堅持不懈,總能搞破窗。但是窗戶是不能只針對於一點全力打造的。從此,修修補補,一般雙方不下班不休。
下面我把反爬和反反爬分開來寫。這樣愛好不同的人可以各取所需。
反爬蟲:
1、我在django-views中設置了登錄身份驗證,設置了裝飾器,通過META.get獲取請求頭,限制請求頭和訪問間隔。
lxcsdn = ["https://blog.csdn.net/weixin_43582101/column/info/33034",
"https://blog.csdn.net/weixin_43582101/article/details/86563910",
"https://blog.csdn.net/weixin_43582101/article/details/86567367",
]
'''設置請求頭和訪問間隔'''
ips = [None] #存儲客戶端最後一次訪問的ip地址 空列表也是列表,None類型只有None一個值
## ips=[None] 爲了防止 list index out of range。
last = 0 #存儲客服端最後一次訪問時間
def isCraw(func):
def wrapper(*args,**kwargs):
global ips,last #聲明全局變量
#request.META 是一個字典,包含了所有本次HTTP請求的Header信息
agent = args[0].META.get('HTTP_USER_AGENT') #獲取請求頭部信息
if 'Mozilla' not in agent and 'Safari' not in agent and 'Chrome'not in agent:
return HttpResponse(random.choice(lxcsdn))
else:
ip = args[0].META.get('REMOTE_ADDR') #客戶端IP地址
# 什麼是remote_addr:
# remote_addr 是服務端根據請求TCP包的ip指定的。假設從client到server中間沒有任何代理,
# 那麼web服務器(Nginx,Apache等)就會把client的IP設爲IPremote_addr;
# 如果存在代理轉發HTTP請求,web服務器會把最後一次代理服務器的IP設置爲remote_addr
now = time.time()
if ip==ips[0] and now-last<2: #爲了防止誤傷
return HttpResponse("Are you curious about what happened? If you read this sentence carefully, you will know what happened. so the page is not found , But is you didn't find it ! " )
last = now
ips.pop()
ips.append(ip)
return func(*args,**kwargs)
return wrapper
!記得上面是一個裝飾器。
mete.get可以獲取請求頭部的信息。如果沒有請求頭User-Agent的話,就只能請求到我的csdn博客鏈接。在最下面記錄了上一次到這一次訪問的ip時間,如果頻率小於2秒就會跳轉到錯誤頁面。
2、設置了cookie和登錄成功後的session,並通過url編碼方式隱藏cookie
cookie和session是網站的必須品,但是有的爬蟲會通過cookie直接省去登錄來訪問你的主頁從而提取數據。
因爲url編碼方式不支持中文,所以我們可以設置爲中文及外文的cookie。
大致如下:
response.set_cookie(getPassword(urlquote("你好")),getPassword(urlquote("公正法治民主")))
response.set_cookie((urlquote("아니카시유")),(urlquote("아니카시유아니카시유")))
在控制檯上顯示的內容則是:
這可以起到一定程度的反爬蟲作用。當然你也可以使用已經加密過的數據,來加大爬蟲破解的難度。
3、設置了自動刷新login頁面,30秒計時。
爲了防止selenuim和無界面瀏覽器的侵襲,只能儘量的犧牲一點用戶體驗,我們把瀏覽器刷新時間控制在30秒左右,運行緩慢的無界面瀏覽器就會很難受了。
這裏我用JS來實現的。
function myrefresh() {
window.location.reload();
}
setTimeout('myrefresh()', 30000);
4、上面設置了30秒的時間是不夠的,所以我在登錄界面設置了驗證碼,這樣也能隔絕一大部分的爬蟲初學者。
首先我寫了一個鼠標移入顯示圖片的方法。因爲要考慮到用戶體驗,又要考慮到selenuim的登錄。所以我設置的標籤爲,鼠標移入外面的div,顯示裏面的圖片,所以selenuim去獲取裏面圖片的屬性時,是獲取不到的。
點擊刷新驗證碼:爲什麼要寫這個呢,對方如果通過點擊觸發圖片的話,可能會在點擊很觸發的圖片已經跟開始的不一樣了,這個我也沒試過,應該是這樣。
function refresh(ths)
{ths.src = ths.src + '?'}
這個寫起來特別簡單,只需要在原click方法上給url+上一個?就可以重複請求。(如果兩次請求url相同是不能刷新圖片的)
下面是驗證碼生成的代碼:
(本篇代碼爲了方便講解,我都寫在視圖中,有不懂請留言)
def auth_code(request):
size = (143,40)
width,height = size
font_list = list("abcdefghklmpqrstuvwxyz*#%0123456789")
c_chars = " ".join(random.sample(font_list,5))
request.session['auth'] = c_chars.replace(' ','')
img = Image.new("RGB",size,(30,31,42))
draw = ImageDraw.Draw(img)
# for i in range(random.randint(1,7)):
# draw.line(
# [
# (random.randint(0, 150), random.randint(0, 150)),
# (random.randint(0, 150), random.randint(0, 150))
# ],
# fill=(0, 0, 0)
# )
# for j in range(1000):
# draw.point(
# ( random.randint(0, 150),
# random.randint(0, 150)
# ),
# fill = (0, 0, 0)
# )
font = ImageFont.truetype("simsun.ttc", 23)
draw.text((5,4),c_chars,font=font,fill="white")
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params)
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
img.save(buf,'png')
return HttpResponse(buf.getvalue(),)
5、因爲只是這樣我感覺力度還是不夠,有耐心的selenuim還是可以很快破解的。所以我從屬性上來限制一下PhantomJS和selenuim。
這個限制是用js來寫的。
…我剛突然發現我把這頁的JS混淆壓縮加密了,不過可以拿去直接用。
window.function(){
if (navigator.webdriver) {
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('0 1=\'\';',2,2,'var|a'.split('|'),0,{}));
var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('0 1=\'\';0 1=\'\';0 1=\'\';0 1=\'\';',2,2,'var|a'.split('|'),0,{}));
window.location.href="https://www.hexu0614.com/blog/blogs/12/"}
else if(/HeadlessChrome/.test(window.navigator.userAgent)) {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/6/";}
else if(navigator.plugins.length === 0 || !navigator.plugins.length) {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/33/";}
else if(navigator.languages === "") {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/5/";}
// {# 獲取圖片判斷 #}
var body = document.getElementsByTagName("body")[0];
var image = document.createElement("img");
image.src = "/static/tboys/img/login_ico.png";
image.setAttribute("id", "auth_img");
body.appendChild(image);
image.onerror = function(){
if(image.width === 0 && image.height === 0) {
window.location.href="https://www.hexu0614.com";
}}
};
首先上文中的方框是selenuim不能是別的字符集,會影響他的讀取操作,有限制作用,如果添加的夠多的話,可能會把他搞崩潰。
第一個if的作用是,如果訪問的驅動是webdriver的話,就把頁面跳轉到別的地方。後面的兩個作用是:通過判斷訪問來源的長度和語言來判斷是不是無界面瀏覽器。最後面那個是通過對圖片的識別方式來判斷是不是無界面瀏覽器。
因爲無界面瀏覽器在訪問圖片的時候,他的訪問得到的圖片寬高都是0;並且無界面瀏覽器的語言默認爲空,長度默認爲0.
6、設置了form表單post請求,通過display:none+hidden進行加密隱藏
這個是常見的form表單。我通過大量的display:none 和標籤的隱藏,來干擾爬蟲對頁面的判斷和控制。從而去增加爬蟲的難度。
並且可以給爬蟲佈置下一個蜜罐,讓他一步一步進入錯誤的路徑,並且毫不知情…
7、將主頁音樂鏈接數據保存在.js中,JS文件經過混淆壓縮加密。
假設對方破解了驗證,通過登錄到了主頁中。也是很難發現他所需要的數據的。頁面信息中並沒有url信息。
我的url如下:
由於我的數據沒有更新完成,這裏並沒有使用JS加密。
當你想加密JS可以去下圖所示
或者自己找一個靠譜的,因爲有的時候加密完,會把功能也給加密沒了。
大家要一邊嘗試一邊加密。
8、設置iframe。iframe 元素會創建包含另外一個文檔的內聯框架(即行內框架)
效果是這樣的。
頁面:
控制檯顯示如下:
你可以通過多個iframe來干擾爬蟲的視角,隱藏自己的url。
9、比較難受人的一種css方法,通過字符集映射來改變頁面信息和源碼信息的不同。
利用前端字體文件(.ttf)混淆文本來阻止爬蟲爬取網站數據.這個我不詳細介紹了,舉幾個列子,大家可以網上搜索下具體操作。
- 1 FONT-FACE拼湊式
- 2 BACKGROUND拼湊式
- 3 字符穿插式
- 4 僞元素隱藏式
- 5 元素定位覆蓋式
- 6 IFRAME異步加載式
- 7 字符分割式
- 8 字符集替換式
看起來是不是很刺激。
大家可以用fontcreator工具來構建自己的字體,然後在html頁面導入自定義字體。
還有點什麼反爬方式,我忘記了。。先不寫了。
下面我們來逐條破解上面的反爬措施。
反反爬蟲:
1、根據表單formdata數據,去提交對應數據。
2、仔細仔細再仔細
3、通過urldncode解密
4、通過圖像識別,打碼平臺,或者軟件工具來進行驗證碼處理
5、JS數據清洗,數據解密。
6、根據網站設置的限制方式,改變我們請求頻率和請求內容。
7、用fontcreator來找出字符集加密規律。
8、使用無頭瀏覽器,就去逐情改變他的屬性
算了 不寫了。反反爬的攻略太多了。大致上瞭解一下網上到處都是。
等到你越到的時候再查就行了。