這可能是爬蟲技術(shù)無法破解的加密格式菩掏。
案例分析
以58房產(chǎn)網(wǎng)站為例,我們爬取的房價以及其他很多數(shù)字都是亂碼辜梳,閏龤龤龤元/月凭峡,龒室龤廳龒衛(wèi)龥龤㎡选泻。
右鍵檢查元素會發(fā)覺乏沸,看上去正常的數(shù)字淫茵,在html代碼中卻是亂碼。
右側(cè)可以注意到這樣的元素使用了奇怪的font-family:fangchan-secret
(房產(chǎn)-加密)字體樣式蹬跃,如果我們關(guān)閉這個strongbox
樣式匙瘪,停用這個字體,頁面上就會如實的顯示亂碼了蝶缀。
font-family
這是一種自定義字體丹喻,它可以把亂碼顯示成正常的數(shù)字。
這是一種有效的反爬蟲方式翁都,但我還是要鄙視這種處理方式碍论,首先這種亂碼對搜索引擎非常不友好,其次用戶在頁面上復(fù)制粘貼得到的也是亂碼柄慰,用戶體驗不友好鳍悠,另外在真的要顯示亂碼所用字符的極端情況下將無法實現(xiàn),所以這種手段僅適合加密少量字符坐搔。
問題分析
右鍵查看頁面源代碼藏研,搜索fangchan-secret
可以看到這個字體是JavaScript臨時生成的。
主要的字體信息是一長段大小寫字母概行,我們把它完整復(fù)制下來蠢挡。
為Python安裝fonttools字體工具模塊,conda install -c mwcraig fonttools
占锯,然后使用下面的代碼將這段字母存儲為ttf字體文件袒哥,中間的key部分需要你手工替換。
from fontTools.ttLib import TTFont
import base64
import io
key='''
AAEAAAALAIAAAwAwR1N.........AAA
'''
data = base64.b64decode(key) #base64解碼
fonts = TTFont(io.BytesIO(data)) #生成二進(jìn)制字節(jié)
fonts.save('fangchan-secret.ttf')
然后打開百度字體編輯網(wǎng)站消略,用打開按鈕選擇剛才生成的fangchan-secret.ttf
,就可以看到真實的字體內(nèi)容堡称。
我們看到每個字形都有一個藍(lán)色的編碼,如$9A4B
這樣的字符艺演,這是字符的16進(jìn)制編碼却紧,用chr()
命令查看它的真正樣子桐臊。
這就是說,fangchan-secret
字體把罕見的字符顯示為0~9數(shù)字了晓殊。
解決方案
對字體數(shù)據(jù)進(jìn)一步處理断凶。
cmap=fonts.getBestCmap() #十進(jìn)制ascii碼到字形名的對應(yīng)
for char in cmap:
print(char,hex(char),chr(char),cmap[char])
這個代碼輸出類似下面這個內(nèi)容:
其中各個內(nèi)容的關(guān)系如下圖(上面的輸出并沒有包含字形圖):
我們可以從字體文件中找到字符、編碼和字形名巫俺,可以用工具查看到字形圖认烁,但如何把0x9476
對應(yīng)到6
這個數(shù)字就只能靠人眼識別了,這也是加密的意義所在了介汹∪次耍——我們無法用代碼直接實現(xiàn)字符到字形所表示數(shù)字的對應(yīng)關(guān)系。
這是死穴嘹承。
但很多時候沒有那么糟糕窗价,畢竟只是10個數(shù)字被加密,我們只要憑人眼建立10個字符亂碼到真實數(shù)字的對應(yīng)關(guān)系就可以解決問題叹卷,例如撼港,假設(shè)我們搞定這個字典就不怕了:
numdict={
'鑶':0,
'閏':1,
'餼':2,
'驋':3,
'鵂':4,
'麣':5,
'齤':6,
'龒':7,
'龤':8,
'龥':9
}
但對于58房產(chǎn)網(wǎng),這樣還不夠骤竹,因為他們的網(wǎng)站會每隔幾秒鐘就變化fangchan-secret
的key
帝牡,就是那一長串AAEAAAALAIAAAwAwR1N.........AAA
。
反復(fù)對比之后發(fā)覺瘤载,它只是隨機(jī)變化字符龥...
和字形名glyph00007...
之間的對應(yīng)關(guān)系否灾,而字形名和字形之間的關(guān)系并不變,比如說glyph00007
幾秒前對應(yīng)龒
幾秒后又對應(yīng)齤
鸣奔,但它總是對應(yīng)字形6
這個不變墨技。
也就是說下面這個對應(yīng)是固定的:
glyphdict = {
'glyph00001': '0',
'glyph00002': '1',
'glyph00003': '2',
'glyph00004': '3',
'glyph00005': '4',
'glyph00006': '5',
'glyph00007': '6',
'glyph00008': '7',
'glyph00009': '8',
'glyph00010': '9'
}
而實際上我們可以從fongchan-secret
中讀取到字符和字形名之間的對應(yīng)關(guān)系,類似:
chrdict={
'鑶':'glyph00006',
'閏':'glyph00004',
'餼':'glyph00001',
'驋':'glyph00002',
'鵂':'glyph00003',
'麣':'glyph00009',
'齤':'glyph00010',
'龒':'glyph00008',
'龤':'glyph00007',
'龥':'glyph00005'
}
綜上我們就可以間接實現(xiàn)亂碼到數(shù)字的轉(zhuǎn)換了挎狸。
最終代碼
首先注意這個multReplace多個替換函數(shù)的作用:
#使用字典批量替換
import re
def multReplace(text, rpdict):
rx = re.compile('|'.join(map(re.escape, rpdict)))
return rx.sub(lambda match:rpdict[match.group(0)], text)
它可以批量執(zhí)行replace的功能扣汪。rx是一個豎線分割的或者表達(dá)式,比如'a|b|c|d
锨匆,這個表達(dá)式可以匹配出符合abcd任何一個字母匹配的列表崭别。
rx.sub()
方法傳入了一個lambda
函數(shù),表示可以對rx匹配列表中的每個匹配都執(zhí)行一個替換恐锣,效果如下:
下面是解密字體函數(shù):
#解密58房產(chǎn)的字體加密
from fontTools.ttLib import TTFont
import base64
import re
import io
def decode58Fangchan(html,key):
glyphdict = {
'glyph00001': '0',
'glyph00002': '1',
'glyph00003': '2',
'glyph00004': '3',
'glyph00005': '4',
'glyph00006': '5',
'glyph00007': '6',
'glyph00008': '7',
'glyph00009': '8',
'glyph00010': '9'
}
data = base64.b64decode(key) #base64解碼
fonts = TTFont(io.BytesIO(data)) #生成二進(jìn)制字節(jié)
cmap = fonts.getBestCmap() #十進(jìn)制ascii碼到字形名的對應(yīng){38006:'glyph00002',...}
chrMapNum = {} #將變?yōu)閧‘龥’:'1',...}
for asc in cmap:
chrMapNum[chr(asc)] = glyphdict[cmap[asc]]
return multReplace(html,chrMapNum)
讀取本地爬取的文件進(jìn)行解密:
from bs4 import BeautifulSoup
import html
with open('./pages/2.html', 'r') as f:
text = html.unescape(f.read()) #將閏室變?yōu)殚c室
key = re.findall(r"base64,(.*)'\).format", text)[0] #用正則表達(dá)式提取AAE..AAA
dehtml = decode58Fangchan(text, key)
soup = BeautifulSoup(dehtml)
moneyTags = soup.find_all('div', 'money')
print(','.join([m.b.text.strip() for m in moneyTags]))
輸出結(jié)果如下所示:
如果不進(jìn)行字體解密的亂碼結(jié)果如下:
動態(tài)字體
這樣的字體加密文件如何實現(xiàn)的茅主?
這里是一些思路和資源:
- 把多個svg文件合成為svg字體。Nodejs可以使用svgtofont模塊土榴,依照官方案例诀姚,把從網(wǎng)站下載(如iconfont網(wǎng)站)的多個svg圖形文件放到icon文件夾下,然后執(zhí)行node代碼就會得到一個可以讀懂的.svg文件玷禽,類似以下文件赫段,可以清楚地看到它包含了三個字形glyph以及每個字形對應(yīng)的unicode代碼呀打,而長串的數(shù)字就是svg圖形數(shù)據(jù)。(你也可以從iconfont購物車直接下載得到這個文件)
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<font id="svgtofont" horiz-adv-x="200">
<font-face font-family="svgtofont"
units-per-em="200" ascent="200"
descent="0" />
<missing-glyph horiz-adv-x="0" />
<glyph glyph-name="iconfont-zan"
unicode="鑶"
horiz-adv-x="200" d="M35.9453333984375 156.107777734375C43.2013333984375 162.9566666015625 52.8688888671875 167.1971111328125 62.828222265625 167.868222265625C70.7455556640625 168.480222265625 78.81244453125 166.806666796875 85.8811111328125 163.212C91.2586666015625 160.535777734375 95.9453333984375 156.63044453125 99.868 152.1026666015625C104.1108888671875 156.99755546875 109.25755546875 161.18755546875 115.197777734375 163.8404443359375C119.3948888671875 165.933333203125 124.0051111328125 167.031333203125 128.622222265625 167.7128888671875C139.672666796875 168.992222265625 151.196 165.966222265625 160.0631111328125 159.219777734375C170.44844453125 151.5731111328125 177.136 139.121777734375 177.6586666015625 126.223333203125C178.3591111328125 112.040888671875 173.656 97.919333203125 165.585777734375 86.3371109375C160.102222265625 78.316888671875 153.19 71.384888671875 145.85755546875 65.0582220703125C135.7428888671875 56.4326666015625 124.6102220703125 49.1366666015625 113.6102220703126 41.72155546875C108.8542220703126 38.6026666015625 104.313333203125 35.179777734375 99.8682220703125 31.637333203125C95.1782220703125 35.39155546875 90.3526666015626 38.969777734375 85.3291109375001 42.2606666015625C73.5079998046875 50.2248888671875 61.525333203125 58.08355546875 50.9282220703125 67.667777734375C41.0062220703125 76.55155546875 32.13555546875 87.0088888671875 26.962888671875 99.4046666015625C24.79266640625 104.53755546875 23.3179998046875 109.9611111328125 22.5575552734375 115.47755546875C21.8891107421875 120.7891109375 21.710221875 126.2162220703125 22.6495552734375 131.505333203125C24.224 140.8673333984375 28.9931111328125 149.6486666015625 35.9453333984375 156.107777734375L35.9453333984375 156.107777734375zM56.0746666015625 159.857777734375" />
<glyph glyph-name="iconfont"
unicode=""
horiz-adv-x="200" d="M65.01953125 18.8671875H134.4140625C140.80078125 18.8671875 145.7421875 24.21875 145.7421875 30.625V65.1953125H53.6328125V30.625C53.6328125 24.23828125 58.65234375 18.8671875 65.01953125 18.8671875zM134.4140625 180.76171875H65.01953125C58.6328125 180.76171875 53.6328125 175.78125 53.6328125 169.39453125V134.98046875H145.76171875V169.39453125C145.7421875 175.78125 140.78125 180.76171875 134.4140625 180.76171875zM157.3828125 134.98046875V123.1640625H41.9921875V134.98046875H30.33203125C23.9453125 134.98046875 18.92578125 129.53125 18.92578125 123.125V42.1875C18.92578125 35.80078125 23.9453125 30.5078125 30.33203125 30.5078125H41.9921875V76.8359375H157.36328125V30.5078125H169.08203125C175.46875 30.5078125 180.80078125 35.80078125 180.80078125 42.1875V123.125C180.80078125 129.51171875 175.46875 134.98046875 169.08203125 134.98046875H157.3828125zM157.3828125 99.7265625H122.8515625V111.54296875H157.3828125V99.7265625z" />
<glyph glyph-name="iconfont_info"
unicode=""
horiz-adv-x="200" d="M200 100C199.3625 71.625 189.625 48.0375 170.7875 29.2125C151.9625 10.375 128.375 0.625 100 0C71.625 0.6375 48.0375 10.375 29.2125 29.2125C10.375 48.0375 0.625 71.625 0 100C0.6375 128.375 10.375 151.9625 29.2125 170.7875C48.0375 189.625 71.625 199.375 100 200C128.375 199.3625 151.9625 189.625 170.7875 170.7875C189.625 151.9625 199.375 128.375 200 100zM100 112.5A12.5 12.5 0 0 1 87.5 100V50A12.5 12.5 0 0 1 112.5 50V100A12.5 12.5 0 0 1 100 112.5zM100 137.5A12.5 12.5 0 1 1 100 162.5A12.5 12.5 0 0 1 100 137.5z" />
</font>
</defs>
</svg>
- 修改unicode改變字符和字形的對應(yīng)關(guān)系糯笙,你可以使用任意語言直接修改這個類似html格式的svg文件贬丛。
- 把svg字體文件轉(zhuǎn)為ttf。Nodejs可以使用svg2ttf模塊给涕,把第一步生成的svg文件轉(zhuǎn)為ttf字體文件豺憔。
-
使用base64解碼讀取ttf。在macOS下可以直接使用終端命令
base64 a.svg > a.txt
獲得base64編碼稠炬,或者使用Python或其他語音進(jìn)行base64編碼焕阿。以下是txt文件的樣子。
AAEAAAALAIAAAwAwR1N........YW4IaWNvbmZvbnQNaWNvbmZvbnRfaW5mbwAAAAAA
- 將上面的base64代碼嵌入到html頁面首启。參照以下index.html代碼(可直接單獨使用):
<style>
@font-face {
font-family: "myfont";
src: url(data:application/x-font-woff;charset=utf-8;base64,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);
font-style: normal;
font-weight: 400;
}
</style>
<a style="font-family: myfont">圖標(biāo)字體:鑶</a>
頁面效果如下:
其他資源
字體松鼠fontsquirrel,可以自定義字形包撤摸,直接下載得到base64數(shù)據(jù)毅桃,注意要選擇expert才能看到base64選項。
fontello,同樣可以上傳自己的svg或者點擊網(wǎng)站現(xiàn)有的圖標(biāo)(一定要點)准夷,點了之后就可以Customize Codes自定義修改了钥飞,58房產(chǎn)就是用的這個網(wǎng)站的API。
百度字體編輯器衫嵌,功能相似读宙,可以打開現(xiàn)有ttf,也可以深度編輯字形和字符楔绞,然后下載ttf或其他字體格式结闸。
阿里iconfont圖標(biāo)庫,可以下載數(shù)十萬各種圖標(biāo)素材酒朵,記得一定要加入購物車桦锄,然后從購物車下載,可以直接得到svg字體文件蔫耽。
結(jié)語结耀,使用這種字體加密反爬其實沒有太多意義,網(wǎng)站既然是公開的匙铡,又為什么害怕別人知道你的公開數(shù)據(jù)呢图甜?但這種加密方法是很強(qiáng)悍的,如果動態(tài)擾亂字形名那就真的無解了鳖眼,如果有的話就只能靠字符圖像識別技術(shù)了黑毅。動態(tài)擾亂字形名和字形之間的關(guān)系,可以在需要極端保密的場景下真正實現(xiàn)每個頁面顯示的信息動態(tài)加密具帮,當(dāng)然字形太多的話base64字符數(shù)據(jù)也會很多博肋,下載很慢低斋。
每個人的智能新時代
如果您發(fā)現(xiàn)文章錯誤匪凡,請不吝留言指正膊畴;
如果您覺得有用,請點喜歡病游;
如果您覺得很有用唇跨,歡迎轉(zhuǎn)載~
END