記一次svg反爬學習

李帥將發表於2020-07-27

網址:http://www.porters.vip/confusion/food.html

開啟開發者工具後

 

 

 

頁面原始碼並不是真實的數字,隨便點一個d標籤檢視其樣式

 

 

 

我們需要找到兩個檔案,food.css和food.svg檔案,點開第一個紅框會在Sources皮膚開啟該檔案,滑鼠放到food.css檔名上,顯示的就是food.css的地址

滑鼠放到第二個紅框的url上面,得到的就是food.svg的地址,也可以右鍵Copy link address獲取地址

svg_url = 'http://www.porters.vip/confusion/font/food.svg'
css_url = 'http://www.porters.vip/confusion/css/food.css'

這裡我們開啟svg的地址後是這樣的

 

 

 我們會看到四行毫無規律的數字,檢視原始碼發現又是一堆看著像加密的程式碼(其實不是,這裡是svg-font的座標)

然後開啟css檔案

 

 

 這裡的background的css渲染數字的座標

到此,需要解釋一下為什麼會有兩個座標,及字元定位的問題:

瀏覽器根據css樣式中設定的座標和元素寬高來確定svg中對應的數字。

 

 

 接下來我們就只獲取下面圖中的電話號碼

 

 

 因為獲取d標籤的class屬性比較容易,這裡就簡單構造一個電話號SVG列表

獲取svg檔案和css檔案,以及構造電話號svg列表(這裡比較簡單,沒什麼解釋的)

def get_file(url):
    resp = requests.get(url=url)
    content = resp.text
    return content

svg_url = 'http://www.porters.vip/confusion/font/food.svg'
css_url = 'http://www.porters.vip/confusion/css/food.css'
css_content = get_file(css_url)
svg_content = get_file(svg_url)

# 獲取原始碼中電話號的SVG列表
svg_list = ['vhkbvu', 'vhk08k', 'vhk08k', 'vhk84t', 'vhk6zl', 'vhkqsc', 'vhkqsc', 'vhk6zl']
for svg_name in svg_list:
  print(svg_name)
  ...

在上一步,我們已經可以通過迴圈拿到每一個svg_name,接下來就是通過正則獲取css文字中,對應的svg_name的樣式(座標)

def get_css_coordinates(css_content, svg_name):
    res = re.findall('\.%s\s{\s+background:\s-(\d+)px\s-(\d+)px;\s}' % svg_name, css_content)
    if bool(res):
        x, y = res[0]
     return (int(x), int(y))

得到css_x和css_y座標後,拿著css_y座標去定位上面svg檔案中四行數字,獲取css_x,css_y座標對應的數字

from parsel import Selector
def
get_svg_text_content(svg_content, css_y): # 獲取svg中字元的font-size屬性,後面會用到 font_size = svg_content.split('font-size:')[1].split('px;')[0] svg_data = Selector(svg_content) # 獲取svg檔案 text元素的y屬性列表 svg_y_list = svg_data.xpath('//text/@y').getall() # 取到大於css_y且最近的一個 new_svg_y_list = [svg_y for svg_y in svg_y_list if css_y <= int(svg_y)] # print(new_svg_y_list[0]) # 獲取目標svg_y在原svg_y_list中的下標 index = svg_y_list.index(new_svg_y_list[0]) # print(svg_data.xpath('//text/text()').getall()) text_content = svg_data.xpath('//text/text()').getall()[index] return text_content, font_size

解釋一下,在上面所示程式碼中,在電話號svg列表中第一個元素vhkbvu,對應的css_y的值為97,而我們獲得的svg_y_list為 ['38', '83', '120', '164'],在此列表大於97且最近接的就是120,

因此我們確定svg_y的值為120,同時也確定了我們需要的是第三行資料,通過svg_y,也就是縱座標的值已經確定

現在我們獲取到了text_content為:671260781104096663000892328440489239185923,也就是上圖中的第三個text標籤中的文字,而font-size為14px

font-size也可以開啟svg檔案後找到style,檢視裡面font-size的值

下面我們來確定橫座標,橫座標確定後,我們就可以找到具體的數字值,從而完成破解

def get_char(text_content, css_x, font_size):
    text_chars = list(text_content)
    # 利用x軸的座標確定是第幾個元素
    n = css_x // int(font_size)
    print(text_chars[n])
    return text_chars[n]

至此,我們的svg反爬破解完成,下面是完整程式碼

import requests
import re
from parsel import Selector


def get_file(url):
    resp = requests.get(url=url)
    # print(resp.text)
    content = resp.text
    return content


def get_css_coordinates(css_content, svg_name):
    res = re.findall('\.%s\s{\s+background:\s-(\d+)px\s-(\d+)px;\s}' % svg_name, css_content)
    if bool(res):
        x, y = res[0]
        return (int(x), int(y))


def get_svg_text_content(svg_content, css_y):
    # 獲取svg中字元的font-size屬性,後面會用到
    font_size = svg_content.split('font-size:')[1].split('px;')[0]
    svg_data = Selector(svg_content)
    # 獲取svg檔案 text元素的y屬性列表
    svg_y_list = svg_data.xpath('//text/@y').getall()
    # 取到大於css_y且最近的一個
    new_svg_y_list = [svg_y for svg_y in svg_y_list if css_y <= int(svg_y)]
    # print(new_svg_y_list[0])
    # 獲取目標svg_y在原svg_y_list中的下標
    index = svg_y_list.index(new_svg_y_list[0])
    # print(svg_data.xpath('//text/text()').getall())
    text_content = svg_data.xpath('//text/text()').getall()[index]
    return text_content, font_size


def get_char(text_content, css_x, font_size):
    text_chars = list(text_content)
    # 利用x軸的座標確定是第幾個元素
    n = css_x // int(font_size)
    print(text_chars[n])
    return text_chars[n]


def get_phone():
    result = ''
    svg_url = 'http://www.porters.vip/confusion/font/food.svg'
    css_url = 'http://www.porters.vip/confusion/css/food.css'
    css_content = get_file(css_url)
    svg_content = get_file(svg_url)

    # 獲取原始碼中電話號的SVG列表
    svg_list = ['vhkbvu', 'vhk08k', 'vhk08k', 'vhk84t', 'vhk6zl', 'vhkqsc', 'vhkqsc', 'vhk6zl']
    for svg_name in svg_list:
        coordinate = get_css_coordinates(css_content, svg_name)
        if coordinate is not None:
            css_x, css_y = coordinate
            print(css_x, css_y)
            text_content, font_size = get_svg_text_content(svg_content, css_y)
            num = get_char(text_content, css_x, font_size)
            result += num
    print(result)


if __name__ == '__main__':
    get_phone()

最後說明:此案例只是我在學習《Python3反爬蟲原理與繞過實戰》裡面svg反爬的學習心得以及實踐,供參考,不喜勿噴

相關文章