爬虫学习笔记(二十)—— 字体反爬
文章目錄
- 一、什么是字體反爬
- 二、編碼原理
- 2.1、ASCII編碼對照表
- 2.2、Unicode編號
- 2.3、UTF-8編碼方式
- 2.4、字符矢量圖
- 三、案例:58同城反爬字體
- 3.1、代碼實現
一、什么是字體反爬
網頁開發者自己創造一種字體,因為在字體中每個文字都有其代號,那么以后在網頁中不會直接顯示這個文字的最終的效果,而是顯示他的代號,因此即使獲取到了網頁中的文本內容,也只是獲取到文字的代號,而不是文字本身。
簡單的說,字體反爬指的就是瀏覽器頁面上的字符和調試窗口或者源碼中的內容,顯示的不一樣,這就是字體反爬。
二、編碼原理
bit(比特):是由0或1構成的二進制位
Byte(字節):1個字節由八個連續的二進制位,或二個16進制數表示
字符:是指計算機中使用的字母、數字、字和符號
2.1、ASCII編碼對照表
ASCII碼
ASCII 碼使用指定的7位或8位二進制數組合來表示128或256種可能的字符。標準ASCII碼也叫基礎ASCII碼,使用7位二進制數(剩下的1位二進制為0)來表示所有的大寫和小寫字母,數字0 到9、標點符號,以及在美式英語中使用的特殊控制字符。
2.2、Unicode編號
Unicode碼
Unicode為世界上所有字符都分配了一個唯一的數字編號,這個編號范圍從 0x000000 到 0x10FFFF(十六進制),有110多萬,每個字符都有一個唯一的Unicode編號,這個編號一般寫成16進制,在前面加上U+。例如:”爬“的Unicode是U+722C。它是一種規定,Unicode本身只規定了每個字符的數字編號是多少,并沒有規定這個編號如何存儲。
理論上可以直接把Unicode編號直接轉換成二進制進行存儲,而Unicode并不是這么操作,因為除了這種直接轉換成二進制的方案外,還有其他方案,主要有UTF-8,UTF-16,UTF-32,gbk。(UTF-8、UTF-16、UTF-32……都是 Unicode編碼 的一種實現。)
2.3、UTF-8編碼方式
UTF-8 最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。
UTF-8 的編碼規則很簡單,只有兩條:
對于單字節的符號,字節的第一位設為0,后面7位為這個符號的 Unicode 碼。因此對于英語字母,UTF-8 編碼和 ASCII 碼是相同的。
對于n字節的符號(n > 1),第一個字節的前n位都設為1,第n + 1位設為0,后面字節的前兩位一律設為10。剩下的沒有提及的二進制位,全部為這個符號的 Unicode 碼。
漢字的UTF-8編碼(n字節)表示
例:
1、獲取漢字unicode編號 ord('爬') =》29228 hex(29228) =》'0x722c'2、轉化為二進制數 bin(29228) =》'0b111001000101100'3、根據UTF-8編碼格式,將二進制數據填充到指定位置(29228>2048所以選3的: 1110xxxx 10xxxxxx 10xxxxxx) 11100111 10001000 101011004、將填充好的新二進制數據,轉換成16進制 hex(int('11100111',2)) =》'0xe7' hex(int('10001000',2)) =》'0x88' hex(int('10101100',2)) =》'0xac' "爬".encode('utf-8') =》b'\xe7\x88\xac' #字節編碼編號與編碼
一個字的Unicode編號是固定的,但是在計算機上的字節碼,取決于編碼方案的實現方式。一個漢字在Unicode中的編號的16進制數,跟utf8編碼后的16進制數不是一回事。
2.4、字符矢量圖
字體可以理解為通過unicode編號,對應的自定義圖形。
字符對應關系
傳遞字符 5 ,有兩種方案,
第一種是傳遞字符4的字節碼:瀏覽器拿到字節碼,轉換成unicode編號,在沒有指定字體文件的前提下,通過unicode編號在到系統自帶字體中尋找字符矢量圖,得到 ‘5’
第二種是,傳遞另一個繁體字的字節碼,同時,傳遞一個自定義的字體。瀏覽器拿到繁體字的字節碼,轉換成unicode編碼,到css指定的字體文件中查找,找到字符矢量圖 ‘5’。
第二種情況下,瀏覽器后臺和爬蟲拿到的繁體字的字節碼,只能在正常字體中查找字符矢量圖,所以只能看到 一個其他字體(如:鱀)
正常對應關系:unicode編號 -->正常字符集 -->正常字符
使用自定義矢量圖:①unicode編號 -->正常字符集 -->難懂的字
?????????②nunicode編號 -->自定義字符集 -->正常字符
工具:
FontCreator 下載exe文件,安裝(下載鏈接:https://pan.baidu.com/s/15Bd9786YB_KcySPh2s4bXQ ,提取碼:cgnb)
fontTools包:pip install fontTools
字體文件存儲的是unicode編號和字符矢量圖的對應關系
三、案例:58同城反爬字體
首先我們先觀察字體的位置:
字體樣式接下來就是查找自定義字符文件
自定義字符文件或者也可以在‘network’處找到
自定義字符文件然后我們就可以找出它們的對應關系了
base64_content = re.findall("charset=utf-8;base64,(.*?)'",res.text) byte_content = base64.b64decode(base64_content) with open('58car.ttf','wb') as f:f.write(byte_content) font = TTFont('58car.ttf') font.saveXML('58car.xml') print(font.getBestCmap()) print(font.getGlyphID()) {37: 'uni0025', 43: 'uni002B', 45: 'uni002D', 47: 'uni002F', 165: 'uni00A5', 19975: 'uni4E07', 20803: 'uni5143', 25240: 'uni6298', 26102: 'uni65F6', 36215: 'uni8D77'} {'.notdef': 0, 'uni5143': 1, 'uni002B': 2, 'uni65F6': 3, 'uni8D77': 4, 'uni002F': 5, 'uni00A5': 6, 'uni6298': 7, 'uni4E07': 8, 'uni0025': 9, 'uni002D': 10}3.1、代碼實現
import base64 import requests import re from lxml import etree from fontTools.ttLib import TTFonturl = 'https://xm.58.com/ershouche/?PGTID=0d100000-0025-e7c0-f349-f6fb99ec299a&ClickID=2' #58反爬比較強,所以我們頭全寫 headers = { 'authority': 'xm.58.com', 'method': 'GET', 'path': '/ershouche/?PGTID=0d100000-0025-e7c0-f349-f6fb99ec299a&ClickID=2', 'scheme': 'https', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'cache-control': 'no-cache', 'cookie': 'f=n; userid360_xml=DF790A407DD312F8230CF109530AC2BD; time_create=1631538931202; commontopbar_ipcity=zhangpu%7C%E6%BC%B3%E6%B5%A6%7C0; myLat=""; myLon=""; id58=r5k1JWEXmnttPtncoqH9+g==; mcity=zhangpu; 58tj_uuid=c72abed3-2489-4e7c-a321-691932bae8d3; wmda_uuid=d82a91d7f9f65ac837bb5b625c7b1068; wmda_new_uuid=1; als=0; xxzl_deviceid=0yvDkPJsU54k1%2BmeRC6Jp43aVt%2Bi7XJ7nI3mjoAoEaBaTbTRjvzYN%2FRqpRUawEXq; sessionid=36b1e461-0f5c-4f3b-ba5b-4eb4c7abd190; fzq_h=9db1bd518df2c5ff11e98c3775bb3bab_1628936871311_d7d3355fc4ca42a7bdb1d0a21f39d5c4_2363523693; f=n; city=xm; 58home=xm; wmda_visited_projects=%3B11187958619315%3B1731916484865%3B1732038237441%3B2385390625025; 58_ctid=606; is_58_pc=1; commontopbar_new_city_info=46%7C%E5%8E%A6%E9%97%A8%7Cxm; ctid=46; aQQ_ajkguid=7A6CE0F2-01D0-978C-9DB3-SX0814211431; sessid=7D16F3E3-652C-5EFB-AD5C-SX0814211431; __xsptplus8=8.1.1628946873.1628946873.1%234%7C%7C%7C%7C%7C%23%23t6AyIlY5mNYqNbQWgTNdey7ludWc_Aq_%23; xxzl_cid=c2bd9243c2614a22a6e811404337c8b6; xzuid=261dd2fa-dd63-49e9-b293-84c4afe5498b; wmda_session_id_1732038237441=1628961854128-e660d3fe-4be8-496b; new_uv=3; utm_source=; spm=; init_refer=https%253A%252F%252Fxm.58.com%252F%253Ffrom%253Dpc_topbar_home%2526PGTID%253D0d3090a7-0025-e161-980b-bb07e2053409%2526ClickID%253D3; new_session=0; fzq_js_usdt_infolist_car=4078e6bf9869aa309438c21d8a1fca5d_1628962005226_2', 'pragma': 'no-cache', 'referer': 'https://xm.58.com/?from=pc_topbar_home&PGTID=0d3090a7-0025-e161-980b-bb07e2053409&ClickID=3', 'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'same-origin', 'sec-fetch-user': '?1', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67' } res = requests.get(url=url,headers=headers) print(res.text)base64_content = re.findall("charset=utf-8;base64,(.*?)'",res.text)[0] byte_content = base64.b64decode(base64_content) with open('58car.ttf','wb') as f:f.write(byte_content) font = TTFont('58car.ttf') font.saveXML('58car.xml') #打印字符對應關系 print(font.getBestCmap()) print(font.getReverseGlyphMap())#通過自定義字符文件,獲取對應字體 def get_car_price(string, font):unicode_glyph = font.getBestCmap()glyph_price = font.getReverseGlyphMap()new_str = ''for char in string:char_unicode = ord(char)if char_unicode in unicode_glyph:glyph_code = unicode_glyph[char_unicode]price = glyph_price[glyph_code]new_str += str(price)return new_strhtml = etree.HTML(res.text) car_list = html.xpath('//li[@class="info"]') for car in car_list:#獲取汽車名(無字體反爬)car_name = car.xpath('./div/a/div/h2/span/text()')[0].strip()#獲取汽車價格(有字體反爬)car_price = car.xpath('./div/a/div/b/text()')[0].strip()car_price = get_car_price(car_price,font)print('汽車:%s, 價格:%s'%(car_name,car_price))結果演示:
總結
以上是生活随笔為你收集整理的爬虫学习笔记(二十)—— 字体反爬的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爬虫学习笔记(十九)—— 滑动验证码
- 下一篇: 爬虫学习笔记(二十一)—— Appium