思路:
爬取网页 -> 提取信息及字体文件地址 -> 字体下载 -> 字体解析为 XML 文档 -> 将 XML 文档中的字的坐标与自己摸索规律得到的数据库做比对 -> 得到相应信息
进阶:
如果字体坐标加入每次加上随机值,可以检测坐标区间
杀手锏:
将坐标画出来,图片经由深度学习进行训练,此方法正确率及稳定性高,但模型构建与训练需要一定时间,过几日补上我的模型地址。
下面以 猫眼深圳 的影院信息为例:
字体乱码显示
可以看到网页返回的不是真实数字,而是由 stonefont
字体渲染得到的。
所以我们接下来的方向就是发出请求,得到字体文件并进行解析:
- 发出请求
import requests
url = 'https://maoyan.com/cinemas?offset=12'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0',
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print(response.content.decode('utf-8'))
else:
print(response.status_code)
可以看到第一个电影院 「华夏星越影城」 的信息如下:
<div class="cinema-cell">
<div class="cinema-info">
<a href="/cinema/17275?poi=156641511" class="cinema-name" data-act="cinema-name-click" data-bid="b_4tkpau4m" data-val="{city_id: 30, cinema_id: 17275}">华夏星越影城(杜比全景声·民治万盛店)</a>
<p class="cinema-address">地址:龙华区民治街道民治大道万众城万盛百货四楼</p>
</div>
<div class="buy-btn">
<a href="/cinema/17275?poi=156641511" data-act="buy-btn-click" data-val="{city_id: 30, cinema_id: 17275}" data-bid="b_4tkpau4m">选座购票</a>
</div>
<div class="price">
<span class="rmb red">¥</span>
<span class="price-num red"><span class="stonefont">.</span></span>
<span>起</span>
</div>
</div>
- 在源代码中搜索
stonefont
得到字体文件地址为:
http://vfile.meituan.net/colorstone/2b72be2a972359c0de8bac4681d75ecc2080.woff
<style>
@font-face {
font-family: stonefont;
src: url('//vfile.meituan.net/colorstone/29640a4087c9cd8e7fc41e261d35f3e03168.eot');
src: url('//vfile.meituan.net/colorstone/29640a4087c9cd8e7fc41e261d35f3e03168.eot?#iefix') format('embedded-opentype'),
url('//vfile.meituan.net/colorstone/2b72be2a972359c0de8bac4681d75ecc2080.woff') format('woff');
}
.stonefont {
font-family: stonefont;
}
</style>
- 将字体文件下载下来并上传到 http://fontstore.baidu.com/static/editor/index.html 进行解析
字体文件解析
可以看到源代码中的票价.
中每一个的后四位对应于字体文字名称的后四位,如f2dd
对应于 2,f43f
对应于 4,eef4
对应于 9,即总的数字为 24.9,与我们在网页看到的数字一致。
电影价格为 24.9
需要注意的是每次发送请求,猫眼所用的字体文件是不一样的,也就是说这次 f2dd
对应数字 2,但下次就不是 f2dd
,所以我们要想办法得到不同字体文件之间的内在规律。
我们利用 Python 的 fonttools 第三方库来将我们的字体文件转为 XML 格式。
from fontTools.ttLib import TTFont
font = TTFont('./2b72be2a972359c0de8bac4681d75ecc2080.woff')
font.saveXML('2b72be2a972359c0de8bac4681d75ecc2080.xml')
保存的文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.43">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
<GlyphID id="0" name="glyph00000"/>
<GlyphID id="1" name="x"/>
<GlyphID id="2" name="uniEEF4"/>
<GlyphID id="3" name="uniF54E"/>
<GlyphID id="4" name="uniF5C7"/>
<GlyphID id="5" name="uniEFEA"/>
<GlyphID id="6" name="uniE9C0"/>
<GlyphID id="7" name="uniE1FA"/>
<GlyphID id="8" name="uniEBC9"/>
<GlyphID id="9" name="uniF43F"/>
<GlyphID id="10" name="uniEF28"/>
<GlyphID id="11" name="uniF2DD"/>
</GlyphOrder>
<TTGlyph name="uniE1FA" xMin="0" yMin="-12" xMax="508" yMax="719">
<contour>
<pt x="42" y="353" on="1"/>
<pt x="42" y="483" on="0"/>
<pt x="67" y="557" on="1"/>
<pt x="93" y="635" on="0"/>
<pt x="197" y="719" on="0"/>
<pt x="275" y="719" on="1"/>
<pt x="389" y="719" on="0"/>
<pt x="448" y="628" on="1"/>
<pt x="476" y="586" on="0"/>
<pt x="492" y="522" on="1"/>
<pt x="508" y="462" on="0"/>
<pt x="508" y="353" on="1"/>
<pt x="508" y="290" on="0"/>
<pt x="496" y="188" on="0"/>
<pt x="482" y="149" on="1"/>
<pt x="455" y="71" on="0"/>
<pt x="354" y="-12" on="0"/>
<pt x="275" y="-12" on="1"/>
<pt x="172" y="-12" on="0"/>
<pt x="112" y="62" on="1"/>
<pt x="42" y="150" on="0"/>
</contour>
<contour>
<pt x="132" y="353" on="1"/>
<pt x="132" y="176" on="0"/>
<pt x="213" y="60" on="0"/>
<pt x="335" y="60" on="0"/>
<pt x="418" y="177" on="0"/>
<pt x="418" y="529" on="0"/>
<pt x="376" y="588" on="1"/>
<pt x="336" y="646" on="0"/>
<pt x="213" y="646" on="0"/>
<pt x="177" y="595" on="1"/>
<pt x="132" y="529" on="0"/>
</contour>
<instructions/>
</TTGlyph>
....下面还有很多代码
可以看到有 11个 id
,他们的值就是我们刚刚看到的百度在线解析得到的字体文件名后4位。下面的 TTGlyph 我的理解是 x, y
分别代表点的横纵坐标,0, 1
代表不同的点(可能是连线的方向)。我从字体文件中取了数字 7 对应的 x, y
值并在 matplotlib 中将这些点绘制出来
数字 7 的绘制
可以看到的是基本轮廓就是数字 7,这也验证了我们的猜想。多观察几个字体文件可以发现,每个字体文件中同一个数字的名称虽然不一样,但是其 TTGlyph 节点下的数据是一样的。所以我们只需要先请求几次,把每个数字对应的所有点的坐标值与该数字一一对应。当有新的响应时,从响应中提取字体文件,将其转化为 XML 后的坐标值与库进行比对,得到每个数字的代码,然后用这个代码去替换我们得到的乱码的数字。
参考资料:
https://cuiqingcai.com/6431.html
https://www.cnblogs.com/gl1573/p/9994286.html
http://fontstore.baidu.com/static/editor/index.html