Python處理超強(qiáng)反爬(TSec防火墻+CSS圖片背景偏移定位)
回復(fù)“書籍”即可獲贈(zèng)Python從入門到進(jìn)階共10本電子書
大家好,我是小小明,今天看到一個(gè)網(wǎng)站:

太神奇了,對(duì)于每個(gè)數(shù)字都用css背景圖片裁切得到一張小圖進(jìn)行顯示??梢源_定的是每個(gè)數(shù)字的圖片大小是8*17。
今天我們就一起玩玩。
開始測試
先嘗試用request讀取數(shù)據(jù),結(jié)果獲得一大堆極度混淆的JS的代碼。然后嘗試用selenium訪問,結(jié)果:

感覺這個(gè)防火墻有點(diǎn)叼。
算了,使用大殺器來隱藏模擬瀏覽器的特征:
from?selenium.webdriver?import?ChromeOptions
from?selenium?import?webdriver
browser?=?webdriver.Chrome()
option?=?ChromeOptions()
option.add_experimental_option('excludeSwitches',?['enable-automation'])
option.add_experimental_option('useAutomationExtension',?False)
option.add_argument(
????'user-agent=Mozilla/5.0?(Macintosh;?Intel?Mac?OS?X?10_15_7)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/86.0.4240.198?Safari/537.36')
option.add_argument("--disable-blink-features=AutomationControlled")
browser?=?webdriver.Chrome(options=option)
with?open('stealth.min.js')?as?f:
????js?=?f.read()
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',?{
????'source':?js
})
url?=?'http://hotels.huazhu.com/inthotel/detail/9005308'
browser.get(url)
這回頁面總算是出來了:

然而價(jià)格有時(shí)并不顯示,只能多刷新幾下頁面:

多次訪問之后,數(shù)據(jù)總算能看到了。
下面讓模擬器模擬點(diǎn)擊查看全部價(jià)格:
from?selenium.webdriver.common.by?import?By
from?selenium.webdriver.support.ui?import?WebDriverWait
from?selenium.webdriver.support?import?expected_conditions?as?EC
wait?=?WebDriverWait(browser,?10)
table?=?wait.until(EC.element_to_be_clickable(
????(By.CSS_SELECTOR,?'#Pdetail_part2?table')))
table.location_once_scrolled_into_view
{'x':?0,?'y':?0}
more_click?=?wait.until(EC.element_to_be_clickable(
????(By.CSS_SELECTOR,?'#Pdetail_part2?a[class="viewallprice"]')))
more_click.click()
這樣7條價(jià)格數(shù)據(jù),我們就全部能夠看到了。
下面我們開始抓取我們需要的數(shù)據(jù):
截圖獲取需要的數(shù)據(jù)
from?io?import?BytesIO
import?base64
from?PIL?import?Image
for?tr?in?table.find_elements_by_css_selector("table?tr[class^='room?first']"):
????print(tr.find_element_by_tag_name("h3").text)
????price?=?tr.find_element_by_css_selector(
????????"div>a[class^='totalprice']")
????img_data?=?base64.b64decode(price.screenshot_as_base64)
????img?=?Image.open(BytesIO(img_data))
????display(img)

調(diào)整鼠標(biāo)滑動(dòng)的位置后再來一次:

說明截圖有時(shí)截的并不準(zhǔn)確,想要精準(zhǔn)截圖也非常困難,因?yàn)闊o法通過程序準(zhǔn)確滾動(dòng)到應(yīng)該的位置。
光截圖就能搞定,那就太簡單了。
本文的主要目前還是為了演示解析CSS,咱們繼續(xù)采用解析法來獲取數(shù)據(jù):
解析CSS獲取圖片數(shù)據(jù)
首先我們解析出我們需要的數(shù)據(jù):
img_url?=?None
for?tr?in?table.find_elements_by_css_selector("table?tr[class^='room?first']"):
????name?=?tr.find_element_by_tag_name("h3").text
????print(name)
????price?=?tr.find_element_by_css_selector("div>a[class^='totalprice']")
????for?var?in?price.find_elements_by_tag_name("var"):
????????if?img_url?is?None:
????????????img_url?=?var.value_of_css_property("background-image")[5:-2]
????????????print(img_url)
????????position?=?var.value_of_css_property("background-position")
????????w,?h?=?map(lambda?x:?int(x[1:-2]),?position.split())
????????print(w,?h)
高級(jí)大床房
http://hotels.huazhu.com/Blur/Pic?b=81efc0b8e3094942a81d01e311864270
170?2
188?2
126?2
豪華大床房
170?2
33?2
56?2
豪華雙床房
145?2
2?2
188?2
觀景豪華大床房
145?2
170?2
111?2
行政大床房
33?2
56?2
56?2
行政雙床房
201?2
2?2
145?2
行政套房
2?2
2?2
33?2
188?2
嘗試下載CSS背景圖片:
browser.get(img_url)
結(jié)果又是被騰訊T-Sec Web應(yīng)用防火墻(WAF)攔截的頁面,說明直接用selenium下載圖片行不通。
用request下載呢?經(jīng)嘗試也老是被攔截。
最終,寫出了如下代碼(還能較為順利的獲取圖片數(shù)據(jù)):
import?requests
from?io?import?BytesIO
import?base64
from?PIL?import?Image
def?download_img(img_url):
????cookies?=?{o['name']:?o['value']?for?o?in?browser.get_cookies()}
????headers?=?{
????????"Accept":?"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
????????"Accept-Encoding":?"gzip,?deflate",
????????"Accept-Language":?"zh-CN,zh;q=0.9",
????????"Cache-Control":?"max-age=0",
????????"Connection":?"keep-alive",
????????"Host":?"hotels.huazhu.com",
????????"Upgrade-Insecure-Requests":?"1",
????????"User-Agent":?"Mozilla/5.0?(Windows?NT?10.0;?WOW64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/86.0.4240.198?Safari/537.36"
????}
????for?_?in?range(10):
????????r?=?requests.get(img_url,?headers=headers,?cookies=cookies)
????????if?r.status_code?==?200:
????????????break
????else:
????????return?None
????img?=?Image.open(BytesIO(r.content))
????return?img
img?=?download_img(img_url)
img

有了圖片,我們就可以裁剪出相應(yīng)的數(shù)字圖片并進(jìn)行拼接了。
對(duì)最后一條數(shù)據(jù)進(jìn)行測試:

可以看到解析和拼接的效果非常不錯(cuò)。
然后測試批量的數(shù)據(jù)提?。?/p>
img_url?=?None
for?tr?in?table.find_elements_by_css_selector("table?tr[class^='room?first']"):
????name?=?tr.find_element_by_tag_name("h3").text
????print(name)
????price?=?tr.find_element_by_css_selector("div>a[class^='totalprice']")
????var_el_s?=?price.find_elements_by_tag_name("var")
????n?=?len(var_el_s)
????target?=?Image.new('RGB',?(10?*?n,?17),?color=(255,?255,?255))
????for?i,?var?in?enumerate(var_el_s):
????????if?img_url?is?None:
????????????img_url?=?var.value_of_css_property("background-image")[5:-2]
????????????img?=?download_img(img_url)
????????position?=?var.value_of_css_property("background-position")
????????w,?h?=?map(lambda?x:?int(x[1:-2]),?position.split())
????????r?=?img.crop((w,?h,?w+8,?h+17))
????????target.paste(r,?(10*i,?0),?r)
????display(target)

可以看到已經(jīng)順利的得到了需要的結(jié)果,與網(wǎng)站看到的數(shù)據(jù)一致:

剩下的我們僅僅只需要對(duì)拼接好的圖片進(jìn)行圖像識(shí)別即可,或者就直接原有圖片形式保存。
圖像識(shí)別
關(guān)于圖像識(shí)別,有在線識(shí)別和離線識(shí)別兩種方式。在線文字可以考慮使用百度云,騰訊云等,根據(jù)官網(wǎng)提供的接口進(jìn)行操作。
下面呢,我們嘗試進(jìn)行離線文字識(shí)別,離線文字識(shí)別的準(zhǔn)確率往往不如在線。
為了更好的識(shí)別效果,我們先對(duì)圖片進(jìn)行二值化處理:
def?image_binarization(im,?threshold=250):
????Lim?=?im.convert("L")
????table?=?[0?if?i?else?1?for?i?in?range(256)]
????return?Lim.point(table,?"1")
image_binarization(target)
下面我們需要安裝pytesseract和Tesseract-OCR。
pytesseract是一個(gè)Python庫:
pip?insatll?pytesseract
Tesseract-OCR則需要在https://digi.bib.uni-mannheim.de/tesseract/下載安裝包。
由于網(wǎng)絡(luò)原因,我在https://www.liangchan.net/liangchan/11545.html下載了一個(gè)。
項(xiàng)目地址:https://github.com/tesseract-ocr/tesseract
安裝完成后,添加安裝路徑到path環(huán)境變量,命令行執(zhí)行后出現(xiàn)如下提示說明安裝成功:
C:\Users\ASUS>tesseract?-v
tesseract?v5.0.0.20190623
?leptonica-1.78.0
??libgif?5.1.4?:?libjpeg?8d?(libjpeg-turbo?1.5.3)?:?libpng?1.6.34?:?libtiff?4.0.9?:?zlib?1.2.11?:?libwebp?0.6.1?:?libopenjp2?2.3.0
?Found?AVX2
?Found?AVX
?Found?SSE
C:\Users\ASUS>
然后我們開始識(shí)別:
import?pytesseract
text?=?pytesseract.image_to_string(image_binarization(target)).strip()
print(text)
1183
于是可以開始進(jìn)行批量識(shí)別了:
import?pytesseract
for?tr?in?table.find_elements_by_css_selector("table?tr[class^='room?first']"):
????name?=?tr.find_element_by_tag_name("h3").text
????price?=?tr.find_element_by_css_selector("div>a[class^='totalprice']")
????var_el_s?=?price.find_elements_by_tag_name("var")
????n?=?len(var_el_s)
????target?=?Image.new('RGB',?(10?*?n,?17),?color=(255,?255,?255))
????for?i,?var?in?enumerate(var_el_s):
????????if?img_url?is?None:
????????????img_url?=?var.value_of_css_property("background-image")[5:-2]
????????????img?=?download_img(img_url)
????????position?=?var.value_of_css_property("background-position")
????????w,?h?=?map(lambda?x:?int(x[1:-2]),?position.split())
????????r?=?img.crop((w,?h,?w+8,?h+17))
????????target.paste(r,?(10*i,?0),?r)
????display(target)
????text?=?pytesseract.image_to_string(image_binarization(target)).strip()
????print(name,?text)

從結(jié)果可以看到,識(shí)別的準(zhǔn)確率還是非常高的,至少目前看到的全部都正確了。?
版權(quán)聲明:本文為CSDN博主「小小明-代碼實(shí)體」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/as604049322/article/details/118401598
????小伙伴們,快快用實(shí)踐一下吧!如果在學(xué)習(xí)過程中,有遇到任何問題,歡迎加我好友,我拉你進(jìn)Python學(xué)習(xí)交流群共同探討學(xué)習(xí)。
-------------------?End?-------------------
往期精彩文章推薦:
補(bǔ)充篇:盤點(diǎn)6種使用Python批量合并同一文件夾內(nèi)所有子文件夾下的Excel文件內(nèi)所有Sheet數(shù)據(jù)
盤點(diǎn)4種使用Python批量合并同一文件夾內(nèi)所有子文件夾下的Excel文件內(nèi)所有Sheet數(shù)據(jù)
手把手教你使用Python網(wǎng)絡(luò)爬蟲實(shí)現(xiàn)郵件定時(shí)發(fā)送(附源碼)

歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Python學(xué)習(xí)群請(qǐng)?jiān)诤笈_(tái)回復(fù)【入群】
萬水千山總是情,點(diǎn)個(gè)【在看】行不行
/今日留言主題/
隨便說一兩句吧~~
