反爬篇 | 手把手教你处理 JS 逆向之字体反爬(下)

a8299f54a9231188a3c0af43a62c5218.jpeg

大家好,我是安果!

上一篇文章我们使用 Scrapy + Selenium 爬取了某个电影网站即将上映的影片

休闲时光:最近上映的电影与爬虫世界,带您彻底放松!

但是该网站针对一些比较敏感的数据(比如:票房、热度、评分等)做了字体反爬

17f4290a5b7f5837021d670af256ef38.jpeg

本篇文章将以「 影片热度 」为例,讲解字体反爬的完整处理方案

1、安装依赖

# 依赖
# OCR
pip3 install ddddocr

# 字体管理
pip3 install fontTools 

# 图片管理
pip3 install Pillow

2、下载字体及格式转换

通过分析,我们发现关键数字与网页中中引入的字体样式有关,并且每次刷新页面,引用的字体地址是变化的

c262036189a6b92a8778c994ce310509.jpeg

因此,我们需要获取网页源码,利用正则表达式解析出字体的下载地址

def download_font(url, font_path):
    headers = {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "Cache-Control": "max-age=0",
        "Connection": "keep-alive",
        "Host": "www.maoyan.com",
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "none",
        "Sec-Fetch-User": "?1",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
        "sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\""
    }

    # 获取网页源码
    resp_pre = requests.get(url, headers=headers)
    resp = resp_pre.text

    # 解析出字体文件(woff格式)的下载地址
    font_file = re.findall(r's3plus\.meituan\.net\/v1\/mss_73a511b8f91f43d0bdae92584ea6330b\/font\/(\w+\.woff)', resp)[
        0]
    font_url = 'https://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/' + font_file

获取字体 URL 下载地址后,我们将字体文件下载到本地

需要注意的是,下载字体时设置请求头和上面请求头不一致,不然下载的字体可能受损

font_headers = {
        'authority': 's3plus.meituan.net',
        'accept': '*/*',
        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'origin': 'https://www.**.com',
        'referer': 'https://www.**.com/',
        'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'sec-fetch-dest': 'font',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'cross-site',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
    }

 # 下载字体文件到本地
 # 注意:这里下载字体必须使用特有的Header,不然下载的字体会受损
 font_content = requests.get(font_url, headers=font_headers)
 with open(f'./{font_file}', 'wb') as f:
     f.write(font_content.content)
     f.close()

最后,将 woff 字体文件转换为 ttf 格式

from fontTools.ttLib.woff2 import decompress

...
# 将 woff文件转成 ttf 文件
decompress(font_file, font_path)

3、字体映射关系

通过 FontCreator 工具打开字体文件,可以获取数字和字体编码的映射关系

79bbc650d9ff8f081a5d3ad005cdf7c3.jpeg

通过对多个字体文件进行对比发现,上面的映射关系不是固定的

因此,我们需要借助字体图片绘制及 OCR,动态获取字体中的映射关系

from PIL import ImageFont, Image, ImageDraw
from io import BytesIO
from fontTools.ttLib import TTFont
from fontTools.ttLib.woff2 import decompress
import ddddocr

def get_font_keymap(font_path):
    ...

    # 映射字典
    font_dict = {}

    # 解析字体文件中的编码对应关系
    for cmap_code, glyph_name in font.getBestCmap().items():
        # print(cmap_code,glyph_name)

        # 实例化一个图片对象(给定的模式和大小创建一个新图像) 白色
        img = Image.new('1', (img_size, img_size), 255)
        # img.show()

        # 绘制图片
        draw = ImageDraw.Draw(img)

        x, y = draw.textsize(chr(cmap_code), font=font_img)

        draw.text(((img_size - x) // 2, (img_size - y) // 2), txt, font=font_img, fill=0)
        bytes_io = BytesIO()
        img.save(bytes_io, format="PNG")

        # 使用OCR识别字体
        content= ocr.classification(bytes_io.getvalue())

        # 加入到键值对中
        font_dict[glyph_name] = content
    return font_dict

4、网页内容还原

通过上面数字与字体编码的映射关系,我们将网页中做了字体反爬的内容替换为正确的数字

# 3、替换源码,将加密内容替换为明文
# .
for key in font_dict.keys():
    # print("key:", f'&#x{key[3:]};'.lower())
    # 去掉前面3个字符,将内容替换成对应的数字
    resp = resp.replace(f'&#x{key[3:]};'.lower(), font_dict[key])

5、爬虫

接下来,我们就可以对网页关键数据进行提取的

import re
from lxml import etree
import os

# 使用lxml解析HTML
root = etree.HTML(resp)
elements = root.xpath('//span[@class="stonefont"]/text()')

for element in elements:
    print(element)

我已经将文中所有源码上传到后台,回复关键字「 230710 」即可以获取完整源码

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!

推荐阅读

用 Python 远程控制 Windows 服务器,太好用了!

JavaScript 逆向爬虫中的浏览器调试常见技巧

JavaScript 逆向爬虫中的浏览器调试常见技巧(下)

反爬篇 | 手把手教你处理 JS 逆向之图片伪装

反爬篇 | 手把手教你处理 JS 逆向之字体反爬

END

好文和朋友一起看~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值