10年的鐘祥網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都全網(wǎng)營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整鐘祥建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“鐘祥網(wǎng)站設(shè)計(jì)”,“鐘祥網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
本文章中所有內(nèi)容僅供學(xué)習(xí)交流,抓包內(nèi)容、敏感網(wǎng)址、數(shù)據(jù)接口均已做脫敏處理,嚴(yán)禁用于商業(yè)用途和非法用途,否則由此產(chǎn)生的一切后果均與作者無(wú)關(guān),若有侵權(quán),請(qǐng)聯(lián)系我立即刪除!
aHR0cDovL2dnenkuamNzLmdvdi5jbi93ZWJzaXRlL3RyYW5zYWN0aW9uL2luZGV4
aHR0cDovL2dnenkuamNzLmdvdi5jbi9wcm8tYXBpLWNvbnN0cnVjdGlvbi9jb25zdHJ1Y3Rpb24vYmlkZGVyL2JpZFNlY3Rpb24vbGlzdA==
通過(guò)鏈接進(jìn)入到網(wǎng)站,會(huì)發(fā)現(xiàn)先轉(zhuǎn)會(huì)圈才進(jìn)入到網(wǎng)頁(yè),這里可能就有個(gè)渲染加載的過(guò)程,打開(kāi)開(kāi)發(fā)者人員工具,刷新網(wǎng)頁(yè),往下滑會(huì)看到抓包到了數(shù)據(jù)返回的接口:aHR0cDovL2dnenkuamNzLmdvdi5jbi9wcm8tYXBpLWNvbnN0cnVjdGlvbi9jb25zdHJ1Y3Rpb24vYmlkZGVyL2JpZFNlY3Rpb24vbGlzdA==
,GET 請(qǐng)求,從 preview 響應(yīng)預(yù)覽中可以看到當(dāng)前頁(yè)面所有公告的信息:
Query String Parameters 中有些參數(shù)信息,各類型什么含義后文會(huì)詳細(xì)講解:
pageNum
: 當(dāng)前為第幾頁(yè)pageSize
: 頁(yè)面大小informationType
: 公告類型projectType
: 項(xiàng)目類型informationName
: 信息類型接下來(lái)隨便點(diǎn)擊一條公告,跳轉(zhuǎn)到一個(gè)新頁(yè)面,會(huì)發(fā)現(xiàn)網(wǎng)頁(yè)鏈接變成了這種格式:XXX/index?projectId=XXX&projectInfo=XXX
,生成了 projectId 和 projectInfo 兩個(gè)加密參數(shù),并且經(jīng)過(guò)測(cè)試,同一個(gè)公告頁(yè)面這兩個(gè)加密參數(shù)的值是固定的,接下來(lái)我們需要嘗試找到這兩個(gè)參數(shù)的加密位置。
從主頁(yè)位置 CTRL + SHIFT + F 全局搜索 projectId 參數(shù),依次對(duì)比可以發(fā)現(xiàn),projectId 和 projectInfo 兩個(gè)加密參數(shù)在 chunk-.eb5f8d30.js
中定義,這里是個(gè)三目運(yùn)算,若項(xiàng)目類型相同則執(zhí)行其后的方法,若不同則往后執(zhí)行:
上文代碼行判斷中出現(xiàn)的 ZFCG、GTGC 是什么意思呢,CTRL + SHIFT + F 全局搜索 ZBGG 參數(shù),在 chunk-043c03b8.34f6abab.js
文件中我們可以找到相應(yīng)的定義,以下即各自的含義:
在第 267 行,return t.stop() 處打下斷點(diǎn)進(jìn)行調(diào)試分析,隨便點(diǎn)擊一條公告,會(huì)發(fā)現(xiàn)斷點(diǎn)斷住,即成功定位,鼠標(biāo)懸停在 projectId 和 projectInfo 對(duì)應(yīng)的值上,可以知道以下信息:
projectId
:項(xiàng)目編號(hào)projectInfo
:信息類型知道了兩個(gè)加密參數(shù)的具體含義,接下來(lái)我們就需要找到其加密位置了,projectId 和 projectInfo 參數(shù)由 a.parameterTool.encryptJumpPage
方法執(zhí)行,encryptJumpPage 跳轉(zhuǎn)頁(yè)面加密?這不簡(jiǎn)直就是明示:
我們將鼠標(biāo)懸停在 a.parameterTool.encryptJumpPage
上,跟進(jìn)到方法生成的 js 文件 app.3275fd87.js
中去瞅瞅:
以上我們可以清晰地知道下面兩個(gè)參數(shù)的具體含義:
query
:加密數(shù)據(jù)( projectId 和 projectInfo)nextPath
:路由跳轉(zhuǎn)位置在第 2389 行打斷點(diǎn)進(jìn)行調(diào)試分析,從下圖可以知道,projectId 和 projectInfo 參數(shù)在 a 中被加密了:
進(jìn)一步跟蹤 a 的位置,往上滑可以看到第 2335 行到 2356 行是很明顯的 DES 加密:
但具體是哪個(gè)函數(shù)部分對(duì) query 中的 projectId 和 projectInfo 參數(shù)進(jìn)行了加密還不得而知,我們繼續(xù)打斷點(diǎn)調(diào)試分析,在 2341 行打斷點(diǎn)時(shí)發(fā)現(xiàn),projectId 參數(shù)對(duì)應(yīng)的值 424,projectInfo 參數(shù)對(duì)應(yīng)的值 ZBGG,都在 function c(t)
中進(jìn)行了處理,證明此處就是關(guān)鍵的加密位置:
function c(t) {
return i.a.DES.encrypt(t, o.keyHex, {
iv: o.ivHex,
mode: i.a.mode.CBC,
padding: i.a.pad.Pkcs7
}).ciphertext.toString()
}
分析這段關(guān)鍵的加密代碼:
iv
:ivHex 十六進(jìn)制初始向量mode
:采用 CBC 加密模式,其是一種循環(huán)模式,前一個(gè)分組的密文和當(dāng)前分組的明文異或操作后再加密padding
:采用 Pkcs7 填充方式,在填充時(shí)首先獲取需要填充的字節(jié)長(zhǎng)度 = 塊長(zhǎng)度 - (數(shù)據(jù)長(zhǎng)度 % 塊長(zhǎng)度), 在填充字節(jié)序列中所有字節(jié)填充為需要填充的字節(jié)長(zhǎng)度值ciphertext.toString()
:將加密后的密文,以十六進(jìn)制字符串形式返回這里直接引用 JS,使用 nodejs 里面的加密模塊 crypto-js 來(lái)進(jìn)行 DES 加密,調(diào)試過(guò)程中提示哪個(gè)函數(shù)未定義,就將其定義部分添加進(jìn)來(lái)即可,改寫后的完整 JS 代碼如下:
var CryptoJS = require('crypto-js');
o = {
keyHex: CryptoJS.enc.Utf8.parse(Object({
NODE_ENV: "production",
VUE_APP_BASE_API: "/pro-api",
VUE_APP_CONSTRUCTION_API: "/pro-api-construction",
VUE_APP_DEV_FILE_PREVIEW: "/lyjcdFileView/onlinePreview",
VUE_APP_FILE_ALL_PATH: "http://www.lyjcd.cn:8089",
VUE_APP_FILE_PREFIX: "/mygroup",
VUE_APP_LAND_API: "/pro-api-land",
VUE_APP_PREVIEW_PREFIX: "/lyjcdFileView",
VUE_APP_PROCUREMENT_API: "/pro-api-procurement",
VUE_APP_WINDOW_TITLE: "XXXXXX",
BASE_URL: "/"
}).VUE_APP_CUSTOM_KEY || ""),
ivHex: CryptoJS.enc.Utf8.parse(Object({
NODE_ENV: "production",
VUE_APP_BASE_API: "/pro-api",
VUE_APP_CONSTRUCTION_API: "/pro-api-construction",
VUE_APP_DEV_FILE_PREVIEW: "/lyjcdFileView/onlinePreview",
VUE_APP_FILE_ALL_PATH: "http://www.lyjcd.cn:8089",
VUE_APP_FILE_PREFIX: "/mygroup",
VUE_APP_LAND_API: "/pro-api-land",
VUE_APP_PREVIEW_PREFIX: "/lyjcdFileView",
VUE_APP_PROCUREMENT_API: "/pro-api-procurement",
VUE_APP_WINDOW_TITLE: "XXXXXX",
BASE_URL: "/"
}).VUE_APP_CUSTOM_IV || "")
};
function c(t) {
return CryptoJS.DES.encrypt(t, o.keyHex, {
iv: o.ivHex,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString()
}
// 測(cè)試
// console.log(c('ZBGG'))
// ff15d186c4d5fa7a
VUE_APP_WINDOW_TITLE
對(duì)應(yīng)值內(nèi)容經(jīng)過(guò)脫敏處理,經(jīng)測(cè)試,不影響結(jié)果輸出
GitHub 關(guān)注 K 哥爬蟲(chóng),持續(xù)分享爬蟲(chóng)相關(guān)代碼!歡迎 star !https://github.com/kgepachong/
以下只演示部分關(guān)鍵代碼,不能直接運(yùn)行!完整代碼倉(cāng)庫(kù)地址:https://github.com/kgepachong/crawler/
本案例代碼:https://github.com/kgepachong/crawler/tree/main/ggzy_jcs_gov_cn
# =======================
# --*-- coding: utf-8 --*--
# @Author : 微信公眾號(hào):K哥爬蟲(chóng)
# @FileName: ggzy.py
# @Software: PyCharm
# =======================
import urllib.parse
import execjs
import requests
url = '脫敏處理,完整代碼關(guān)注 https://github.com/kgepachong/crawler/'
def encrypted_project_id(id_enc):
with open('ggzy_js.js', 'r', encoding='utf-8') as f:
public_js = f.read()
project_id = execjs.compile(public_js).call('Public', id_enc)
return project_id
def encrypted_project_info(info_enc):
with open('ggzy_js.js', 'r', encoding='utf-8') as f:
public_js = f.read()
project_info = execjs.compile(public_js).call('Public', info_enc)
return project_info
def get_project_info(info_name, info_type):
index_url = '脫敏處理,完整代碼關(guān)注 https://github.com/kgepachong/crawler/'
urlparse = urllib.parse.urlparse(index_url)
project_info = urllib.parse.parse_qs(urlparse.query)['informationName'][0]
return project_info
def get_content(page, info_name, info_type):
headers = {
"Connection": "keep-alive",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"Accept": "application/json, text/plain, */*",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
"Referer": "脫敏處理,完整代碼關(guān)注 https://github.com/kgepachong/crawler/",
"Accept-Language": "zh-CN,zh;q=0.9"
}
url_param = "脫敏處理,完整代碼關(guān)注 https://github.com/kgepachong/crawler/"
params = {
"pageNum": page,
"pageSize": "20",
"releaseTime": "",
"search": "",
"informationType": info_type,
"departmentId": "",
"projectType": "SZFJ",
"informationName": info_name,
"onlyCanBidSectionFlag": "NO"
}
response = requests.get(url=url_param, headers=headers, params=params)
return response
def main():
print("脫敏處理,完整代碼關(guān)注 https://github.com/kgepachong/crawler/")
info_name = input("請(qǐng)輸入信息類型:")
info_type = input("請(qǐng)輸入公告類型:")
page = input("您想要獲取數(shù)據(jù)的頁(yè)數(shù):")
get_content(page, info_name, info_type)
response = get_content(page, info_name.upper(), info_type.upper())
num = int(page) * 20
print("總共獲取了 %d 個(gè)項(xiàng)目" % num)
for i in range(20):
title = response.json()['rows'][i]['content']
query_id = response.json()['rows'][i]['projectId']
query_info = get_project_info(info_name.upper(), info_type.upper())
project_id_enc = encrypted_project_id(str(query_id))
project_info_enc = encrypted_project_info(query_info)
project_url = '%s?projectId=%s&projectInfo=%s' % (url, project_id_enc, project_info_enc)
print("第 %d 個(gè)項(xiàng)目:" % (i+1) + "\n" + "項(xiàng)目名稱:%s 項(xiàng)目編號(hào):%d " % (title, query_id) + "\n" + "項(xiàng)目鏈接:%s" % project_url)
if __name__ == '__main__':
main()
代碼實(shí)現(xiàn)效果: