题目地址:
点击进入
单个页面的数据如下,从网页元素中可以看出,网页源码中的数字和我们看到的数字并不同,如果简单爬取网页内容肯定无法达到题目要求。注意右下角篮圈中的字体样式,这就是
372
能显示成
127
的关键所在。
由于
font-family: glided_sky;
样式以Base64的形式存放在网页中,且网页元素每次刷新都会有不同的网页数字,这里为了便于分析就直接把第一页保存在本地用于编程演示。
把网页中的字体数据保存解码出来。
import requests
from lxml import etree
import tools
import base64
import xml.dom.minidom
from bs4 import BeautifulSoup
import re
fhtml = open('GlidedSky字体加密1.html', 'r', encoding='utf-8')
soup = BeautifulSoup(fhtml, 'lxml')
html = str(soup.select('style'))
base64font = re.findall("base64,(.*?)\) format", html)[0]
font = base64.b64decode(base64font)
with open("字体文件.ttf", mode="wb") as f:
f.write(font)
用 FontCreator 打开 字体文件.ttf 已经可以看到映射关系了。
再用Python fontTools库 解析字体文件。
from fontTools.ttLib import TTFont
font = TTFont('字体文件.ttf')
font.saveXML("字体文件.xml")
解析后的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.24">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="nine"/>
<GlyphID id="2" name="three"/>
<GlyphID id="3" name="seven"/>
<GlyphID id="4" name="zero"/>
<GlyphID id="5" name="one"/>
<GlyphID id="6" name="four"/>
<GlyphID id="7" name="eight"/>
<GlyphID id="8" name="two"/>
<GlyphID id="9" name="five"/>
<GlyphID id="10" name="six"/>
</GlyphOrder>
...省略...
<tableVersion version="0"/>
<cmap_format_4 platformID="0" platEncID="3" language="0">
<map code="0x30" name="zero"/>
<map code="0x31" name="one"/>
<map code="0x32" name="two"/>
<map code="0x33" name="three"/>
<map code="0x34" name="four"/>
<map code="0x35" name="five"/>
<map code="0x36" name="six"/>
<map code="0x37" name="seven"/>
<map code="0x38" name="eight"/>
<map code="0x39" name="nine"/>
</cmap_format_4>
<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x30" name="zero"/>
<map code="0x31" name="one"/>
<map code="0x32" name="two"/>
<map code="0x33" name="three"/>
<map code="0x34" name="four"/>
<map code="0x35" name="five"/>
<map code="0x36" name="six"/>
<map code="0x37" name="seven"/>
<map code="0x38" name="eight"/>
<map code="0x39" name="nine"/>
</cmap_format_4>
</cmap>
...省略...
由 <cmap> 和 <GlyphOrder> 可以列出下面映射。
字符 | code(Ascii编码值) | name | id | FontCreator看到的绘制文字 |
---|
0 | 0x30 | zero | 4 | 3 |
1 | 0x31 | one | 5 | 4 |
2 | 0x32 | two | 8 | 7 |
3 | 0x33 | three | 2 | 1 |
4 | 0x34 | four | 6 | 5 |
5 | 0x35 | five | 9 | 8 |
6 | 0x36 | six | 10 | 9 |
7 | 0x37 | seven | 3 | 2 |
8 | 0x38 | eight | 7 | 6 |
9 | 0x39 | nine | 1 | 0 |
通过表格,我想到了三种方法还原字体:
- 手动映射,直接手动写一个【字符–FontCreator看到的绘制文字】关系映射表,通过这个映射表来获取实际文字。优点:几乎没有技术门槛,编码快捷。缺点:如果字符较多则手动工作量巨大,且无法应对动态字体加密。
- 编码中查找【字符–FontCreator看到的绘制文字】的逻辑关系,比如上面可以通过字符的Ascii查询到name,再由name查到对应的id,最后减1即是FontCreator绘制的数字。优点:技术门槛不高。缺点:字符与实际绘制文字并不一定就有逻辑关系,且无法应对动态字体加密。
- OCR识别,通过字符对应OCR识别出的字符建立映射关系。优点:可以应对动态字体加密。缺点:技术门槛较高,识别可能不准确。
针对这道题来说,总共也就9个数需要映射,而且每次刷新页面,网页内容的数字都会改变,但实际看到的数字不变。好在每次刷新都符合通过字符的Ascii查询到name,再由name查到对应的id,最后减1即是FontCreator绘制的数字规律。于是可以按如下编码爬取:
import requests
from lxml import etree
import tools
import base64
from fontTools.ttLib import TTFont
import xml.dom.minidom
from bs4 import BeautifulSoup
import re
def saveHtml(url, output_name):
"""保存网页内容到本地文件"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
'Cookie': '__gads=ID=24468711568854be-22bdaffd29c8007c:T=1620874506:RT=1620874506:S=ALNI_MYJCrE0xSUvEfDx7UFPoqgfc0oEoQ; _ga=GA1.2.1515855357.1620874505; footprints=eyJpdiI6IkRZWEx6UFJaNUVOMHVIbkVFSTFvdkE9PSIsInZhbHVlIjoiTUFtM2xOTVZWVVJROUFuYWZuOWlIcFlsbCtaMzFxYzFocmVrZXZlenpCUDZ6K2RJdGp0a1lJOVNzU3NjWEZ6eiIsIm1hYyI6ImUyZjA5ZWZjOGE1YjFlOTcxZjkyYjZiMWJhZWI2MWUwMjQ2MGJlY2U5N2MwMDk0OGM1ZTU4YzMyMTIxMGYxYTIifQ%3D%3D; remember_web_59ba36addc2b2f9401580f014c7f58ea4e30989d=eyJpdiI6Im5laDlOMkFKMWx2UHFLMjliWlhHOEE9PSIsInZhbHVlIjoiaDkrZjIySWo0cnk1bEN4cmxhMGd5Z3hxTFNDc1VTejZsdTBoZTJnRVh4a3BxeXJhSWZqWXBmZnYyYzFnWjdleEg5R0hSN2traW9VcmZjZkJNXC9aaVdEMEN5UERZVktiSllUcURKT1VkZ1F6TGl3TXE2YXRoUWFBSVNhMDN5TE04d2NqTk12Y3NRa3FHdGFXbWo5azQwN1pBNnlqWGhBY0pzS0pxd0VNc2ZKdz0iLCJtYWMiOiI5OTFjNTU0YWNlYTk2YWQzZjlkNjI4NDlhYjY1NzEzYTk4YjBlNTcxNzVkNTg5OTBiNjg4NWI0ZTc5NTY4OGI4In0%3D; _gid=GA1.2.1650202755.1621993422; Hm_lvt_020fbaad6104bcddd1db12d6b78812f6=1621994369,1621994747,1621994780,1621994782; XSRF-TOKEN=eyJpdiI6InAwaURUOFNCWjdqU2lCNGV2VFNBVkE9PSIsInZhbHVlIjoidGM1RHFRYnlYQ1ZRdXpoOGJsc2JEQlc1Ym9QZ3VMMXNJY3hOM0lOQlN0VnZJOEpXOVJVUmZZblJFRVh1K0FQcCIsIm1hYyI6IjNlZGI2NGQ0Yjg3NDc2NDNmNzVkYjA2NzhiY2M0YjI2NTliMTUwMTgyZDM0ZmIzNWJlYmYyMjI0NGE0MzM0YjcifQ%3D%3D; Hm_lpvt_020fbaad6104bcddd1db12d6b78812f6=1622078279; glidedsky_session=eyJpdiI6ImRzV2xnQm9LUHJmWFJnUWsydndlUVE9PSIsInZhbHVlIjoiSHZia2dKYnViSmpcL1dZRCtBTGV4bG12RWUxa0YyZnMxaGFPR3RwSEtjWEdJc3FoU2tjd3pmUzIxTnJBa3dEVWUiLCJtYWMiOiJkZTYzMjQyZWU3ZDA4NmY4OTEwZjJiOTI4YzFmY2ZiYmM3NGJkMjQxNjU4MGE2ZTgzZTIzYzNhMWEzZTA4OWUwIn0%3D'
req = requests.get(url, headers=headers)
f = open(output_name, 'w', encoding='utf-8')
for i in req.text:
f.write(i)
print('网页保存到'+output_name)
def saveFont(input_name, output_name):
"""从网页文件中获取字体文件"""
fhtml = open(input_name, 'rb')
soup = BeautifulSoup(fhtml, 'lxml')
html = str(soup.select('style'))
base64font = re.findall("base64,(.*?)\) format", html)[0]
font = base64.b64decode(base64font)
with open(output_name, mode="wb") as f:
f.write(font)
print('字体保存到'+output_name)
def saveXML(input_name, output_name):
"""字体文件中获取xml信息"""
font = TTFont(input_name)
font.saveXML(output_name)
print('字体xml保存到'+output_name)
def getCodeIdMap(input_name):
"""返回 id--实际显示的数 字典"""
fxml = open(input_name, 'rb')
soup = BeautifulSoup(fxml, 'html.parser')
items_map = soup.find('cmap').find('cmap_format_4').find_all('map')
code_map = {}
for item in items_map:
code = item['code']
name = item['name']
code_map[code] = name
items_glyphid = soup.find('glyphorder').find_all('glyphid')
id_map = {}
for item in items_glyphid:
id = item['id']
name = item['name']
id_map[name] = id
code_id_map = {}
for code in code_map:
name = code_map[code]
id = id_map[name]
code_id_map[code] = int(id) - 1
return code_id_map
def getHtmlNum(input_name, code_id_map):
"""从网页文件中获取要爬取数字并转换成真实看到的数字"""
fhtml = open(input_name, 'rb')
soup = BeautifulSoup(fhtml, 'html.parser')
strNums = soup.find_all('div',class_='col-md-1')
realNums = []
for strNum in strNums:
strFake = strNum.get_text().strip()
realStr = ""
for n in strFake:
code = hex(ord(n))
realNum = code_id_map[str(code)]
realStr += str(realNum)
realNums.append(realStr)
return realNums
if __name__ == "__main__":
page = "1"
url = 'http://glidedsky.com/level/web/crawler-font-puzzle-1?page=' + page
html_name = page + ".html"
font_name = page + ".ttf"
xml_name = page + ".xml"
saveHtml(url, html_name)
saveFont(html_name, font_name)
saveXML(font_name, xml_name)
code_id_map = getCodeIdMap(xml_name)
realNums = getHtmlNum(html_name,code_id_map)
print(realNums)
运行,成功:
为了能够更加清除的知道什么是字体反爬,我们直接上图看一下。
大家看一下,很明显的就能发现我们正常浏览能够看到数据,但是当我们打开Elements面板定位到对应元素上时发现竟然不是我们在页面上看到的数据,而是一些特殊的符号。我们按Ctrl+F直接看看页面源码,
是不是很奇怪,竟然是一些编码。这个就是字体反爬,这里的字体都是自定义的字体,在CSS3之前.........
时隔这么多天,终于能有时间写抽空写篇博文了,正值今天1024程序员节,所以写了篇字体反扒博文给大家,希望大家看后能有所搜获!
本次反扒对象是 GlidedSky 网站的题目,说实话有点难度,但逻辑搞通的话其实还好,话不多说,开搞!!!
温馨提示:如果想练习需要先注册账号,而且题目不是一次性全部出来的,类似于闯关类型,如果是新用户需要先完成前面的题,才能解锁后面的题目,对于前面题目的讲解,我已经发表过博文
实例代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="tex
[size=medium]续前一篇博文[url=http://rubel.iteye.com/blog/891657]《几个文字加密的简洁算法和一些个人的想法》[/url]——字符平移法
以及上一篇博文[url=http://rubel.iteye.com/blog/897191]《几个文字加密的简洁算法(续)》[/url]——字符错位法
这里提供第三种加密方法——对字符 [b]Unicode...
B的编译有两种方式,一种是P-Code方式,一种是本机代码。P_Code方式是VB
从早期版本保留下来的,也是比较难
逆向的一种。而本机代码方式编译的程序已经
和VC很接近了。这里只探讨以本机代码方式编译的程序。由于微软在VB的实现方面
的资料很少,使人觉得VB的
逆向比较难。其实掌握一些规律,VB的程序还是很容易
vue示例 url跳转时可以使用encodeURI,
解密使用decodeURI
//
加密:
https://www.baidu.com/#/login?Name=${encodeURI(this.nickName)}&id=${this.id}
//
解密:
decodeURI(this.$route.query.Name)
[size=medium]未深入了解过数据加密——我想那应该是科学家们的事。
俺小程序员一个,但喜欢思考,琢磨过“加密的问题”良久良久……(没有最终结果) 这里说说自己琢磨之后的一些想法,仅供茶余饭后 没事瞎想时的一些零星素材。
“加密”嘛,顾名思义,基本就是要别人看不明白本来可以明白的xx,所以,对于我们日常看见的文字,只要让别人看不明白就算加密了。
不同于对其他信息的加密...