前言
同樣的,接上一篇 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開頭的,計算映射真實文字的函數,等等,很多都可以優化的,具體細節就不去摳了,核心的邏輯能實現,剩下的就是完善優化了。