python爬取猫眼遇到动态字体反爬

前一段时间,爬取了58同城,发现当时的网页对数字有字体反爬虫,然后废了九牛二虎之力找到了规律,终于破解了反爬虫,后来发现猫眼的这个网页虽然使用了字体反爬,但是和原来的58同城还是有很大的差别,后来了解到这个新的字体反爬虫属于动态字体加载。

首先我们登陆上猫眼的首页,然后可以看到

本来应该正常显示数字的部分,变成了一组我们看不懂的文本格式,然后我们去查看源码

 

发现在源码中是以html编码编写的,同时我们也可以在网页中找到

这个就是整个猫眼网页中使用的字体文本,所以我们需要花费很大的精力在这一部分,这个也是破解这个动态字体反爬的关键必要操作。

我们需要使用到一个叫做fontcreator的应用,可以自行在网上下载一下。

我们将这个应用下载下来,然后将上图中的

//vfile.meituan.net/colorstone/726c02e5f18a0632020d0d5c82ec81b72088.woff

这一段代码提取出来,然后组成一个url,用Python将网页中的信息提取出来,以为二进制的形式保存在.woff文件中,将其作为基础字体模板,用来比较后面的字体。

    def write_file(self,response_woff):
        #存储woff字体文件
        with open('get_fonts.woff','wb')as f:
            f.write(response_woff)

然后用刚刚下载的应用将这个woff文件打开。

我们将这个.woff文件命名为basefonts.woff,其目地就是为了对比以后获取到的字体woff文件。

baseFonts = TTFont('basefonts.woff')
        base_nums = ['1', '6', '5', '0', '7', '9', '8', '4', '2', '3']
        base_fonts = ['uniF2DD', 'uniF747', 'uniF01C', 'uniEBD0', 'uniEA1B', 'uniE897', 'uniE477', 'uniF53B', 'uniF1DF','uniE38B']

以上我么就完成了将字体模板解析和缓存在本地。

接下来,我们刷新网页,可以发现woff字体发生了改变,

我们对比两组字体发现对应的Unicode编码也并不相同,但是我们将.woff文件转换成xml文件,可以找到一定的规律。

font = TTFont("basefonts.woff")
font.saveXML('basefonts.xml')

我们将两个woff文件 都转换成xml格式,打开以后可以看到其中,对于字体笔画顺序的编写是相同的,

所以我们可以找出相应的规律,将先后得到的代码进行一一对应,就可以找出每一次加载出来的字体有什么变化规律。

base_nums = ['1', '6', '5', '0', '7', '9', '8', '4', '2', '3']
        base_fonts = ['uniF2DD', 'uniF747', 'uniF01C', 'uniEBD0', 'uniEA1B', 'uniE897', 'uniE477', 'uniF53B', 'uniF1DF','uniE38B']
        onlineFonts = TTFont('get_fonts.woff')
        #经过观察字体本身可以发现规律,第一个为None,最后一个也为None值,所以需要处理掉
        uni_list = onlineFonts.getGlyphNames()[1:-1]
        temp = {}
        #因为字体是动态加载出来的,所以需要实现字体的一一对应:
        for i in range(10):
            onlineGlyph = onlineFonts['glyf'][uni_list[i]]
            for j in range(10):
                baseGlyph = baseFonts['glyf'][base_fonts[j]]
                if onlineGlyph == baseGlyph:
                    temp["&#x" + uni_list[i][3:].lower() + ';'] = base_nums[j]

最后,我们就可以分析出整个动态字体反爬的心路历程了。

# -*- coding: utf-8 -*-
# Created : 2019/4/1

import re
import requests
from fontTools.ttLib import TTFont
from lxml import etree

class Maoyanspider:
    def __init__(self):
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36",
        }

    def parse_fonts(self,url):
        #获取网页源码
        response = requests.get(url,headers = self.headers).text
        #获取网页动态加载的字体模板
        self.get_font(response)
        #字体手动获取到的网页字体模板
        baseFonts = TTFont('basefonts.woff')
        base_nums = ['1', '6', '5', '0', '7', '9', '8', '4', '2', '3']
        base_fonts = ['uniF2DD', 'uniF747', 'uniF01C', 'uniEBD0', 'uniEA1B', 'uniE897', 'uniE477', 'uniF53B', 'uniF1DF','uniE38B']
        onlineFonts = TTFont('get_fonts.woff')
        #经过观察字体本身可以发现规律,第一个为None,最后一个也为None值,所以需要处理掉
        uni_list = onlineFonts.getGlyphNames()[1:-1]
        temp = {}
        #因为字体是动态加载出来的,所以需要实现字体的一一对应:
        for i in range(10):
            onlineGlyph = onlineFonts['glyf'][uni_list[i]]
            for j in range(10):
                baseGlyph = baseFonts['glyf'][base_fonts[j]]
                if onlineGlyph == baseGlyph:
                    temp["&#x" + uni_list[i][3:].lower() + ';'] = base_nums[j]
        #将被处理过的字体进行替换
        pat = '(' + '|'.join(temp.keys()) + ')'
        response_index = re.sub(pat, lambda x: temp[x.group()], response)
        #最终返回的是一个处理过(信息正常)的网页源码
        return response_index

    def get_font(self,response):
        #提取出woff文件,存储在本地
        woff = re.search(r"url\('(.*\.woff)'\)", response).group(1)
        woff_url = 'http:' + woff
        response_woff = requests.get(woff_url, headers=self.headers).content
        self.write_file(response_woff)

    def write_file(self,response_woff):
        #存储woff字体文件
        with open('get_fonts.woff','wb')as f:
            f.write(response_woff)
        font = TTFont("basefonts.woff")
        font.saveXML('basefonts.xml')

    def parse_url(self,response):
        #该response是已经处理过的网页源码,可以直接操作
        html = etree.HTML(response)
        ul_list = html.xpath('//ul[@class="ranking-wrapper ranking-box"]/li')
        content_list = []
        for ul in ul_list:
            item = {}
            #获取影片票房
            movie_price = ul.xpath(".//span[@class='stonefont']/text()")[0]
            #获取影片名称
            movie_name = ul.xpath(".//span[@class='ranking-top-moive-name']/text()|.//span[@class='ranking-movie-name']/text()")[0]
            movie_price_unit ="".join(ul.xpath(".//p[@class='ranking-top-wish']/text()|.//span[@class='ranking-num-info']/text()")).replace("\n","").replace(" ","")
            item['movie_name'] = movie_name
            item['movie_price'] = movie_price + movie_price_unit
            content_list.append(item)

        return content_list
    #将信息打印出来,也可以选择存储在数据库中
    def print_movie_info(self,content_list):
        for content in content_list:
            print(content)

    def run(self):
        #输入需要获取的网页
        url = 'https://maoyan.com/'
        #将网页信息提取出来,将被处理过的字体进行反处理后解析出来
        response = self.parse_fonts(url)
        #获取需要的信息
        content_list = self.parse_url(response)
        self.print_movie_info(content_list)


if __name__ == '__main__':
    maoyan = Maoyanspider()
    maoyan.run()

如有问题,欢迎斧正!

联系qq:986361369

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spider boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值