前言
同樣的,接上一篇 python爬蟲 - js逆向之woff字體反爬破解 ,而且也是同一個站的數據,只是是不同的反爬
網址:
aHR0cDovL3{防查找,刪除我,包括花括號}d3dy5kaWFuc{防查找,刪除我,包括花括號}GluZy5jb20vcmV2aWV3L{防查找,刪除我,包括花括號}zEwMDM1NDgxNjI=
分析
打開網站:
這個根據之前的經驗,現在直接找css,找映射關係就行了,所以看右邊的css,看到有很多東西,其中有兩個比較重要:
有沒有想過,爲啥給個ji6c6就能映射成【一】呢?爲什麼不是其他的,看到上面的background-image的url就很可疑了,打開這個url看看:
臥槽,這就是一堆字體啊,跟上一篇的woff字體有得一拼了,我猜哈,他就是通過這個的css來控制顯示的:
那行,我們隨便改個數值看看:
改之前:
改之後,只改了寬度的px,
再改個高度的px看看,我估計隨便輸入的值哈,所以肯定是對不齊的
那麼,再看下svg源碼的文字,
對得上,所以寬度跟高度改來改去,現在能夠確定的是,最開始的-336px和-214px,這個負值跟svg的值是相反的,svg是正數,css是負數,這個不重要哈,同時,我們也可以確認下:
用截圖工具大概取個值,
而源碼裏的是這樣的:
感覺有點不一樣,不過至少,寬度339px和336px能夠約等於上,但是高度對不上,很奇怪了。
沒事,再看看哈,把高度的值改爲0px看看,改完成這樣了
有老哥要問了,改爲0px幹嘛,現在不是要找高度的規律嗎,對啊,要找規律就要看看第一排的px是多少啊,上面的0px顯然不對,那麼說明第一排的縱座標的不是從0px,那稍微調下,調成一個與其他內容規整排列的
沒過一會兒,就調出一個規整排列的,結果是-13px
再看第二排是多少:57px
第三排:104px
第四排:141px
第五排:175px
第六排:214px
第七排:257px
。。。。
結果發現這些值並沒有很大的關聯,插值並不是一致的
看來這條路不通,換,再回過頭看css:
圈出來的就非常關鍵了,width爲14px,大膽猜測,應該就是一個字體的寬度,高度的話,應該就似乎height加上margin-top的值,極爲39px
那行,一個字體佔比就是寬度爲14,高度39,用這個值再次驗證一下,再回過頭看這個【一】,
再看,這裏數起來,麻煩,直接用python操作了:
ok,又對上了,再隨機找個吧,一次兩次,萬一還是巧合可以,隨機再找一個還能對上,那就一定是了:
就找個【是】
140/14=10
1723/39=44.1794...≈ 45
45排有點多,看源碼,源碼裏的y,1723大於上一排的1707,小於這一排的1746,那說明就是這一排了
然後直接說,也剛好是第10個數,ok了。而且這裏還看出一個規律,【是】的高度是1723px,在svg源碼裏:
1746>1723>1707,所以閉着眼睛也知道一定在這一行,那麼這裏還可以有一個方法,用常用的那幾個算法就能很快就能定位在哪一排,然後定位是哪一個字就行了,臥槽,感覺很簡單啊
規律找出來了,剩下的就是代碼調試了
調試
爲了方便調試,就不每操作一次實際請求一次,因爲這個平臺的風控挺強的,所以直接查看源碼,然後把內容保存到本地吧:
流程就是,讀取源碼,然後先把內容用xpath提取出來,把那些class屬性找出來,然後請求拿到css,再通過css內容找到設置的寬度高度,同時把css裏的svg的url請求一下,保存到本地,接着通過給定的寬度高度,取svg的原內容找就行了
所有需要i請求的,我都暫時保存在本地了,svg:
同時直接複製css裏的內容,手動放到content2.html文件裏了,給了一個style標籤
python實現
在執行的時候發現,當是42,23673px時,會報錯:
主要是沒有那麼多的縱向,那看來我們之前用height除以39還是有問題,不能涵蓋完整的情況
後面經過我的測試,發現以下規律:
1.高度除以39,結果如果不超過svg總排數的,減去一個通用值(假設爲2),即爲真實縱向位置
2.高度除以39,結果如果超過svg總排數的,如果值小於【總排數加通用值(假設爲2)】,先減去一個通用值(假設爲2)之後,如果這個值結果帶有小數,再四捨五入,如果結果沒有超過總排數,不用四捨五入,直接取整,即爲真實縱向位置
3.高度除以39,結果如果超過svg總排數的,如果值大於【總排數加通用值(假設爲2)】,直接按最後一排的數作爲真實縱向位置
也可能這套規律並不通用,至少目前來看沒有問題
我過了2天,再次打開這個網址,然後看到變了,源碼變得如下:
然後svg源碼也變了:
那這種,就沒法用二分法了,所i有,上面想到的思路,並不通用,那咋辦呢?這有點難爲人了哈
但是,仔細發現,上面發現的規律還是可以通用的
只是二分法不通用了,來,繼續看【一】,此時是【-126px -2737px】
126/14=9
2737/39=70.179.....
先找到70行,然後減2,得68,找第九個:
看是不是第9個,沒毛病:
看來,在svg的總排數以前的,都可以這麼算,現在找一個縱向相對比較大的:
56/14=4
3184/39=81.641
此時,81.641<80+2,那就直接減2得79,然後小數位四捨五入,得80,找80行第4個:
就看是不是真的是這個【粉】字了,拿到剛纔得class名【sr4q6】,到網頁裏覆蓋即可:
果然變成了【粉】字
再來確認上面的規律的第三種情況,找個總值大於80+2的試試,發現,此時的css裏沒有,那就姑且這麼認定了,直接寫代碼吧
同時更新下css和svg,以及html源碼
此時,縱向的發現沒毛病了,但是發現橫向的又出現問題了,比如 -462.0px -1470.0px
462/14= 33
1470/39 = 37.69
按上面的邏輯,應該是33,35,但是一找發現報錯:
看來橫向也不能直接一昧的除14啊,看看這個本來應該是啥字:
把【sr7u9】換上去看看,變成了【誰】字
看看svg裏【誰】:
臥槽,越來越迷糊了,這到底咋回事,接着再看,看橫向座標都大於等於400的看看:
找到這麼多:
一個一個看,不信找不到規律:
看【srjbq】,-504.0px -424.0px
本來是
36 10.89
但這裏取得是36 11
sr4qu -504.0px -2841.0px
算出是36,72.84 ==> 36,70
實際是36,71
sr5lf {
background: -518.0px -1324.0px
算出37 33.98 ==>33 31
實際37,33
.srwcf {
background: -532.0px -662.0px
算出 38 16.97 ==> 38 14
實際 38 17
.srqrx {
background: -546.0px -2841.0px
算出 39 72.84
實際 39 71
.srauc {
background: -546.0px -14.0px
算出 39 0.35
實際 39 1
.srkfy {
background: -532.0px -741.0px
算出 38 19
實際 38 19
.sr7k0 {
background: -504.0px -772.0px
算出 36 19.79
實際 36 20
當我在找規律的時候,我無意間又打開一個標籤:
哎~,這他媽,看屬性d的中間個數,嘿嘿,看來還是可以用二分法查找,ok,不再傻逼的找規律了,直接用算法,二分查找,或者冒泡的都可以,我這裏就不耽誤時間了,已經花了好幾天時間分析了,不能再拖了,我就寫個了最容易想的冒泡排序,也不考慮運行時間了,搞爬蟲的,不用太糾結這些東西。
ok,代碼:
至少現在沒報錯了,而且對了下都對上了,
還是這個靠譜點,上面總結的規律還是不能完美覆蓋問題,就以上這樣纔是最好的,
行,現在就差最後一個問題,就是文字的順序,看如下哈,沒有做svg的,文字就在外層,有做文字加密的未加密的並列,那這個順尋就有點不好操作了
再看當我直接用xpath的text時,是如下,所以,這個順序還真不好處理
咋辦,直接字符串替換吧
執行:
看第一條:
一直對喬一喬的印象都還不錯 這次到的他家機場店 環境還可以 阿姨服務態度也不錯 菜品口感也沒問題 就是出了一個小插曲 紅糖餈粑裏吃出了一塊鐵 着實嚇了一跳 還以爲把牙喫掉了店長處理態度也還行 給菜品打了個8折 說事後牙有什麼問題 隨時聯繫他 我們也不是訛人的人 小問題也不去麻煩商家了 溫馨提示一下估計這個鐵的問題應該還存在 進貨的時候仔細一點 這樣挺影響體驗感的
原文內容,完美對上,emoji圖的問題暫不考慮,就是一個靜態資源,想拼湊一下就是很簡單的問題
部分python代碼
content2.html和css.txt,svg.html,自行下載
def get_real_height(text, raw_height):
# 這個函數已經棄用,得到的結果不精準
length = len(text)
temp_h = raw_height / 39
if temp_h > length + 2: # 大於總排數+2
height = length
elif temp_h > length: # 大於總排數,小於總排數+2
height = length - 2
height = int(round(height, 0)) # 四捨五入
else: # 小於總排數
height = raw_height // 39
return height
def get_real_font(text, text_index, tuples=None):
if not tuples:
tuples = ('336.0', '214.0')
width, height = tuples
width = int(float(width))
height = int(float(height))
real_w = int(width / 14)
d, real_h = get_real_height_v2(text_index, height)
# print(12312312321, d, real_h)
# print(555555, real_h, real_w, tuples)
real_font = text[real_h][real_w]
return real_font
def get_css():
f = open('css.txt', encoding='utf-8')
source_data = f.read()
f.close()
cont = re.findall(r'\.(\w+) \{.*?background: -(.*?)px -(.*?)px;', source_data, re.S | re.M)
css_dict = {}
for c in cont:
css_dict[c[0]] = c[1:]
# print(css_dict)
return css_dict
def get_data_html():
css_dict = get_css()
f = open('svg.html', encoding='utf-8')
svg = f.read()
f.close()
html_source = etree.HTML(svg)
text = html_source.xpath('//text[position()>1]/text()')
if not text:
text = html_source.xpath('//textpath/text()')
text_index = html_source.xpath('//defs/path/@d')
text_index = [int(te.split(' ')[1]) for te in text_index]
f = open('content2.html', encoding='utf-8')
source_data = f.read()
source_data = source_data.replace('<svgmtsi class=', '').replace('></svgmtsi>', '')
f.close()
html_source2 = etree.HTML(source_data)
data = html_source2.xpath('//div[contains(@class,"review-words")]')
for item in data:
temp_dict = dict()
raw_cont = item.xpath('.//text()')
raw_cont = ''.join(raw_cont).strip() if raw_cont else ''
if not raw_cont:
continue
raw_cont = raw_cont.split('"')
raw_cont = [t for t in raw_cont if t]
real_cont = raw_cont[:]
# print(real_cont)
# print(raw_cont)
for ind, ra in enumerate(raw_cont):
if ra.startswith('sr'):
real_c = parser_svg_font(ra, css_dict, text, text_index)
real_cont[ind] = real_c
print('raw', real_cont)
temp_dict['raw_cont'] = ''.join(real_cont)
print('real', temp_dict['raw_cont'])
def parser_svg_font(s, css_dict, text, text_index):
cont = []
if isinstance(s, list):
cont = []
for i in s:
temp = css_dict.get(i)
real_font = get_real_font(text, text_index, temp)
# print(real_font)
cont.append(real_font)
elif isinstance(s, str):
temp = css_dict.get(s)
cont = get_real_font(text, text_index, temp)
# print(123123, cont)
return cont
def get_real_height_v2(data_list, target):
"""
:param data_list: 傳入的有序列表
:param target: 傳入要查找的目標值
"""
# data_list默認已排好序
if target in data_list:
return data_list.index(target)
for index, d in enumerate(data_list):
if d > target:
if index == 0:
return d, index
else:
if data_list[index - 1] < target:
return d, index
get_data_html()
然後有沒有可優化的地步,那肯定有的,那個處理svg部分的映射關係,和橫向座標除以14,然後判斷svg的classa是否是sr開頭的,計算映射真實文字的函數,等等,很多都可以優化的,具體細節就不去摳了,核心的邏輯能實現,剩下的就是完善優化了。