python爬蟲 - js逆向之svg字體反爬破解

前言

同樣的,接上一篇 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,用這個值再次驗證一下,再回過頭看這個【一】,

-336px    -214px
 
336/14 = 24
214/39 = 5.48717.... 可以這裏肯定是要做取整計算的,直接記爲6
 
也就是是是再第6排第24個字
 
那看下那邊的svg源碼是不是:

 

再看,這裏數起來,麻煩,直接用python操作了:

 

 

 

果然了,對上了,有點小激動,再次驗證下,看後面的【直】

 

 

196/14 = 14
57/39 =  1.4625... ≈ 2
 
看是不是第2排第14個:
 

 

 

 

 

 

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章