背景 && 摘要

所谓道高一尺,魔高一丈.爬虫与反爬虫之间的斗争从来没有休止过. 然而正是这种博弈,推动着技术的不断发展前进,没有永远的胜利者, 也没有永远的失败者.只有短暂的互相压制.

缘起实验室小妹爬猫眼电影数据,遇到了这样的问题, 无法解决,来和我讨论时引起了我的注意,经过几个小时的研究,成功解决. 今天我们来就说一下 @font-face 方法的反爬虫解决策略.

环境

Ubuntu 16.04 x64 + Python3

问题的尴尬处境

随便找一部电影,查看它的累计票房,显示如下:

奇怪的数据显示

起初我还以为是网页编码问题,但是看了一下源码是这样的:

查看源码显示

"这个不就是 HTML Entity 嘛!",我的下意识反应就是这样的.但是查了查, 根本查不到这些 HTML 实体,然后以为是十六进制编码,用 python 解码了一下,啥也不是.

这个时候我注意到,当在样式中勾选掉 font-family 时,在网页上显示的字体也将失效, 进一步在 chrome 的 Elements 中搜索 stonefont,我们找到了下图这里.

stonefont 字体

到这里就算是找到线索了,然后上网搜索 @font-face 反爬虫, 很快就知道了这是通过自定义字体来进行网页数据的显示. 可以看到样式里有三个 url,删除前面两个不影响显示,而删除最后一个则字体显示不正常, 所以我们可以定位到实际的解析字体应该是 .woff 字体,当然 eot 也是字体, 但是对于我目前处境而言,解析 woff 字体肯定是正确的.所以剩下的问题就是, 如何找到 WOFF 字体的字形和自行编码值的映射表.这自然少不了要解析字体, 难道要自己去解析字体吗?我刚开始是这么想的,但是一想到字体结构, 就有点不想动手了,太繁琐了,于是就找到了大名鼎鼎的 fontforge.

工具在手,天下我有,但问题是,咋用到 python 中去呢? 很不幸, PIP 库里面没有 fontforge, 而且也没有人编译好 Python 扩展包,怎么办,自己动手丰衣足食.

编译 fontforge

编译安装 fontforge 以及 Python3 支持

sudo apt install libtool m4 automake autoconf
sudo apt install libpangox-1.0-dev

git clone git@github.com:fontforge/fontforge.git
cd fontforge && ./bootstrap
PYTHON=python3 ./configure
make
sudo make install
sudo ldconfig

其中 configure 配置结果如下:

Configuration:

  Source code location	.
  Build code location
  Destination prefix	/usr/local
  Compiler		gcc

Summary of optional features:

  real (floating pt)	double
  programs		yes
  native scripting	yes
  python scripting	yes
  python extension	yes
  freetype debugger	no
  raw points mode	no
  tile path		no
  gb12345 encoding	no
  potrace or autotrace	no

Summary of optional dependencies:

Optional Library	UseIt?	HaveIt?	WebsiteURL
  cairo			check	yes	http://www.cairographics.org/
  giflib		check	no	http://giflib.sourceforge.net/
  libjpeg		check	yes	http://en.wikipedia.org/wiki/Libjpeg
  libpng		check	yes	http://www.libpng.org/
  libreadline		check	no	http://www.gnu.org/software/readline
  libspiro		check	no	https://github.com/fontforge/libspiro
  libtiff		check	yes	http://en.wikipedia.org/wiki/Libtiff
  libuninameslist	check	no	https://github.com/fontforge/libuninameslist
  zeromq (libzmq)		no	http://www.zeromq.org/
  X Window System		yes	http://www.x.org/

这同时安装了 fontforge 的 GUI 程序以及 Python3 包.

首先用 wget 下载上面提到的 woff 字体,然后用 fontforge 打开, 然后勾选 Encoding/Compact,显示如下图所示:

fontforge 显示字体

很快的,我们发现 1 的编码值正好是 0xEFD4,正好和网页源码中的相对应. 所以下面我们只需要找到这些显示的字体和它们的编码值即可. 另一个很自然的疑问就是,如何能够自定义字体呢,经过查找, 维基给了很好的解释, 几个自定义字体的区域为:

  1. U+E000–U+F8FF
  2. U+F0000–U+FFFFD
  3. U+100000–U+10FFFD
这样的话,我们就可以遍历字体中的所有字形,提取私有区域的自型进行识别. 字形提取出来之后,需要进行识别,我们使用 pytesseract 这个包.稍后马上提到.

修正 Python3 安装包路径

Fontforge Python 包这里安装的位置为 /usr/local/lib/python3.5/site-packages,生成的文件如下:

 fontforge.la  fontforge.so  psMat.la  psMat.so 
但上述路径默认不在 sys.path 中,两种方法解决:

1:动态导入:

python3 -c "import sys; sys.path.append('/usr/local/lib/python3.5/site-packages'); import fontforge; f=fontforge.font(); print(f)";

2:新建 pks.pth

在 ~/.local/lib/python3.5/site-packages 下面新建一个pkgs.pth,写入下面一行:

/usr/local/lib/python3.5/site-packages

解决问题

fontforge 的相关 Python API 可以参考这里.

刚刚说过,为了识别字体,我们使用 pytesseract 包,安装方法如下:

sudo pip3 install pytesseract

然后我们就可以愉快的处理字体了,下面是代码:

#! /usr/bin/env python3
#! -*- coding:utf-8 -*-
import fontforge
import pytesseract
from PIL import Image
import os

def ispua(val):
    if 0xE000 <= val <= 0xF8FF: return True
    if 0xF0000 <= val <= 0xFFFFD: return True
    if 0x100000 <= val <= 0x10FFFD: return True
    return False

fontpath = "/home/bugnofree/Downloads/1ac08d94fdfdf8c0e76b72d33b7d25f22084.woff"
fontobj = fontforge.open(fontpath)
for glyph in fontobj.glyphs():
    codeval = hex(glyph.unicode)
    if(not ispua(int(codeval,16))):continue
    imgname = codeval + ".png"
    glyph.export(imgname)
    image = Image.open(imgname)
    ocrval = pytesseract.image_to_string(image,lang = 'eng',config  = '-psm 10')
    print("%s : %s" % (codeval,ocrval))
    if(os.path.exists(imgname)):os.remove(imgname)

代码中唯一需要注意的是 pytesseract 需要配置 "-psm 10" 这一参数,至于其含义, 可以查看 tesseract 的帮助手册,我就不多说了.

处理结果如下所示:

字体成功识别

这样找到映射表以后其他就都好说了,具体爬取数据不是我的兴趣所在,在此略过.

其他问题

如果打开 fontforge 时提示 No module named 'gtk' import gtk, 那么打开 /usr/local/share/fontforge/python/graphicore.py 注释掉 import graphicore.shell 即可.





Contact me by dXAyZ2Vla0AxNjMuY29tCg==
OR
Follow me on Sinablog

Copyright ©2017 by bugnofree All rights reserved.