字體反爬之奇聞007網(wǎng)簡(jiǎn)單又特殊的字體反爬

一個(gè)偶然的機(jī)會(huì)襟铭,一個(gè)朋友讓我?guī)退纯茨硞€(gè)網(wǎng)站的反爬機(jī)制犀盟,他弄了大半天都沒(méi)有解決,這個(gè)網(wǎng)站就是奇聞007搪桂,一開(kāi)始我也沒(méi)搞明白它的反爬機(jī)制,后來(lái)經(jīng)過(guò)仔細(xì)分析調(diào)試發(fā)現(xiàn)它的反爬機(jī)制是字體反爬盯滚,一個(gè)簡(jiǎn)單而又特殊的字體反爬踢械,為什么說(shuō)它簡(jiǎn)單而又特殊呢?因?yàn)樗亲煮w反爬但是它的字體文件的編碼不會(huì)隨著頁(yè)面的刷新而刷新淌山,但是它的字有點(diǎn)多278個(gè)字裸燎,特殊在于它不像我們常見(jiàn)的反爬網(wǎng)頁(yè)源代碼顯示的是編碼而是中文漢字,這樣就起到了混淆的作用泼疑。

目標(biāo)分析

我們先打開(kāi)奇聞007的一個(gè)頁(yè)面德绿,檢查一下元素看看它的反爬現(xiàn)象:

可以發(fā)現(xiàn)瀏覽器Elements里的(渲染后)段落數(shù)據(jù)不一樣,簡(jiǎn)直詞不達(dá)意退渗,但是段落里的有些字還是在頁(yè)面渲染是正確的移稳,比如字,頁(yè)面看到的和渲染的是一樣会油。我們?cè)倏纯淳W(wǎng)頁(yè)原代碼:
發(fā)現(xiàn)和瀏覽器Elements里的(渲染后)段落數(shù)據(jù)一樣和頁(yè)面看到的不一樣个粱,那么它是怎么實(shí)現(xiàn)的呢?我們?cè)贑hrome控制臺(tái)搜索article發(fā)現(xiàn):
article相關(guān)的請(qǐng)求雖然有幾個(gè)翻翩,但是仔細(xì)分析都不是我們想要的(不是解密相關(guān))都许。我們?cè)倏纯凑?qǐng)求中的js請(qǐng)求,看看能否找到加密解密的相關(guān)js:
發(fā)現(xiàn)好多js都請(qǐng)求失敗嫂冻,其他js也不是加密解密的文件胶征,我們?cè)偈褂肅hrome瀏覽器的一個(gè)插件Toggle JavaScript,通過(guò)它我們可以攔截頁(yè)面所有的js請(qǐng)求桨仿,我們測(cè)試一下是不是不需要js這個(gè)頁(yè)面也是能夠正常顯示:
這就說(shuō)明這個(gè)加密解密跟js沒(méi)有關(guān)系睛低,那它是如何做到代碼上混淆頁(yè)面正常顯示的呢?那么我們?cè)倏纯闯薺s還有那些可疑請(qǐng)求:
除了js和本頁(yè)面的html也沒(méi)有什么可疑請(qǐng)求服傍,唯一一個(gè)可疑的可能就是字體文件hansansjm.ttf钱雷,難道是字體反爬?不像啊吹零,以前做過(guò)字體反爬罩抗,字體反爬的網(wǎng)頁(yè)原代碼顯示都是的方式,而瀏覽器Elements里顯示?瘪校,難道是特殊的字體反爬澄暮?抱著嘗試的態(tài)度下載這個(gè)字體文件然后用百度FontEditor打開(kāi)查看:
發(fā)現(xiàn)這個(gè)字體文件的文字還挺多的居然有3頁(yè)名段,因?yàn)樵谇懊嫖覀兛吹紼lements中的字對(duì)應(yīng)頁(yè)面顯示的字,我們看看這幾頁(yè)有沒(méi)有字:
還真有泣懊,我們?cè)诘?頁(yè)找到了字伸辟,它的編碼是$76F4,我們?cè)倏纯雌渌囊恍┐a中和顯示結(jié)果不一樣的字是否能在字體文件中找到馍刮,結(jié)果發(fā)現(xiàn)都能找到信夫,那就說(shuō)明這個(gè)反爬還真是字體反爬。

按照之前做過(guò)的字體反爬經(jīng)驗(yàn)卡啰,我們每刷新一下字體文件里文字對(duì)應(yīng)的編碼都是會(huì)變的静稻,我們也刷新一下頁(yè)面然后再看看字體文件的字以及它們對(duì)應(yīng)的編碼會(huì)不會(huì)改變,經(jīng)過(guò)多次嘗試發(fā)現(xiàn)它的字體文件里面的字以及字對(duì)應(yīng)的編碼是不會(huì)變的匈辱。

這就簡(jiǎn)單了振湾,我們直接把每個(gè)字的映射關(guān)系弄出來(lái),然后在頁(yè)面替換就能獲取正常的的數(shù)據(jù)了亡脸,可是有這么多字押搪,現(xiàn)在我們只知道部分字的對(duì)應(yīng)關(guān)系,比如對(duì)應(yīng)浅碾,其他的不知道大州,這樣就很難做映射,而且有個(gè)疑問(wèn)為什么是對(duì)應(yīng)而不是其他什么字垂谢?它們之間有什么聯(lián)系厦画?

我們可以使用Python的fonttools庫(kù)把字體和編碼的映射弄出來(lái),比如字我們可以找出它的編碼0x76f4滥朱,0x76f4有什么關(guān)系根暑,憑借著以前的經(jīng)驗(yàn),我推斷它可能是Unicode編碼轉(zhuǎn)為字符徙邻,我們使用js的String.fromCharCode()方法或者Python的chr()方法試一下:

真的如我預(yù)測(cè)的一樣购裙,這樣我們就可以解決這個(gè)反爬了

解密

按照上面的分析我們只需要下載字體文件,使用fonttools庫(kù)把字體和編碼的映射弄出來(lái)鹃栽,我們?cè)偈謩?dòng)把這278個(gè)字手動(dòng)敲出來(lái),再用chr()方法把真正的映射關(guān)系弄出來(lái)躯畴,最后替換文字民鼓,就可以提取數(shù)據(jù)了。

from fontTools.ttLib import TTFont
import requests
from lxml import etree

# 可以是.ttf類型的字體文件也可以是.woff類型的字體文件
font = TTFont('hansansjm.ttf')
# 手動(dòng)把字體文件中的字寫下來(lái)
font_data = ['萬(wàn)', '三', '上', '下', '不', '與', '世', '東', '兩', '個(gè)', '中', '為', '主', '麗', '么', '之', '樂(lè)', '也', '了', '事', '二',
             '五', '些', '親', '人', '什', '今', '他', '代', '以', '們', '會(huì)', '傳', '位', '體', '何', '作', '你', '值', '做', '像', '兒',
             '元', '光', '入', '全', '公', '關(guān)', '內(nèi)', '寫', '冰', '出', '分', '劉', '到', '前', '劇', '力', '動(dòng)', '十', '千', '卻', '原',
             '去', '友', '發(fā)', '變', '古', '只', '可', '吃', '合', '同', '名', '后', '嗎', '吳', '員', '和', '四', '回', '因', '國(guó)', '圖',
             '圈', '在', '地', '場(chǎng)', '型', '外', '多', '大', '天', '太', '夫', '頭', '奇', '女', '她', '好', '如', '媽', '妻', '娛', '婚',
             '子', '學(xué)', '孩', '寶', '實(shí)', '家', '對(duì)', '將', '小', '少', '就', '山', '歲', '已', '巴', '帥', '年', '底', '度', '開(kāi)', '張',
             '當(dāng)', '影', '很', '得', '心', '性', '怪', '情', '驚', '想', '意', '感', '戲', '成', '我', '房', '手', '打', '拍', '排', '新',
             '方', '無(wú)', '日', '時(shí)', '明', '星', '是', '曝', '曾', '最', '月', '有', '服', '本', '機(jī)', '李', '來(lái)', '楊', '林', '果', '樣',
             '榜', '次', '死', '母', '比', '民', '氣', '水', '沒(méi)', '法', '活', '海', '清', '游', '演', '火', '點(diǎn)', '熱', '然', '照', '愛(ài)',
             '片', '物', '特', '狗', '王', '現(xiàn)', '球', '生', '用', '電', '男', '界', '白', '百', '的', '直', '相', '看', '真', '眼', '著',
             '知', '神', '種', '秘', '稱', '穿', '竟', '笑', '第', '粉', '紅', '經(jīng)', '結(jié)', '給', '網(wǎng)', '美', '老', '而', '能', '臉', '自',
             '色', '藝', '花', '英', '行', '衣', '被', '裝', '西', '要', '見(jiàn)', '視', '認(rèn)', '讓', '說(shuō)', '誰(shuí)', '走', '趙', '起', '超', '路',
             '身', '車', '過(guò)', '還', '這', '造', '道', '遭', '部', '都', '里', '重', '金', '長(zhǎng)', '陳', '面', '穎', '顏', '食', '馬', '高',
             '魚', '黃', '黑', '龍', '一']
# 編碼和文字之間的映射關(guān)系
name_font = {name:fonts for name, fonts in zip(font.getGlyphOrder()[1:],font_data)}
# 頁(yè)面字和文字的映射
code_font = {chr(code):name_font[name] for code, name in font.getBestCmap().items() if name in name_font }
"""
完整的映射關(guān)系(code_font變量)蓬抄,字典的key是網(wǎng)頁(yè)源代碼中的文字丰嘉,value是頁(yè)面顯示的文字
{'?': '比', '?': '氣', '?': '視', '?': '過(guò)', '?': '陳', '?': '馬', '?': '高', '?': '黃', '?': '黑', '?': '一', '?': '萬(wàn)', '?': '五', '?': '什', '?': '元', '?': '全', '?': '動(dòng)', '?': '千', '?': '天', '?': '她', '?': '學(xué)', '?': '少', '?': '歲', '?': '性', '?': '打', '?': '無(wú)', '?': '日', '?': '時(shí)', '?': '有', '?': '民', '?': '水', '?': '沒(méi)', '?': '點(diǎn)', '?': '物', '?': '用', '?': '電', '?': '百', '?': '美', '?': '而', '?': '能', '?': '色', '?': '藝', '?': '衣', '?': '被', '?': '趙', '?': '車', '?': '重', '?': '長(zhǎng)', '?': '穎', '?': '馬', '?': '魚', '?': '龍', '一': '萬(wàn)', '萬(wàn)': '三', '三': '上', '上': '下', '下': '不', '不': '與', '與': '世', '世': '東', '東': '兩', '兩': '個(gè)', '個(gè)': '中', '中': '為', '為': '主', '主': '麗', '麗': '么', '么': '之', '之': '樂(lè)', '樂(lè)': '也', '也': '了', '了': '事', '事': '二', '二': '五', '五': '些', '些': '親', '親': '人', '人': '什', '什': '今', '今': '他', '他': '代', '代': '以', '以': '們', '們': '會(huì)', '會(huì)': '傳', '傳': '位', '位': '體', '體': '何', '何': '作', '作': '你', '你': '值', '值': '做', '做': '像', '像': '兒', '兒': '元', '元': '光', '光': '入', '入': '全', '全': '公', '公': '關(guān)', '關(guān)': '內(nèi)', '內(nèi)': '寫', '寫': '冰', '冰': '出', '出': '分', '分': '劉', '劉': '到', '到': '前', '前': '劇', '劇': '力', '力': '動(dòng)', '動(dòng)': '十', '十': '千', '千': '卻', '卻': '原', '原': '去', '去': '友', '友': '發(fā)', '發(fā)': '變', '變': '古', '古': '只', '只': '可', '可': '吃', '吃': '合', '合': '同', '同': '名', '名': '后', '后': '嗎', '嗎': '吳', '吳': '員', '員': '和', '和': '四', '四': '回', '回': '因', '因': '國(guó)', '國(guó)': '圖', '圖': '圈', '圈': '在', '在': '地', '地': '場(chǎng)', '場(chǎng)': '型', '型': '外', '外': '多', '多': '大', '大': '天', '天': '太', '太': '夫', '夫': '頭', '頭': '奇', '奇': '女', '女': '她', '她': '好', '好': '如', '如': '媽', '媽': '妻', '妻': '娛', '娛': '婚', '婚': '子', '子': '學(xué)', '學(xué)': '孩', '孩': '寶', '寶': '實(shí)', '實(shí)': '家', '家': '對(duì)', '對(duì)': '將', '將': '小', '小': '少', '少': '就', '就': '山', '山': '歲', '歲': '已', '已': '巴', '巴': '帥', '帥': '年', '年': '底', '底': '度', '度': '開(kāi)', '開(kāi)': '張', '張': '當(dāng)', '當(dāng)': '影', '影': '很', '很': '得', '得': '心', '心': '性', '性': '怪', '怪': '情', '情': '驚', '驚': '想', '想': '意', '意': '感', '感': '戲', '戲': '成', '成': '我', '我': '房', '房': '手', '手': '打', '打': '拍', '拍': '排', '排': '新', '新': '方', '方': '無(wú)', '無(wú)': '日', '日': '時(shí)', '時(shí)': '明', '明': '星', '星': '是', '是': '曝', '曝': '曾', '曾': '最', '最': '月', '月': '有', '有': '服', '服': '本', '本': '機(jī)', '機(jī)': '李', '李': '來(lái)', '來(lái)': '楊', '楊': '林', '林': '果', '果': '樣', '樣': '榜', '榜': '次', '次': '死', '死': '母', '母': '比', '比': '民', '民': '氣', '氣': '水', '水': '沒(méi)', '沒(méi)': '法', '法': '活', '活': '海', '海': '清', '清': '游', '游': '演', '演': '火', '火': '點(diǎn)', '點(diǎn)': '熱', '熱': '然', '然': '照', '照': '愛(ài)', '愛(ài)': '片', '片': '物', '物': '特', '特': '狗', '狗': '王', '王': '現(xiàn)', '現(xiàn)': '球', '球': '生', '生': '用', '用': '電', '電': '男', '男': '界', '界': '白', '白': '百', '百': '的', '的': '直', '直': '相', '相': '看', '看': '真', '真': '眼', '眼': '著', '著': '知', '知': '神', '神': '種', '種': '秘', '秘': '稱', '稱': '穿', '穿': '竟', '竟': '笑', '笑': '第', '第': '粉', '粉': '紅', '紅': '經(jīng)', '經(jīng)': '結(jié)', '結(jié)': '給', '給': '網(wǎng)', '網(wǎng)': '美', '美': '老', '老': '而', '而': '能', '能': '臉', '臉': '自', '自': '色', '色': '藝', '藝': '花', '花': '英', '英': '行', '行': '衣', '衣': '被', '被': '裝', '裝': '西', '西': '要', '要': '見(jiàn)', '見(jiàn)': '視', '視': '認(rèn)', '認(rèn)': '讓', '讓': '說(shuō)', '說(shuō)': '誰(shuí)', '誰(shuí)': '走', '走': '趙', '趙': '起', '起': '超', '超': '路', '路': '身', '身': '車', '車': '過(guò)', '過(guò)': '還', '還': '這', '這': '造', '造': '道', '道': '遭', '遭': '部', '部': '都', '都': '里', '里': '重', '重': '金', '金': '長(zhǎng)', '長(zhǎng)': '陳', '陳': '面', '面': '穎', '穎': '顏', '顏': '食', '食': '馬', '馬': '高', '高': '魚', '魚': '黃', '黃': '黑', '黑': '龍', '龍': '一', '老': '而', '路': '身', '不': '與', '力': '動(dòng)', '年': '底', '里': '重', '林': '果', '什': '今', '行': '衣'}
"""

# 定義一個(gè)文字替換的函數(shù)
def replace_word(row,replace_dict):
    result = []
    for word in row:
        if word in replace_dict.keys():
            result.append(replace_dict[word])
        else:
            result.append(word)
    return ''.join(result)

# 請(qǐng)求的url地址
url = "http://www.qiwen007.com/zbwz/366283.html"

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36",
    }
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
# 源文章標(biāo)題
row_title_list = html.xpath('//div[@class="article_main_right"]/h1/text()')
# 源文章內(nèi)容
row_content_list = html.xpath('//div[@class="article"]//p/text()')
if row_title_list:
  row_title = row_title_list[0]
  print(replace_word(row_title,code_font ))
else:
  print('沒(méi)有提取到數(shù)據(jù)')
if row_content_list:
  row_content = '\n'.join(row_content_list)
  print(replace_word(row_content ,code_font ))
else:
  print('沒(méi)有提取到數(shù)據(jù)')

結(jié)果:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嚷缭,隨后出現(xiàn)的幾起案子饮亏,更是在濱河造成了極大的恐慌耍贾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件路幸,死亡現(xiàn)場(chǎng)離奇詭異荐开,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)简肴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門晃听,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人砰识,你說(shuō)我怎么就攤上這事能扒。” “怎么了辫狼?”我有些...
    開(kāi)封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵初斑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我膨处,道長(zhǎng)见秤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任灵迫,我火速辦了婚禮秦叛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瀑粥。我一直安慰自己挣跋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布狞换。 她就那樣靜靜地躺著避咆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪修噪。 梳的紋絲不亂的頭發(fā)上查库,一...
    開(kāi)封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音黄琼,去河邊找鬼樊销。 笑死,一個(gè)胖子當(dāng)著我的面吹牛脏款,可吹牛的內(nèi)容都是我干的围苫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼撤师,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剂府!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起剃盾,我...
    開(kāi)封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腺占,失蹤者是張志新(化名)和其女友劉穎淤袜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體衰伯,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铡羡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嚎研。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓖墅。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖临扮,靈堂內(nèi)的尸體忽然破棺而出论矾,到底是詐尸還是另有隱情,我是刑警寧澤杆勇,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布贪壳,位于F島的核電站,受9級(jí)特大地震影響蚜退,放射性物質(zhì)發(fā)生泄漏闰靴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一钻注、第九天 我趴在偏房一處隱蔽的房頂上張望蚂且。 院中可真熱鬧,春花似錦幅恋、人聲如沸杏死。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淑翼。三九已至,卻和暖如春品追,著一層夾襖步出監(jiān)牢的瞬間玄括,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工肉瓦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遭京,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓泞莉,卻偏偏與公主長(zhǎng)得像洁墙,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子戒财,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359