• Python3爬取起猫眼电影实时票房信息,解决文字反爬~~~附源代码


    上文解决了起点中文网部分数字反爬的信息,详细链接https://www.cnblogs.com/aby321/p/10214123.html

    本文研究另一种文字反爬的机制——猫眼电影实时票房反爬

    虽然都是仅仅在“数字”上设置了反爬,相同点与不同点如下:

    相同点:

    在“数字”上设置了文字反爬
    通过浏览器的“检查”显示的是“□”,但是可以在网页源代码中找到映射后的数字
    正则爬的是网页源代码,xpath是默认utf-8解析网页数据,用xpath爬出来的也是方框,因此只能使用正则匹配爬取关键数字信息

    不同点:

    起点中文网:
    每次刷新网页,通过正则爬取的数字不发生变化
    使用了自定义的文字文件ttf,可爬虫提取ttf的下载地址,通过分析字体文件可以找到映射关系,并且这个映射关系是固定的

    猫眼电影:
    每次刷新网页(中间间隔几秒时间),通过正则爬取的数字发生变化
    无字体文件ttf
    在源代码中搜索‘font-face’可查询到一堆字符串,预测应该是base64的编码方式,但是每次刷新网页这段字符串均会发生变化
    也就是说即使通过FontTools包将这段字符串转换成ttf文件,但是每次一刷新生成的ttf文件均会发生变化,因此ttf文件中的key和value也都发生了变化


    映射关系怎么找呢?

    通过研究发现,虽然每次ttf不一样,但是通过ttf生成的xml文件中TTGlyph中的坐标轴所表示的“数”是固定的,这也是我们要寻找的潜在对应关系

    举个栗子:
    生成的这个ttf通过FontCreator软件查看,例如‘uniE124’对应的是数字‘4’
    这个ttf生成的xml文件通过浏览器打开查看‘uniE124’对应的坐标是红色框内的内容;
    再次刷新网页,生成了新的ttf文件,重复上两个步骤,可以看到‘uniF878’对应的数字是‘4’,生成的xml文件中‘uniF878’对应的坐标与上个xml中的一样
    因此可以断定映射关系为:
    任意一次生成的xml中数字潜在对应的坐标是固定的

    
    

    
    
    爬取思路
    1. 某次提取网页源代码中的font-face中base64后面一段字符串,生成ttf文件和xml文件,并保存到本地
    2. 通过这两个文件研究其坐标和数字的对应关系,这个关系是固定不变的,每次生成的ttt/xml不同但是其内部对应关系都是这个,手动写入字典
    本例中的对应关系如下:
        fontdict_local = {
            'uniE346':7,
            'uniE3DB':2,
            'uniE4AC':4,
            'uniE6BF':5,
            'uniEA17':0,
            'uniEBBC':1,
            'uniEF7E':9,
            'uniF227':3,
            'uniF4C0':8,
            'uniF551':6
        }

    3. 通过正则匹配爬取网页源代码中设置了反爬机制的数字,并进行数据前期处理

    本例中需要进行以“;”分片,去掉“&#x”,判断是否含有小数点“.”,英文字母大小写转换等

    4. 最重要的一步,进行比对

    因为每次刷新页面或者重新运行程序都相当于会生成一个新的ttf/xml文件,因此需要将新的文件与第1步生成的文件(已保存到本地)进行坐标比对

    比如新文件中若有个数字对应的坐标与本地文件中的坐标是一样的,那么就根据第2步的字典判断这个数字,在程序中坐标可以用编码对象替换

    local代表是本地的文件

    font_local = TTFont('font_face_local.ttf')
    codelist_local = font_local.getGlyphNames()[1:-1]#第一个和最后一元素不是0-9的编码

    输出(codelist_local其实就是字典的keys,只不过通过读取ttf文件自动获取,但是注意要判断下这个列表,总共有12个元素,有两个元素不是0-9的key):codelist: ['uniE346', 'uniE3DB', 'uniE4AC', 'uniE6BF', 'uniEA17', 'uniEBBC', 'uniEF7E', 'uniF227', 'uniF4C0', 'uniF551']

        for i in codelist_local:
            obj_local = font_local['glyf'][i]#获取本地字体文件数字0-9的编码对象

    输出:

    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465950>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465930>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465A30>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465970>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465A50>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x034659B0>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x034659D0>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465990>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x03465A10>
    obj_local: <fontTools.ttLib.tables._g_l_y_f.Glyph object at 0x034659F0>


    也就是说本地的编码对象obj_local和codelist_local是一一对应的,并且每次生成的文件获取的编码对象是固定不变的
    每次运行生成的文件用同样的方法获取编码对象obj,与obj_local进行比对获取codelist_local值,在通过字典获取可展示的数值,还是通过图说明一下吧

    
    
    源代码
      1 """
      2 猫眼电影的票房等数据(数字)设置了文字反爬机制
      3 不同于起点中文网能直接看见ttf文件,猫眼的数字反爬更复杂些,主要表现在:
      4 进行了web上的base64编码,并且每次刷新都会变化
      5 通过FontTools可生成ttf和xml文件,但是每次运行生成的文件都不一样
      6 需要找寻其内部固定的映射关系
      7 """
      8 import requests, time, re, pprint,base64
      9 from fontTools.ttLib import TTFont
     10 from io import BytesIO
     11 from lxml import etree
     12 
     13 
     14 def get_relation(url):
     15     """
     16     获取网页源代码中的font—face中的内容,并保存成.ttf格式的文件
     17     :param url: <str> 需要解析的网页地址
     18     :return:<dict> 网页字体(动态变化的)编码与阿拉伯数字的映射关系
     19     """
     20     #获取网页源代码中的font—face中的内容
     21     font_face = re.findall('base64,(.*?)) format',html_data.text,re.S)[0]
     22     #print(font_face)
     23     #保存成.ttf格式的文件
     24     b = base64.b64decode(font_face)
     25     with open('font_face.ttf','wb') as f:
     26         f.write(b)
     27     font = TTFont('font_face.ttf')
     28     font.saveXML('font_face.xml')#将ttf文件生成xml文件并保存到本地
     29     codelist = font.getGlyphNames()[1:-1]  # 第一个和最后一元素不是0-9的编码
     30     font_local = TTFont('font_face_local.ttf')
     31     font_local.saveXML('font_face_local.xml')
     32     codelist_local = font_local.getGlyphNames()[1:-1]#第一个和最后一元素不是0-9的编码
     33     print('codelist:',codelist_local)
     34     fontdict_local = {
     35         'uniE346':7,
     36         'uniE3DB':2,
     37         'uniE4AC':4,
     38         'uniE6BF':5,
     39         'uniEA17':0,
     40         'uniEBBC':1,
     41         'uniEF7E':9,
     42         'uniF227':3,
     43         'uniF4C0':8,
     44         'uniF551':6
     45     }
     46     key = []
     47     value = []
     48     for i in codelist_local:
     49         obj_local = font_local['glyf'][i]#获取本地字体文件数字0-9的编码对象
     50         print('obj_local:',obj_local)
     51         for k in codelist:
     52             obj = font['glyf'][k]
     53             #print('obj:',obj)
     54             if obj == obj_local:
     55                 #print(k,fontdict_local[i])
     56                 key.append(k.strip('uni'))
     57                 value.append(fontdict_local[i])
     58     dict_relation = dict(zip(key,value))#网页字体(动态变化的)编码与阿拉伯数字的映射关系
     59 
     60     #print('网络文字映射关系:')
     61     #pprint.pprint(dict_relation)
     62     return dict_relation
     63 
     64 
     65 def get_decode_font(numberlist,relation):
     66     """
     67     对反爬数字进行解码
     68     :param numberlist: <list> 直接从网页源代码re获得的需要解码的数字
     69     :param relation: <dict> 解码所需的映射关系
     70     :return: <str> 解码后的数字
     71     """
     72     data = ''
     73     for i in numberlist:
     74         numbers = i.replace('&#x','').upper()
     75         #print(numbers)
     76         #小数点没有单独成为里列表的元素,与后面的数字写在了一起,需要判断下
     77         if len(numbers)==5:
     78             data += '.'
     79             numbers = numbers.strip('.')
     80             #print('numbers:',numbers)
     81         fanpa_data = str(relation[numbers])
     82         data += fanpa_data
     83     print('实时票房(万元):',data+'
    ')
     84     #return data
     85 def get_movie_info(url):
     86     """
     87     爬取网页的影片名称和实时票房(网页源代码中未解码的数字)
     88     :param url:
     89     :return:
     90     """
     91     selector = etree.HTML(html_data.text)
     92     infos = selector.xpath('//*[@id="ticket_tbody"]/ul')
     93     boxes = re.findall('<b><i class="cs">(.*?)</i>',html_data.text,re.S)
     94     for info,i in zip(infos,boxes):
     95         movie_name = info.xpath('li[1]/b/text()')[0]
     96         movie_box = i.split(';')[0:-1]
     97         print('影片名称:',movie_name)
     98         #print('网页直接获取的影片实时票房:',movie_box)#一维列表形式
     99         relation = get_relation(url)
    100         get_decode_font(movie_box,relation)
    101 
    102 url = 'https://piaofang.maoyan.com/?ver=normal'
    103 headers = {
    104     'User-Agent': 'User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
    105 }
    106 html_data = requests.get(url, headers=headers)
    107 get_movie_info(url)

    运行结果:


    还有一种更加简洁的方法:
    找到obj_local和数字的关系写入字典,去掉中间的codelist_local

  • 相关阅读:
    Flask第二篇——服务器相关
    Flask第一篇——URL详解
    Appium 定位方法例子(4)
    selenium 上传文件方法补充——SendKeys、win32gui
    Appium+python (3) 异常处理
    Appium+python (3) 元素定位(1)
    "http://127.0.0.1:4723/wd/hub"的解释
    Appium + Python App自动化(2)第一个脚本
    Appium+python(1)简单的介绍环境搭建
    用fiddler设置手机代理
  • 原文地址:https://www.cnblogs.com/aby321/p/10232209.html
Copyright © 2020-2023  润新知