暑假如约而至,暑期档电影究竟是谁能脱颖而出呢?

字体反爬之猫眼电影_Python

目前看来是刚上映的银河补习班热度最高。但最后鹿死谁手还尚未可知,我们可以通过爬取猫眼的实时票房数据来一看究竟。

字体反爬之猫眼电影_爬虫_02

通过观察网页源代码,我们发现,票房的数字变成了.而不是我们在网页上看到的6276.7。这个网站采取了字体反爬,这也是一种常见的反爬技术。网站采用了自定义的字体文件,内容能够在浏览器上正常显示,但是爬取的数据就变成了乱码,如同下图的小方框。

字体反爬之猫眼电影_Python_03

 

采用自定义字体文件是CSS3的新特性,CSS3 @font-face中定义了字体,这个自定义字体文件存放到 web 服务器上,它会在需要时被自动下载到用户的计算机上,以便我们在访问网页时渲染出字体。

我们可以利用fontTools这个第三方库,来对这些字体文件进行操作。

  •  
from fontTools.ttLib import TTFontfont_1 = TTFont('a.woff')font_1.saveXML('font_1.xml')

我们可以在xml中查看这个字体文件。也可以利用百度字体编辑器,导入相应woff文档,便能够查看对应字体。

字体反爬之猫眼电影_编程语言_04

既然如此,那我们只要建立相应的映射关系,不就能解密字体了吗?easy~

字体反爬之猫眼电影_编程_05

经过观察发现,该字体文件每刷新一次页面都会发生更新,其name随着字体文件的变化而变化,这就令人有些头大了。

字体反爬之猫眼电影_编程_06

别着急,我们来看看这个xml页面吧。借助百度字体编辑器,发现这个F496代表的是4,而另外一个字体文件中的E603代表的是4。

字体反爬之猫眼电影_Python_07

我们在第一个xml中找到4,也就是F496,在第二个xml中也找4,即E603。

字体反爬之猫眼电影_编程_08

字体反爬之猫眼电影_爬虫_09

我们惊喜的发现这两个不同字体文件中对4的描述是完全一致的,仅仅是不同的字体文件它对4的命名不同,同时观察其他数字也能得到相同的结论。这样我们就找到了问题的关键,只要TTGlyph对象相同,它所表示的就是相同的数字。

我们可以先在本地存放一个base字体文件,这个字体文件中我们通过百度字体编辑器得到了一一对应关系。

  •  
font1=TTFont('./fonts/base.woff')base_dict={'uniE18E': '3', 'uniE585': '2', 'uniE194': '9', 'uniF439': '4', 'uniE7DB': '7','uniF115': '0',                'uniF0A4': '5', 'uniE311': '1', 'uniF7EF': '8', 'uniEACB': '6'}

 

再利用正则表达式从网页中下载到新的字体文件,并通过相同的字体对象构造新的映射关系,即新字体文件中定义的对象名与真实代表数字之间的对应关系。

  •  
response=requests.get(url,headers).text# 正则匹配字体woff文件font_file=re.findall(r'vfile\.meituan\.net\/colorstone\/(\w+\.woff)', response)[0]url2='http://vfile.meituan.net/colorstone/' + font_filenew_file=requests.get(url2,headers)with open('./fonts/'+font_file,'wb') as f:    f.write(new_file.content)font2=TTFont('./fonts/'+font_file)# font2.saveXML('font_2.xml')# 获取字符的name列表name_list2=font2.getGlyphNames()[1:-1]new_dict={}
for name2 in name_list2: obj2=font2['glyf'][name2]    for name1 in name_list1:           obj1=font1['glyf'][name1]            # 对象相等则说明对应的数字相同            if obj1==obj2: new_dict[name2]=base_dict[name1]

在解决了字体反爬的问题之后,我们能利用requests库和各种解析的库在页面源代码中提取我们想要的信息了。

  •  
def get_info(response):    '''    输入:页面源码    输出:包含电影票房等信息的字典列表        '''    # Mongo配置    conn=MongoClient('127.0.0.1', 27017)    db=conn.maoyan                    #连接maoyan数据库,没有则自动创建    mongo_my=db.film                  #使用film集合,没有则自动创建    items=[]    tree=etree.HTML(response)    film_name=tree.xpath('//div[@class="movie-item-info"]/p/a/text()')    booking_office_today=tree.xpath('//div[@class="movie-item-number boxoffice"]/p[@class="realtime"]/span/span/text()')    booking_office_total=tree.xpath('//div[@class="movie-item-number boxoffice"]/p[@class="total-boxoffice"]/span/span/text()')    a=tree.xpath('//div[@class="movie-item-number boxoffice"]/p[@class="realtime"]/text()')[1::2]    b=tree.xpath('//div[@class="movie-item-number boxoffice"]/p[@class="total-boxoffice"]/text()')[1::2]    for i in range(len(film_name)):        item={'film_name':film_name[i],              'booking_office_today':booking_office_today[i]+a[i].replace('\n',''),              'booking_office_total':booking_office_total[i]+b[i].replace('\n',''),                          }        items.append(item)        mongo_my.insert_one(item)           return items

字体反爬之猫眼电影_编程_10

 

 

字体反爬之猫眼电影_Python_11