日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

爬虫学习笔记(二十)—— 字体反爬

發布時間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 爬虫学习笔记(二十)—— 字体反爬 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、什么是字體反爬
  • 二、編碼原理
    • 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字節)表示

  • 獲取漢字unicode編號
  • 轉化為二進制數
  • 根據UTF-8編碼格式,將二進制數據填充到指定位置
  • 將填充好的新二進制數據,轉換成16進制
  • 例:

    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))

    結果演示:

    總結

    以上是生活随笔為你收集整理的爬虫学习笔记(二十)—— 字体反爬的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。