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+'\n')
 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM