Python 字体爬虫应对

2019-06-26  本文已影响0人  牛奶加冰ooo

思路:
爬取网页 -> 提取信息及字体文件地址 -> 字体下载 -> 字体解析为 XML 文档 -> 将 XML 文档中的字的坐标与自己摸索规律得到的数据库做比对 -> 得到相应信息
进阶:
如果字体坐标加入每次加上随机值,可以检测坐标区间
杀手锏:
将坐标画出来,图片经由深度学习进行训练,此方法正确率及稳定性高,但模型构建与训练需要一定时间,过几日补上我的模型地址。

下面以 猫眼深圳 的影院信息为例:

网址:https://maoyan.com/cinemas?offset=12

字体乱码显示

可以看到网页返回的不是真实数字,而是由 stonefont 字体渲染得到的。
所以我们接下来的方向就是发出请求,得到字体文件并进行解析:

  1. 发出请求
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">&#xf2dd;&#xf43f;.&#xeef4;</span></span>
            <span>起</span>
        </div>
      </div>
  1. 在源代码中搜索 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>
  1. 将字体文件下载下来并上传到 http://fontstore.baidu.com/static/editor/index.html 进行解析
    字体文件解析
    可以看到源代码中的票价 &#xf2dd;&#xf43f;.&#xeef4 中每一个的后四位对应于字体文字名称的后四位,如 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

上一篇 下一篇

猜你喜欢

热点阅读