Pythonクローラー:フォントの暗号化とフォントのアンチクロール

はじめに:フォントのアンチクライミングは、58.com、猫眼電影の映画興行収入、Autohome、Tianyancha、その他のWebサイトなどの一般的なクライミング防止テクノロジーでもあります。これらのWebサイトは、ブラウザに通常表示されるカスタムフォントファイルを使用しますが、クローラーによってクロールされたデータは、カスタムフォントファイルを使用し、スタイルを参照するためにオンラインでロードするため、文字化けするか、他の文字に変換されます。 CSS3の新機能であるCSS3を使用すると、Webデザイナーは好きなフォントを使用できます。クローラーはオンラインフォントを積極的に読み込まないため、

フォントの暗号化とは、通常、Webページがデフォルトの文字エンコードセットを変更し、独自に定義したフォントファイルをフォントスタイルとしてWebページにロードすることを意味します。これにより、番号を正しく表示できますが、ソースコードの同じ2進数はロードされません。カスタムフォントファイルがロードされていないため、コンピュータのデフォルトのエンコーディングが文字化けしました。

目標

目標:住宅情報を取得するために、今日58の同じ都市の賃貸情報をクロールする方法を学びましょう。

データスクレイピング

以前に学んだ基本的なクローラーの知識に従い、キーボードを手に取って直接アクセスしましょう(経験がなければ、フォントクロールが何であるかわかりません)。xpathで解析していて、BeautifulSoupの抽出を忘れてしまいました。ここでは、この抽出を使用します。レビューを確認します。

import requests
from bs4 import BeautifulSoup


url = 'https://cs.58.com/chuzu/?PGTID=0d100000-0019-e310-48ff-c90994a335ae&ClickID=4'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'
}

response = requests.get(url,headers=headers)

html_text = response.text
bs = BeautifulSoup(html_text, 'lxml')

# 获取房源列表信息,通过css选择器来
lis = bs.select('li.house-cell')

# 获取每个li下的信息
for li in lis:
    title = li.select('h2 a')[0].stripped_strings  # stripped_strings获取某个标签下的子孙非标签字符串,会去掉空白字符。返回来的是个生成器
    room = li.select('div.des p')[0].stripped_strings
    money = li.select('.money b')[0].string  # 获取某个标签下的非标签字符串。返回来的是个字符串。
    print(list(title)[0], list(room)[0], money)

出力結果:

文字化けした文字が表示され、ページにも文字化けします

右クリックして選択します查看网页源代码

このタイプのフォントは暗号化されています。一般的な解決策は、フォントファイルを見つけて、ファイル内のマッピング関係を分析することです。一般的に、フォントファイルは暗号化されたフォントにスタイルとして追加されます。そこで、htmlヘッダーで関連するスタイルを探しfont-face、CSS@font-faceヘッダー情報のフォントスタイルを見つけました。これにより、Web開発者はWebページのオンラインフォントを指定できます。

ctrl + f検索@font-face

フォントについて

FontTools操作関連

ここfontToolsでは、フォントを操作するためのライブラリであるモジュールを使用します。これは、woffやttfなどのフォントファイルをXMLファイルに変換するために使用されます。

1.pipを直接使用してインストールできます。

pip install fontTools

2.フォントファイルをロードします。

font = TTFont('58.woff')

3.xmlファイルに変換します。

font.saveXML('58.xml')

4.各ノードの名前:

font.keys()

5.GlyphOrderノード名の値を次の順序で取得します。

font.getGlyphOrder() 或  font['cmap'].tables[0].ttFont.getGlyphOrder()

6.cmapノードコードと名前の値の間のマッピングを取得します。

font.getBestCmap()

7.フォント座標情報を取得します。

font['glyf'][i].coordinates

8.座標の0または1を取得します。

font['glyf'][i].flags  **注:** 0表示弧形区域 1表示矩形

フォントの基本とXML

フォントは複数のテーブルで構成され、フォントの情報はテーブルに格納されます。1.基本フォントファイルには、次のテーブルが含まれている必要があります。

  • cmap:文字からグリフへのマッピングUnicodeと名前のマッピング関係
  • head:フォントヘッダーフォントのグローバル情報
  • hhea:水平ヘッダーは水平ヘッダーを定義します
  • hmtx:水平メトリックは水平メトリックを定義します
  • maxp:最大プロファイルはフォントにメモリを割り当てるために使用されます
  • name:ネーミングテーブルは、フォント名、スタイル名、著作権表示などを定義します。
  • glyf:グリフデータは輪郭の定義と調整の指示です
  • OS 2:OS2およびWindows固有のメトリック
  • 投稿:PostScript情報

フォントを復号化し、ローカルに保存して、次のことを確認しましょう。

import requests

from fontTools.ttLib import TTFont

import re
import base64


url = 'https://cs.58.com/chuzu/?PGTID=0d100000-0019-e310-48ff-c90994a335ae&ClickID=4'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'
}

response = requests.get(url,headers=headers)

html_text = response.text
# print(html_text)

pattern = r"base64,(.*?)'"   # 提取加密信息
result = re.findall(pattern, html_text)  # 返回列表

if result:  # 避免有的页面没有使用加密
    print(type(result), len(result))
    base64str = result[0]
    fontfile_content = base64.b64decode(base64str)  # 通过base64编码的数据进行解码,输出二进制
    with open('58.ttf', 'wb') as f:   # 生成字体文件
        f.write(fontfile_content)

    font = TTFont('58.ttf')   # 加载字体文件
    font.saveXML('58.xml')  # 转换成xml文件

else:
    print('没有内容')
    base64str = ""

生成されたフォントライブラリファイル58.ttf。

生成されたxmlファイル:

xmlファイルを分析します

xmlファイルのマッピング関係を分析してみましょう。
GlyphOrderラベルをクリックすると、IDと名前が表示されます。ここで、idは特定の番号に対応するのではなく、シリアル番号のみを表します。

グリフラベルをクリックして、名前といくつかの座標点を確認します。これらの座標点は、フォントの形状を説明するために使用されます。これらの座標点に注意を払う必要はありません。

コードと名前の対応であるcmapラベルをクリックします。

ここで、 以下に示すように、フォントファイルをhttp://fontstore.baidu.com/static/editor/index.htmlにインポートして 開きます。

Webページのソースコードでの表示は、 ここに示されているものと 少し似ていますか?実際、これが当てはまります。開始&#xと終了;を削除した後、残りの4つの16進表示番号とuniがフォントファイルのエンコーディングになります。したがって、対応するのは番号「6」です。これによるとglyph00007、これら2つの図から、glyph00001は番号0に対応し、glyph00002は番号1に対応し、以下同様に続きます... glyph00010は番号9に対応します。

コードを使用して、コードと名前の対応を取得します。

from fontTools.ttLib import TTFont

font = TTFont('58.ttf')  # 打开本地的ttf文件
font.saveXML('58.xml') # 转换为xml文件
bestcmap = font['cmap'].getBestCmap()  # 获取cmap节点code与name值映射
print(bestcmap)

出力:

{38006: 'glyph00010', 38287: 'glyph00006', 39228: 'glyph00007', 39499: 'glyph00005', 40506: 'glyph00009', 40611: 'glyph00002', 40804: 'glyph00008', 40850: 'glyph00003', 40868: 'glyph00001', 40869: 'glyph00004'}

出力は辞書であり、キーはエンコードされたint型です。これを、xmlに表示される16進数と、特定の数値とのマッピング関係に変換する必要があります。

        for key,value in bestcmap.items():
            key = hex(key)  # 10进制转16进制

            value = int(re.search(r'(\d+)', value).group()) -1  # 通过上面分析得出glyph00001对应的是数字0依次类推。
            print(key,value)

出力結果:

0x9476 6
0x958f 5
0x993c 4
0x9a4b 3
0x9e3a 7
0x9ea3 2
0x9f64 9
0x9f92 1
0x9fa4 0
0x9fa5 8

これで、ページのカスタムフォントを通常のフォントに置き換えて、解析することができます。コード全体は次のとおりです。

import requests
from bs4 import BeautifulSoup
from fontTools.ttLib import TTFont

import re
import base64
import io


def base46_str(html_text):
    pattern = r"base64,(.*?)'"  # 提取加密部分
    result = re.findall(pattern, html_text)  # 返回列表

    if result:  # 避免有的页面没有使用加密
        # print(type(result), len(result))
        base64str = result[0]
        bin_data = base64.b64decode(base64str)  # 通过base64编码的数据进行解码,输出二进制
        # # print(fontfile_content)
        # with open('58.ttf', 'wb') as f:
        #     f.write(bin_data)
        # font = TTFont('58.ttf')  # 打开本地的ttf文件
        # font.saveXML('58.xml')
        # bestcmap = font['cmap'].getBestCmap()
        # print(bestcmap)
        fonts = TTFont(io.BytesIO(bin_data))  # BytesIO实现了在内存中读写bytes,提高性能
        bestcmap = fonts['cmap'].getBestCmap()
        # print(bestcmap) # 字典

        # for key,value in bestcmap.items():
        #     key = hex(key)  # 10进制转16进制

        #     value = int(re.search(r'(\d+)', value).group()) -1
        #     print(key,value)

        # 使用字典推导式
        cmap = {hex(key).replace('0x', '&#x') + ';' : int(re.search(r'(\d+)', value).group(1)) - 1 for key, value in bestcmap.items()}
        # print(cmap)

        for k,v in cmap.items():
            html_text = html_text.replace(k, str(v))

        return html_text

    else:
        print('没有内容')
        base64str = ""
        return html_text


def parse_html(html_text):
    bs = BeautifulSoup(html_text, 'lxml')
    # 获取房源列表信息,通过css选择器来
    lis = bs.select('li.house-cell')

    # 获取每个li下的信息
    for li in lis:
        href = li.select('h2 a')[0]['href']
        title = li.select('h2 a')[0].stripped_strings  # stripped_strings获取某个标签下的子孙非标签字符串,会去掉空白字符。返回来的是个生成器
        room = li.select('div.des p')[0].stripped_strings
        money = li.select('.money')[0].get_text().replace('\n','')  # 获取某个标签下的非标签字符串。返回来的是个字符串。
        print(href, list(title)[0], list(room)[0], money)



if __name__ == '__main__':

    url = 'https://cs.58.com/chuzu/?PGTID=0d100000-0019-e310-48ff-c90994a335ae&ClickID=4'

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'
    }

    response = requests.get(url, headers=headers)

    html_text = response.text
    html_text = base46_str(html_text)
    parse_html(html_text)

出力結果:

この時点で、58のTongchengフォントがほぼ関連しています。

展開

上記は、車の家、キャッツアイの映画のような単純なフォントのアンチクライミングです。挑戦することができます。

 

おすすめ

転載: blog.csdn.net/weixin_45293202/article/details/113729806