如上圖我們可以發現有些數據的數字變成了加密字體,我就去查看了一下網站的代碼,結果發現網站的代碼顯示是這樣的:
原來有些網站上使用了字體加密技術,為了解決這個問題,我找了大量的資料,可是網上的很多方法由於網站反爬技術的進步或者網站更新了字體加密規則已經不能使用了,於是我就開始了破解字體加密的艱辛歷程。
解決方法
方法一:
代碼如下:
from selenium import webdriver from selenium.webdriver.firefox.options import Options import pytesseract from PIL import Image from fontTools.ttLib import TTFont def font_parse(url, font_str1, font_str2, font_str3): """ 解析亂碼的數字 :param rental: :param house_type: :param toward_floor: :return: """ response = requests.get(url) base64_string = response.text.split("base64,")[1].split("'")[0].strip() bin_data = base64.decodebytes(base64_string.encode()) with open("base.woff", r"wb") as f: f.write(bin_data) html = '''<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> @font-face { font-family: myFirstFont; src: url(base64.woff); } div { font-family: myFirstFont; font-size: 32px; text-align: center; } </style> </head> <body> <div>%s</div> <div>%s</div> <div>%s</div> </body> </html> ''' % (font_str1, font_str2, font_str3) # 將生成的HTML字符串寫入html文件中 with open("font_parse.html", r"wb") as f: f.write(bytes(html, encoding="utf-8")) # 使用selenium在瀏覽器中加載html文件 options = Options() options.add_argument("-headless") browser = webdriver.Firefox(executable_path="E:/python/geckodriver", firefox_options=options) browser.get(r"file:///E:\python\practice\practice06\font_parse\font_parse.html") # 最大化窗口,因為每一次爬取只能看到視窗內的圖片 browser.maximize_window() # 將網頁保存為圖片格式 browser.save_screenshot("font_parse.png") browser.close() image = Image.open('font_parse.png') # 利用tesseract-OCR識別庫識別圖片中的文字,chi_sim是簡體中文語言庫 result = pytesseract.image_to_string(image, lang="chi_sim") result_list = result.splitlines() return result_list
1.url是你要爬取網站的網址,font_str是需要解析的加密字符,可以根據需求定義font_str(1, 2, …n)的個數,在html字符串里面修改div的個數並在‘%()’里面寫入對應的參數
2.executable_path=“E:/python/geckodriver"里面的路徑換成自己電腦上‘geckodriver’所在的位置
3.browser.get(r"file:///E:\python\practice\practice06\font_parse\font_parse.html”)里面的路徑換成html文件保存的路徑
4.tesseract識別英文和數字比較簡單,識別中文需要下載中文語言庫,放在“Tesseract-OCR\tessdata”文件夾里面。有一點需要說明一下:tesseract對於單個字符的識別不太好,我試了幾次識別單個數字字符發現識別的結果為空,大家使用tesseract時盡量避免這個問題
這個方法可以解決目前大部分網站的字體加密,不過這個方法比較麻煩,而且使用selenium模擬打開瀏覽器會造成程序運行速度非常慢,爬取得數據較少時可以使用,當爬取大量數據時這個方法就不太實用了,於是就有了下面的進階方法。
方法二:
代碼如下:
text = "鑶驋龒龒" im = Image.new("RGB", (60, 25), (255, 255, 255)) dr = ImageDraw.Draw(im) font = ImageFont.truetype('base.woff', 18) dr.text((10, 5), text, font=font, fill="#000000") #im.show() im.save("t.png")
利用此代碼替換方法一代碼中selenium模擬打開網頁並保存網頁為圖片的部分,可以節約大量的代碼運行所需的時間
方法三:
import base64 from fontTools.ttLib import TTFont import re from PIL import Image, ImageDraw, ImageFont html = open("test.html", 'r', encoding="utf-8").read() base64_string = html.split("base64,")[1].split("'")[0] bin_data = base64.decodebytes(base64_string.encode()) with open("base.woff", r"wb") as f: f.write(bin_data) font_list = re.findall('&#x(\w+)', html) for font_secret in font_list: one_font = int(font_secret, 16) font = TTFont('base.woff') # font.saveXML('test.xml') c = font['cmap'].tables[2].ttFont.tables['cmap'].tables[1].cmap # print("c::::::", c) gly_font = c[one_font] b = font['cmap'].tables[2].ttFont.getReverseGlyphMap() # print("b:::::::", b) print(b[gly_font] - 1)
1.提取加密字體的十六進制格式除了可以使用正則直接提取出來,有些也可以把加密的字體用“unicode_escape”編碼格式解析一下生成字體對應的十六進制,但是不同網站的編碼格式可能不同,需要大家自己驗證。
2.最后一行的代碼對(從“b”字典里取出對應的數字字符再減一)是我根據58網站上的信息與解析之后的信息進行比對后發現的規則,我並不確定其他網站是不是也是這樣。
3.這個方法是別人告訴我的,代碼比較簡單,但是原理比較復雜,我對於這個代碼的運行原理也不是太了解,不過並不影響使用。我驗證了一下這個代碼,也成功解決了文字加密問題,大家有興趣的可以去深入了解一下。
關於b和c打印之后是結果如下圖:
大家最好能夠自己打印看一下。生成的xml文件這里不太好粘貼,大家也最好能夠生成之后看一下,加深對代碼的理解。
利用“unicode_escape”編碼格式解析的代碼如下:
from fontTools.ttLib import TTFont def font_parse(text): """ 解析亂碼的數字 :param rental: :param house_type: :param toward_floor: :return: """ decryption_text = "" for alpha in text: hex_alpha = alpha.encode('unicode_escape').decode()[2:] if len(hex_alpha) == 4: one_font = int(hex_alpha, 16) font = TTFont('base.woff') font_dict = font['cmap'].tables[2].ttFont.tables['cmap'].tables[1].cmap b = font['cmap'].tables[2].ttFont.getReverseGlyphMap() if one_font in font_dict.keys(): gly_font = font_dict[one_font] item_text = str(b[gly_font] - 1) else: item_text = alpha
上圖是測試加密字體解析之后的結果。
方法四:
response = requests.get(url) base64_string = response.text.split("base64,")[1].split("'")[0].strip() bin_data = base64.decodebytes(base64_string.encode()) with open("base.woff", r"wb") as f: f.write(bin_data)
這個代碼在前面幾個方法中都會用到,用於生成網站上的加密字體所用的字體庫
下載FontCreator軟件,打開生成的"base.woff"字體庫,軟件會自動生成字體庫的對應關系,如下圖:
根據此軟件解析出來的字體庫對應關系,可以建立一個字體查詢字典,如:{“9f92”: “0”, “EC3A”: “上”, …},然后根據“方法三”介紹的利用正則或者“unicode_escape”編碼格式解析將加密字體轉換為十六進制格式並利用此十六進制數字在字體查詢字典里面查詢加密字體對應的真實字體。
這個方法很簡單,但是字體查詢字典需要手動建立而不能自動生成,如果爬取的網站的字體對應關系不變的話可以使用。然而對於如58同城之類的網站每次刷新之后網頁的字體對應關系都會變化,這個方法就不適用了。
結尾:
以上就是我解決網站字體加密的方法,至於如何把這些方法應用到爬蟲程序里面,相信大家只要好好研究一下,應該都是沒有問題的,我就不再贅述了。大神們如果有其他解決方法可以在評論里留言,最好能夠在評論里留下鏈接,希望能和大家一起進步。
轉載於 https://blog.csdn.net/litang199612/article/details/83413002