詳細解析反爬手段以及處理方案
前言
互聯網時代,無論在工作上,還是生活上都離不開網絡,而網絡能給我們帶來什么?
新聞,小說,資料,各行業的數據或者報表等等;
比如:快畢業了為了論文,在各種網站上爬取需要的數據進行分析;還有一些為了興趣愛好,爬取各種類型的圖片,視頻,文章,數據等。
各網站的開發人員為了約束這種行為,開始絞盡腦汁,采取各種手段去約束爬蟲,於是,有了反爬機制!
常見反爬機制
1,通過對 User-Agent 過濾來控制訪問
無論是瀏覽器,程序,還是爬蟲,在向服務器發起網絡請求時,都會先發送一個請求頭文件 headers ,
比如:
{
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,zh-HK;q=0.8",
"Host": "httpbin.org",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-5fe2b4fe-6e4edc1c4dbbe85a3c25492b"
}
}
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
請求頭大部分的字段主要是瀏覽器向服務端 “表明自己的身份”用的,很多網站都會建立 user-agent 白名單,只有在正常范圍內的 user-agent 才能正常訪問 。
user-agent 是一個閱讀器標志,用戶都是一中閱讀器,網站很粗糙的辨別你有咩有作弊,必須要結構不同的閱讀器標志,不然就會認為你是爬蟲,寧殺錯,不放過,你說氣不氣;
缺點:很容易偽造頭部
處理方案:
修改閱讀器標志,模擬其他閱讀器的標志(定義一個標志庫,隨機獲取一個),能夠通過API接口實現各種閱讀器的收集模擬;
# 定義 user-agent/標志庫
# 第一種方法
def get_user_agent():
"""
模擬headers的user-agent字段,
返回一個隨機的user-agent字典類型的鍵值對
"""
agents = [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0;) Gecko/20100101 Firefox/61.0",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
]
fakeheader = {}
fakeheader['User-agent'] = agents[random.randint(0,len(agents))]
return fakeheader
def get_html(url):
try:
r= requests.get(url, timeout=30,headers=get_user_agent())
r.raise_for_status
r.encoding = r.apparent_encding
return r.status_code
except:
return "someting wrong!"
# 第二種方法
def get_html():
agents = [...]
headers = {
'User-Agent': random.choice(user_agent_list),
'Referer': 'https://www.baidu.com',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
'Cookie': '...'
}
try:
r= requests.get(url, timeout=30,headers=headers)
r.raise_for_status
r.encoding = r.apparent_encding
return r.status_code
except:
return "someting wrong!"
2,IP訪問頻率限制
如果一個固定的IP在短暫的時間內,訪問太快或者快速大量的訪問一個網站,網站就會認為你不是一個人后台管理員可以編寫IP限制,不讓該IP繼續訪問
解決方案:
設定頻率的閾值,限制訪問時間;
比如,每隔5秒訪問一次頁面,但是如果遇到比較刁鑽的網站,會去檢測你的訪問時間,如果你長時間訪問了幾十個頁面,且每次訪問的時間間隔都剛好5秒鍾,又不是機器怎么可能做到這么准確的時間間隔呢?如果使用的是selenium來訪問的話,也就不會出現這個問題了,因為它本來也要打開頁面,雖然效率不咋地,但是繞開了頻率檢查的反爬機制。
訪問時間設定一個隨機值。例如0-10之間的隨機秒數
修改訪問頻率的場景如下:
(1)使用 request 腳本爬蟲
# 放在request請求之后
random = random.randint(3,10)
time.sleep(random)
(2)scrapy 腳本爬蟲或者scrapy_redis 分布式爬蟲
RETRY_TIMES = 10 # 重新請求次數
DOWNLOAD_DELAY = 1 # 間隔時間
CONCURRENT_REQUESTS = 5 # 請求並發數
https://www.cnblogs.com/beiyi888/p/11283823.html
(3)針對業務性寫爬蟲
個別網站,比如,hwt,服務器會限制你的訪問頻率,但是不會封IP,頁面將持續顯示403(服務器拒絕訪問),偶爾顯示200(請求成功),那么就證明(前提是我們設置過請求頭等信息),這樣的反爬機制,只是限制了請求的頻率,但是並不會影響到正常的內容采集,這種可以忽略的情況並不多見,所以我們還是更具網站業務有針對性的編寫爬蟲
(4)服務器性能原因,相應較慢(響應超時,導致終止請求)
小網站出現的比較多(DYW),在我們將請求的參數都安排好后,卻發現,由於服務器的性能原因,采集程序持續報網頁404,出現這種情況只能延長響應超時的時長;
try:
resp = requests.get(url=url,headers=headers,proxies=proxies,verify=False,timeout=120)
(5)代理IP或者分布式爬蟲
如果對頁的爬蟲的效率有要求,那就不能通過設定訪問時間間隔的方法來繞過頻率檢查了。
代理IP訪問可以解決這個問題。如果用100個代理IP訪問100個頁面,可以給網站造成一種100個人,每個人訪問1頁的錯覺。
但是代理IP經常不穩定,隨便搜一個”免費代理“,會出現很多網站,每個網站也會給你很多的代理IP,但實際上,真正可以用的代理IP並不多。你需要維護一個可用的代理IP池,但是一個免費的代理IP,也許會在幾分鍾后失效,這也是很正常的事情。網上有免費和付費的,但是質量都層次不齊。如果是企業里需要的話,可以通過自己購買集群雲服務來自建代理池。
可以使用http://icanhazip.com/ 這個網站來檢測你的代理IP是否設定成功。當你直接使用瀏覽器訪問這個網站的時候,它會返回你的IP地址。
通過requests,我們可以設置代理訪問網站,在requests的get方法中,有一個proxies參數,它接收的數據是一個字典,在這個字典中我們可以設置代理。
大家可以在requests的官方中文文檔中看到關於設置代理的更多信息:http://docs.python-requests.org/zh_CN/latest/user/advanced.html#proxies
我選擇第一個HTTP類型的代理來給大家做測試,運行效果如下圖所示:
import requests
proxy = {'http': 'http://182.253.69.34:8080'}
html = requests.get('http://icanhazip.com/')
html_proxy = requests.get('http://icanhazip.com/', proxies=peoxy)
分布式爬蟲會部署在多台服務器上,每個服務器上的爬蟲統一從一個地方拿網址。這樣平均下來每個服務器訪問網站的頻率也就降低了。
設定IP代理池
def get_proxy():
"""
模擬代理池
返回一個字典類型的鍵值對
"""
proxy = [
"http://182.253.69.34:8080",
"http://39.81.63.233:9000",
"http://58.253.153.164:9999",
"http://182.92.233.137:8118",
"http://41.65.201.162:8080",
"http://45.224.149.219:8080",
]
fakepxs = {}
fakepxs['http'] = proxy[random.randint(0, len(proxy))]
return fakepxs
驗證代理IP是否失效?
ip.txt 文件的內容如下:
182.253.69.34:8080
39.81.63.233:9000
58.253.153.164:9999
182.92.233.137:8118
41.65.201.162:8080
142.93.57.37:80
45.224.149.219:8080
27.220.49.194:9000
5.9.112.247:3128
36.250.156.230:9999
36.249.48.6:9999
proxy_agent.py 文件
import random
import requests
ua_list = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36",
"Mozilla / 5.0(Windows NT 6.1;WOW64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 45.0.2454.101Safari / 537.36"
]
def _load_text_file(txt_file):
"""
加載text文件
:param txt_file:
:return:
"""
text_content = []
with open(txt_file, 'r', encoding='utf-8-sig') as text_file:
while True:
content = text_file.readline()
if not content:
break
line = content.strip('\n')
text_content.append(line)
FileUtils._check_format(txt_file, text_content)
return text_content
# 隨機取出一個Ip
def get_random_ip(path):
black_ = []
client = _load_text_file(path)
print(client)
url_for_test = 'https://baidu.com/'
user_agent = random.choice(ua_list)
headers = {
'Accept': 'text/html, application/xhtml+xml, application/xml;',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Referer': 'https://baidu.com/',
'User-Agent': user_agent
}
for useful_proxy in client:
proxy = {
'http': 'http://' + useful_proxy,
#'https': 'http://' + useful_proxy
}
response = requests.get(url_for_test,headers=headers,proxies=proxy,timeout=10)
if response.status_code == 200:
return useful_proxy
else:
print('此ip - {0} 已失效!!!'.format(useful_proxy))
black_.append(useful_proxy)
get_random_ip(path)
if __name__ == '__main__':
path = r'ip.txt'
print(get_random_ip(path))
session 訪問限制
后台統計登錄用戶的操作,比如短時間的點擊時間,請求數據事件,與正常值比對,用於區分用戶是否處理異常狀態,如果是,則限制登錄用戶操作權限。比如,每次訪問之后,鏟除緩存,這樣能有用躲避部分網站的檢測,如果都是新鏈接從IP宣布,也會被斷定回絕(直接403回絕訪問),因此有些爬蟲會去剖析網站的cookies 緩存內容,然后進行修改。
缺點:需要增加數據埋點功能,閾值設置不好,容易造成誤操作
解決方案:注冊多個賬號,模擬正常操作
Spider Trap
蜘蛛陷阱導致網絡爬蟲進入無限循環之類的東西,這回浪費蜘蛛的資源,降低生產力,並且在編寫得不好得爬蟲得情況下,可能導致程序崩潰。禮貌蜘蛛在不同主機之間交替請求,並且不會每隔幾秒鍾從同一服務器請求多次文檔,這意味着”禮貌“網絡爬蟲比”不禮貌“爬蟲得影響程度要小得多。
反爬方式:創建無限深度得目錄結構
缺點:反爬方式1,2會增加很多無用目錄或文件,造成資源浪費,也對正常的SEO十分不友好,可能會被懲罰。
# 比如:
HTTP://example.com/bar/foo/bar/foo/bar/foo/bar /
動態頁面,為網絡爬蟲生成無限數量的文檔。如由算法生成雜亂的文章頁面。
文檔中填充了大量字符,使解析文檔的詞法分析器崩潰。
此外,帶蜘蛛陷阱的網站通常都有robots.txt告訴機器人不要進入陷阱,因此合法的“禮貌”機器人不會陷入陷阱,而忽視robots.txt設置的“不禮貌”機器人會受到陷阱的影響。
解決方案:
把網頁按照所引用的css文件進行聚類,通過控制類里最大能包含的網頁數量防止爬蟲進入trap后出不來,對不含css的網頁會給一個penalty,限制它能產生的鏈接數量。這個辦法理論上不保證能避免爬蟲陷入死循環,但是實際上這個方案工作得挺好,因為絕大多數網頁都使用了css,動態網頁更是如此。
驗證碼驗證
驗證碼(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自動區分計算機和人類的圖靈測試)的縮寫,是一種區分用戶是計算機還是人的公共全自動程序。可以防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定注冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試,實際上用驗證碼是現在很多網站通行的方式,我們利用比較簡易的方式實現了這個功能。這個問題可以由計算機生成並評判,但是必須只有人類才能解答。由於 計算機無法解答CAPTCHA的問題,所以回答出問題的用戶就可以被認為是人類。
(1)圖片驗證碼
復雜性
打碼平台雇佣了人力,專門幫人識別驗證碼。識別完把結果傳回去。總共的過程用不了幾秒時間。這樣的打碼平台還有記憶功能。圖片被識別為“鍋鏟”之后,那么下次這張圖片再出現的時候,系統就直接判斷它是“鍋鏟”。時間一長,圖片驗證碼服務器里的圖片就被標記完了,機器就能自動識別了。
簡單型
OCR識別技術(利用python第三方庫--tesserocr)來識別,經過灰度變換和二值化后,由模糊的驗證碼背景變成清晰可見的驗證碼。對於容易迷惑人得圖片驗證碼,在這種驗證碼,語言一般自帶圖形庫,添加上扭曲就成了這個樣子,我們可以利用9萬張圖片進行訓練,完成類似人的精准度,到達識別驗證碼的效果
(2)短信驗證碼
用Webbrowser技術,模擬用戶打開短信的行為,最終獲取短信驗證碼。
(3)計算題圖片驗證碼
把所有可能出現的漢字都人工取出來,保存為黑白圖片,把驗證碼按照字體顏色二值化,去除噪點,然后將所有圖片依次與之進行像素對比,計算出相似值,找到最像的那張圖片
(4)滑動驗證碼
我們可以利用圖片的像素作為線索,確定好基本屬性值,查看位置的差值,對於差值超過基本屬性值,我們就可以確定圖片的大概位置。
(5)圖案驗證碼
對於這種每次拖動的順序不一樣,結果就不一樣,我們怎么做來識別呢?
利用機器學習所有的拖動順序,利用1萬張圖片進行訓練,完成類似人的操作,最終將其識別
利用selenium技術來模擬人的拖動順序,窮盡所有拖動方式,這樣達到是別的效果
(6)標記倒立文字驗證碼
首先點擊前兩個倒立的文字,可確定7個文字的坐標, 驗證碼中7個漢字的位置是確定的,只需要提前確認每個字所在的坐標並將其放入列表中,然后人工確定倒立文字的文字序號,將列表中序號對應的坐標即可實現成功登錄。
解決方法:接入第三方驗證碼平台,實時破解網站得驗證碼
缺點:影響正常得用戶體驗操作,驗證碼越復雜,網站體驗感越差。
通過robots.txt 來限制爬蟲
robots.txt(統一小寫)是一種存放於網站根目錄下的ASCII編碼的文本文件,它通常告訴網絡搜索引擎的漫游器(又稱網絡蜘蛛),此網站中的哪些內容是不應被搜索引擎的漫游器獲取的,哪些是可以被漫游器獲取的。因為一些系統中的URL是大小寫敏感的,所以robots.txt的文件名應統一為小寫。robots.txt應放置於網站的根目錄下。如果想單獨定義搜索引擎的漫游器訪問子目錄時的行為,那么可以將自定的設置合並到根目錄下的robots.txt,或者使用robots元數據(Metadata,又稱元數據)。
robots.txt協議並不是一個規范,而只是約定俗成的,所以並不能保證網站的隱私。注意robots.txt是用字符串比較來確定是否獲取URL,所以目錄末尾有與沒有斜杠“/”表示的是不同的URL。robots.txt允許使用類似"Disallow: *.gif"這樣的通配符。
https://itunes.apple.com/robots.txt
User-agent: *
Disallow: /WebObjects/MZFastFinance.woa
Disallow: /WebObjects/MZFinance.woa
Disallow: /WebObjects/MZPersonalizer.woa
Disallow: /WebObjects/MZStoreElements.woa
Disallow: /station/idst.
Disallow: /WebObjects/*
Allow: /WebObjects/MZStore.woa/wa/viewMultiRoom?*
Disallow: /search*
Disallow: /*/rss/*
Disallow: /*/lookup?
User-agent: Googlebot
Disallow: /*/album/*/*?i=*
Disallow: /*/tv-season/*/*?i=*
Disallow: /*/podcast/*/*?i=*
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_1.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_2.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_3.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_4.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_5.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_6.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_7.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_8.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_9.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_10.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_31.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_41.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_51.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_61.xml
可以參考 https://www.moyublog.com/notes/323.html
解決方案:
如果使用scrapy框架,只需將settings文件里的ROBOTSTXT_OBEY 設置值為 False
數據動態加載
python的requests庫只能爬取靜態頁面,爬取不了動態加載的頁面。使用JS加載數據方式,能提高爬蟲門檻。
解決方案:
抓包獲取數據url
通過抓包方式可以獲取數據的請求url,再通過分析和更改url參數來進行數據的抓取。
示例:
看 https://image.baidu.com 這部分的包。可以看到,這部分包里面,search下面的那個 url和我們訪問的地址完全是一樣的,但是它的response卻包含了js代碼。
通過抓包工具,尋找URL得規律,對URL進行構造便可獲取所有照片
使用selenium
通過使用selenium來實現模擬用戶操作瀏覽器,然后結合BeautifulSoup等包來解析網頁通過這種方法獲取數據,簡單,也比較直觀,缺點是速度比較慢。
缺點:如果數據API沒做加密處理,容易曝光接口,讓爬蟲用戶更容易獲取數據。
數據加密-使用加密算法
前端加密
通過對查詢參數、user-agent、驗證碼、cookie等前端數據進行加密生成一串加密指令,將加密指令作為參數,再進行服務器數據請求。該加密參數為空或者錯誤,服務器都不對請求進行響應。
網站的請求如果加密過,那就看不清請求的本來面目,這時候只能靠猜想,通常加密會選用簡略的編碼,如:base64、urlEncode等,如果過於復雜,只能窮盡的去嘗試;
服務器端加密
在服務器端同樣有一段加密邏輯,生成一串編碼,與請求的編碼進行匹配,匹配通過則會返回數據。
缺點:加密算法明文寫在JS里,爬蟲用戶還是可以分析出來。
解決方案:
JS加密破解方式,就是要找到JS的加密代碼,然后使用第三方庫js2py在Python中運行JS代碼,從而得到相應的編碼。
案例參考:
https://blog.csdn.net/lsh19950928/article/details/81585881
數據加密-使用字體文件映射
服務器端根據字體映射文件先將客戶端查詢的數據進行變換再傳回前端,前端根據字體文件進行逆向解密。
映射方式可以是數字亂序顯示,這樣爬蟲可以爬取數據,但是數據是錯誤的。
破解方式:
其實,如果能看懂JS代碼,這樣的方式還是很容易破解的,所以需要做以下幾個操作來加大破解難度。
對JS加密
使用多個不同的字體文件,然后約定使用指定字體文件方式,比如時間戳取模,這樣每次爬取到的數據映射方式都不一樣,映射結果就不一樣,極大提高了破解的難度。
該種方式相比使用加密算法方式難度更高,因為加密算法是固定的幾種,對方很容易獲取並破解,而字體文件映射可以按任意規則映射,正常的數據使之錯誤顯示,爬蟲不容易察覺。
參考案例:https://www.jianshu.com/p/f79d8e674768
缺點:需要生成字體文件,增加網站加載資源的體量。
非可視區域遮擋
此方式主要針對使用senlium進行的爬蟲,如果模擬界面未進入可視區域,則對未見數據進行遮擋,防止senlium的click()操作。這種方式只能稍稍降低爬蟲的爬取速度,並不能阻止繼續進行數據爬取。
