python爬蟲與反爬、加密算法

網絡爬蟲

網絡爬蟲,是一個自動提取網頁的程序,它爲搜索引擎從萬維網上下載網頁,是搜索引擎的重要組成。
但是當網絡爬蟲被濫用後,互聯網上就出現太多同質的東西,原創得不到保護。
於是,很多網站開始反網絡爬蟲,想方設法保護自己的內容。
他們根據ip訪問頻率,瀏覽網頁速度,賬戶登錄,輸入驗證碼,flash封裝,ajax混淆,js加密,圖片等技術,來應對網絡爬蟲。
防的一方不惜成本,迫使抓的一方在考慮成本效益後放棄。
抓的一方不惜成本,防的一方在考慮用戶流失後放棄。

常見的反爬策略

知識點一: User-Agent + Referer檢測
User-Agent 是HTTP協議的中的一個字段, 其作用是描述發出HTTP請求的終端的一些信息。使得服務器能夠識別客戶使用的操作系統及版本、CPU類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。服務器通過這個字段就可以知道訪問網站的是什麼人。對於不是正常瀏覽器的用戶進行屏蔽。

解決方案: 僞裝瀏覽器的User-Agent,因爲每個瀏覽器的User-Agent不一樣,並且所有的用戶都能使用瀏覽器。所有每次請求的時候條件瀏覽器的User-Agent,就能解決UA檢測。

Referer 是header的一部分,當瀏覽器向web服務器發送請求的時候,一般會帶上Referer,告訴服務器我是從哪個頁面鏈接過來的。例如有一些圖片網站在你請求圖片的時候,就會檢測你的Referer值,如果Referer不符合,不會返回正常的圖片。

解決方案:在檢測referer的請求中,攜帶符合的referer值。

知識點二:js混淆和渲染

所謂 JavaScript 混淆,基本就是:
1.去掉一些實際沒有調用的函數。
2.將零散的變量聲明合併。
3.邏輯函數的精簡。
4.變量名的簡化。具體要看不同的壓縮工具的考慮優劣。常見的有UglifyJS、JScrambler等工具。

js渲染其實就是對HTML頁面的修改。比如有一些網頁本身沒有返回數據,數據是經過js加載之後添加到HTML當中的。當遇到這種情況的時候,我們要知道爬蟲是不會執行JavaScript操作。所以需要用其他的方法處理。

解決方案
1.通過閱讀網站js源碼,找到關鍵的代碼,並用python實現。
2.通過閱讀網站js源碼,找到關鍵的代碼,用PyV8等庫直接執行js代碼。
3.通過selenium庫直接模擬瀏覽器環境。

知識點三: IP限制頻次
WEB系統都是走http協議跟WEB容器連通的,每次請求至少會產生一次客戶端與服務器的tcp連接。對於服務端來說可以很清楚的查看到,一個ip地址在單位時間內發起的請求。當請求數超過一定的值之後,就可判斷爲非正常的用戶請求。

解決方案:
1.自行設計ip代理池,通過輪換的方式,每次請求攜帶不同的代理地址。
2.ADSL動態撥號他有個獨有的特點,每撥一次號,就獲取一個新的IP。也就是它的IP是不固定的。

知識點四:驗證碼

驗證碼(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自動區分計算機和人類的圖靈測試)的縮寫,是一種區分用戶是計算機還是人的公共全自動程序。可以防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試。
這個問題可以由計算機生成並評判,但是必須只有人類才能解答。由於計算機無法解答CAPTCHA的問題,所以回答出問題的用戶就可以被認爲是人類。

解決方案:
1.手動識別驗證碼
2.pytesseract識別簡單的驗證碼
3.對接打碼平臺

加密算法

加密算法的簡介
據記載,公元前400年,古希臘人發明了置換密碼。1881年世界上的第一個電話保密專利出現。在第二次世界大戰期間,德國軍方啓用“恩尼格瑪”密碼機,密碼學在戰爭中起着非常重要的作用。
隨着信息化和數字化社會的發展,人們對信息安全和保密的重要性認識不斷提高,於是在1997年,美國國家標準局公佈實施了“美國數據加密標準(DES)”,民間力量開始全面介入密碼學的研究和應用中,採用的加密算法有DES、RSA、SHA等。隨着對加密強度需求的不斷提高,近期又出現了AES、ECC等。
使用密碼學可以達到以下目的:
保密性:防止用戶的標識或數據被讀取。
數據完整性:防止數據被更改。
身份驗證:確保數據發自特定的一方。

加密算法介紹

加密算法分類
根據密鑰類型不同將現代密碼技術分爲兩類:對稱加密算法(祕密鑰匙加密)和非對稱加密算法(公開密鑰加密)。
對稱鑰匙加密系統是加密和解密均採用同一把祕密鑰匙,而且通信雙方都必須獲得這把鑰匙,並保持鑰匙的祕密。
非對稱密鑰加密系統採用的加密鑰匙(公鑰)和解密鑰匙(私鑰)是不同的。

對稱加密算法主要包含DES、3DES、AES等算法。
非對稱加密算法主要包含RSA、DSA、ECC等算法

常用加密算法
DES:
全稱爲Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法
入口參數有三個:Key、Data、Mode
Key爲7個字節共56位,是DES算法的工作密鑰;
Data爲8個字節64位,是要被加密或被解密的數據;
Mode爲DES的工作方式,有兩種:加密或解密

3DES(即Triple DES)是DES向AES過渡的加密算法,
使用兩個密鑰,執行三次DES算法,
加密的過程是加密-解密-加密
解密的過程是解密-加密-解密

AES:

  1. 高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),這個標準用來替代原先的DES

    AES的區塊長度固定爲128 比特,密鑰長度則可以是128,192或256比特 (16、24和32字節)
    
    大致步驟如下:
    
    1、密鑰擴展(KeyExpansion),
    
    2、初始輪(Initial Round),
    
    3、重複輪(Rounds),每一輪又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,
    
    4、最終輪(Final Round),最終輪沒有MixColumns。
    

RSA:

  1. 公鑰加密算法,一種非對稱密碼算法

    公鑰加密,私鑰解密
    
    3個參數:
    
    rsa_n, rsa_e,message
    
    rsa_n, rsa_e  用於生成公鑰
    
    message: 需要加密的消息
    

python加密庫

PyCrypto是Python中密碼學方面最有名的第三方軟件包。可惜的是,它的開發工作於2012年就已停止。其他人還在繼續發佈最新版本的PyCrypto,如果你不介意使用第三方的二進制包,仍可以取得 Python 3.5 的相應版本。比如,可以在 Github 上找到了對應 Python 3.5 的 PyCrypto 二進制包。

幸運的是,有一個該項目的分支 PyCrytodome 取代了 PyCrypto 。爲了在 Linux 上安裝它,你可以使用以下 pip 命令

pip install pycryptodome

在 Windows 系統上安裝則稍有不同

pip install pycryptodomex

位:比特,就是計算機裏面的0或1

計算機最小的存儲單位:字節(byte)

一個字節有8位(比特)

binascii庫

a2b_hex :16進制轉bytes

b2a_hex :把bytes字符串轉16進制

a2b_base64 :把ascii字符轉成二進制

b2a_base64 :把二進制轉成ascii字符

DES加密實例

from Cryptodome.Cipher import DES
import binascii

def pad(text):
    while len(text)%8 != 0: #不是8的倍數,用空格補全成8的倍數
        text += ' '
    return text
# 或者
# def pad(text):
#     text = text.encode()
#     result = len(text) %8
#     if ( result != 0): #當字節數不是8的倍數時,用\0字節補全
#         text = text + (b'\0'*(16-result))
#     return text

key = b'abcdefgh' # 密鑰,一般是8位或16位
des = DES.new(key,DES.MODE_ECB) #參數 key:密鑰 mode:模式一般是DES.MODE_ECB
text = 'hello world' #要加密的文本
encrypto_text = des.encrypt(pad(text).encode()) # 加密
print('密文',binascii.b2a_hex(encrypto_text)) #把bytes轉成16進制
# 如果不經過pad處理,會報錯 :加密的數據不對,必須是密鑰的位數的整數倍(此處是8的整數倍)
data = des.decrypt(encrypto_text).rstrip() # 會多出來一些字符串,所以要rstrip一下
print('明文',data)

>>
密文 b'c720e6acbf9f18b072b2abe014f5a6ce'
明文 b'hello world'

AES加密實例

from Cryptodome.Cipher import AES
from binascii import a2b_hex,b2a_hex

class aescrypt():
    def __init__(self,key):
        self.key = key.encode() #傳入公鑰
        self.mode = AES.MODE_ECB #選定模式
        self.aes = AES.new(self.key,self.mode) #創建一個aes對象
        #這裏的密鑰長度必須是16、24或32,目前16位的就夠用了

    def aesencrypt(self,text):
        # 用公鑰加密數據,得到密文,並轉成16進制
        # text = text.encode()
        # result = len(text) %16
        # if ( result != 0): #當字節數不是16的倍數時,用\0字節補全
        #     text = text + (b'\0'*(16-result))
        # self.encrypt_text = self.aes.encrypt(text)
        while len(text) % 16 !=0: #當需要加密的字符串長度不是16的倍數,在後面添加空格直到是16的倍數爲止
            text = text + ' '
        self.encrypt_text = self.aes.encrypt(text.encode())
        return b2a_hex(self.encrypt_text)

    def aesdecrypt(self,text):
        # 把密文轉成字符串,然後用公鑰解密,去掉多餘的空格
        self.decrypt_text = self.aes.decrypt(a2b_hex(text))
        return self.decrypt_text.decode().rstrip(' ')

if __name__ == '__main__':
    pr = aescrypt('abcdefghabcdefgh')
    en_text = pr.aesencrypt('hello world')
    print('密文:',en_text)
    de_text = pr.aesdecrypt(en_text)
    print('明文:',de_text)


>>
密文: b'10ccb47c783a235845b23d23ce68c4f2'
明文: hello world

RSA加密實例

import rsa
from binascii import a2b_hex,b2a_hex

class rsacrypt():
    def __init__(self):
        self.pub_key,self.priv_key = rsa.newkeys(1024)# 生成公鑰和私鑰

    def rsa_encrypt(self,text):
        self.encrypt_text = rsa.encrypt(text.encode(),self.pub_key)# 此時是bytes
        return b2a_hex(self.encrypt_text) # 轉成16進制密文

    def rsa_decrypt(self,text):
        self.decrypt_text = rsa.decrypt(a2b_hex(text),self.priv_key) # 把16進制密文轉成bytes,然後解密
        return self.decrypt_text.decode()

if __name__ == '__main__':
    rs = rsacrypt()
    text = "hello world"
    en_text = rs.rsa_encrypt(text)
    print('密文:',en_text)
    de_text = rs.rsa_decrypt(en_text)
    print('明文:',de_text)


>>
密文: b'0ecaa6c11e95d1f720ae22d4d80280aa3f28052072773f608bba2c80ee93dfc9cbd5d47c76134c6fec0c26398807b0836e221fdb28eb20bee2d676ebb2c37671c926e11109aa19e1f1ee9e3f2e37e5218b0452390878a4d2aa557aa278462691ff08eababbd02707d7be8e25f6e6b814a88b107f56f8afbb6b7c4d14e9352808'
明文: hello world

補充:

MD5消息摘要算法

(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致

import hashlib


s = 'hello world'
m = hashlib.md5()  # 實例
m.update(s.encode())  # 加密的數據都是bytes類型
print(m.hexdigest())  # 加密後以16進制展示

>>
5eb63bbbe01eeed093cb22bb8f5acdc3

Base64是網絡上最常見的用於傳輸8Bit字節碼的編碼方式之一,Base64就是一種基於64個可打印字符來表示二進制數據的方法。

原理

用64個字符來表示二進制數據的方法

A-Z a-z 0-9 + /

把3個字節(24位)的二進制數據進行拼接,按照6位進行分割,變成4個字節,如果二進制數據不能被3整除,餘1個字節則在末尾有1個=,餘2個字節則在末尾有2個=

轉碼過程例子:

38=46

內存1個字節佔8位

轉前: s 1 3

先轉成ascii:對應 115 49 51

2進制: 01110011 00110001 00110011

6個一組(4組) 011100110011000100110011

然後纔有後面的 011100 110011 000100 110011

然後計算機是8位8位的存數 6不夠,自動就補兩個高位0了

所有有了 高位補0

科學計算器輸入 00011100 00110011 00000100 00110011

得到 28 51 4 51

查對下照表 c z E z

以“迅雷下載”爲例: 很多下載類網站都提供“迅雷下載”的鏈接,其地址通常是加密的迅雷專用下載地址。

其實迅雷的“專用地址”也是用Base64"加密"的,其過程如下:

一、在地址的前後分別添加AA和ZZ

二、對新的字符串進行Base64編碼

另: Flashget的與迅雷類似,只不過在第一步時加的“料”不同罷了,Flashget在地址前後加的“料”是[FLASHGET]

而QQ旋風的乾脆不加料,直接就對地址進行Base64編碼了
實例

import base64

s = 'hello world'
r1 = base64.encodebytes(s.encode()) #編碼
print(r1) #每57個字節有一個\n
r2 = base64.b64encode(s.encode()) # 若不想有\n分隔,用b64encode
print(r2)

data = base64.b64decode(r1)  # 解碼
print(data)

>>
b'aGVsbG8gd29ybGQ=\n'
b'aGVsbG8gd29ybGQ='
b'hello world'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章