JavaScript中的JSON轉(zhuǎn)為Python可讀取
創(chuàng)作背景
最近本菜雞在爬一個(gè)網(wǎng)站的時(shí)候,意外發(fā)現(xiàn)所需 JSON 數(shù)據(jù)在頁(yè)面前端,但是不易提取,寫下此篇博客以記錄解決方案。
問題再現(xiàn)
數(shù)據(jù)是通過 JS 代碼傳遞的,大致格式(僅 作舉例說明 ,方便查看層次,實(shí)際 在同一行 )如下:
function (a, b, c, d){
return {
title: a,
data: [
{
data: b
},
{
data: c
},
{
data: d
}
]
}
}('title', 2, 3, 4)
我要提取的是整個(gè) JSON 格式的數(shù)據(jù)。
如果要直接提取,可以使用
re.findall('return (\{.*?\})\}\(', content)得到結(jié)果,但如果要解析數(shù)據(jù),會(huì)報(bào)以下的錯(cuò):

意思是:鍵值對(duì)中的鍵需要被雙引號(hào)包含 。
所以我們要完成任務(wù)的話,需要解決以下問題:
- 鍵需要用
""包含。 - 需要將形式參數(shù)
a, b, c, d轉(zhuǎn)化為實(shí)際參數(shù)"title", 2, 3, 4。
解決辦法
形參與實(shí)參的對(duì)應(yīng)關(guān)系容易解決,所以先解決這個(gè)問題。
形參與實(shí)參對(duì)應(yīng)關(guān)系
可以使用以下代碼得到 形式參數(shù) 。
''.join(re.findall('function\((.*?)\)\{', content)).split(',')使用下述代碼獲得 實(shí)際參數(shù) 。
''.join(re.findall('}\((.*?)\)', content)).split(',')因?yàn)?形式參數(shù) 和 實(shí)際參數(shù) 的 個(gè)數(shù) 一樣,所以可以根據(jù) 列表索引 建立對(duì)應(yīng)關(guān)系,使用 np.c_ 可以進(jìn)行 列表橫向合并 。
代碼如下:
# 獲得實(shí)際參數(shù)Argument = ''.join(re.findall('}\((.*?)\)', content)).split(',')# 獲得形式參數(shù)Formal_parameter = ''.join(re.findall('function\((.*?)\)\{', content)).split(',')# 建立對(duì)應(yīng)關(guān)系mapping = pd.DataFrame(np.c_[Formal_parameter, Argument])# 以形參作為索引mapping.set_index(0, drop=True, inplace=True)結(jié)果如下:

格式化 JSON
要解決這個(gè)問題,我們需要先提取出 JSON 字符串,代碼如下:
string = ''.join(re.findall('return (\{.*?\})\}\(', content))結(jié)果如下:

然后,我們需要有個(gè)思路,如下:
{或,后邊,:之前的部分是 鍵 ,需要加上雙引號(hào)。:、[或{后邊,]、,之前的部分都是 值 ,需要識(shí)別且替換。
給鍵加上雙引號(hào)
因?yàn)樯婕暗?插入元素 ,Python 中只有 列表 能擔(dān)此重任,所以我們需要先 將字符串轉(zhuǎn)為列表 ,代碼如下:
string = list(''.join(re.findall('return (\{.*?\})\}\(', content)))然后,我們需要設(shè)置一個(gè)變量,當(dāng)識(shí)別出是 鍵 的時(shí)候就 置 1 ,否則為 0 。
key_flag = 0
如果我們按照上文中的規(guī)則識(shí)別出 鍵 ,就要從當(dāng)前位開始,一直到 : 之前的這部分都用 雙引號(hào) 包含。
還有些特殊情況,比如
- 嵌套字典 ,比如一個(gè)列表中值均為字典 。
- 空字典 ,
{后邊是}。
考慮到特殊情況,代碼如下:
index = 0
while True:
# 如果索引超出范圍,就跳出循環(huán)
if index >= len(string):
break
# 給鍵加雙引號(hào)
if key_flag:
# 在當(dāng)前位插入一個(gè)雙引號(hào)
string.insert(index, '"')
index += 1
# 循環(huán)讀取
while True:
# 直到出現(xiàn) ":",循環(huán)讀取的部分為 鍵
# 在 鍵 的最后添加一個(gè)雙引號(hào)
if string[index] == ':':
string.insert(index, '"')
# 重置 key_flag
key_flag = 0
# 終止循環(huán)
break
# 讀取下一位
index += 1
# 當(dāng)前字符為 "{" 或 ",",則后邊的為 鍵
if string[index] in '{,':
key_flag = 1
# 嵌套時(shí),則將索引移向下一位
if string[index+1] in '{':
index += 1
# 如果為空字典,則重置 key_value
if string[index+1] in '}':
key_flag = 0
index += 1
結(jié)果如下(因篇幅限制,代碼無(wú)法截全):

可以看到,已經(jīng)將所有的鍵用雙引號(hào)包含。
識(shí)別且替換值
這一部分還是小有難度的。
首先,和上邊一樣,我們還是需要一個(gè)變量,記錄當(dāng)前識(shí)別 值 的狀態(tài),1 代表識(shí)別出來了,0 代表沒有。
value_flag = 0
不過也是有特殊情況:
- 值已經(jīng)是字符串 ,但 字符串中有
:。 - 值是
js語(yǔ)句 ,不過其不是我們要提取的數(shù)據(jù)。
考慮到特殊情況,代碼如下:
while True:
if index >= len(string):
break
# 檢測(cè)到 值
if value_flag:
# 取出當(dāng)前字符
value = string.pop(index)
# 如果字符是數(shù)字、"[" 和 "{" 或者已經(jīng)是字符串
if value in '"1234567890[{' or is_value:
value_flag = 0
string.insert(index, value)
index += is_value
# 不符合上述情況
else:
# 循環(huán)取出 值 字符串
while True:
# 如果為 "," 或 "}",則代表已取完
if string[index] in ',}':
break
value += string.pop(index)
# 如果 值 字符串在對(duì)應(yīng)關(guān)系中,就替換
if value in mapping.index:
# 因?yàn)槭窃诋?dāng)前位不斷插入,所以要將數(shù)據(jù)反向
trans = mapping.loc[value][::-1]
# 如果不在,則直接替換成空字符串
else:
trans = '""'
# 計(jì)算索引要移動(dòng)幾位
length = len(trans)
# 插入對(duì)應(yīng)的數(shù)據(jù)
for c in trans:
string.insert(index, c)
# 索引移動(dòng)
index += length
value_flag = 0
continue
# 如果識(shí)別到 ":" 且該 ":" 不在值字符串中
if string[index] in ':' and not is_value:
value_flag = 1
# 如果是值是字符串,則設(shè)置 is_value
if string[index+1] in '"':
is_value = 1
# 識(shí)別值字符串結(jié)束,并重置 is_value
elif string[index] in '"' and is_value:
is_value = 0
index += 1
結(jié)果如下:

可以看到,轉(zhuǎn)換的還是挺成功的。
總代碼
content = '''
function(a,b,c,d){return {title:a,data:[{data:b},{data:c},{data:d}]}}("title",2,3,4)
'''
string = list(''.join(re.findall('return (\{.*?\})\}\(', content)))
is_value = 0
key_flag = 0
value_flag = 0
index = 0
while True:
if index >= len(string):
break
if key_flag:
string.insert(index, '"')
index += 1
while True:
if string[index] == ':':
string.insert(index, '"')
key_flag = 0
break
index += 1
elif value_flag:
value = string.pop(index)
if value in '"1234567890[{' or is_value:
value_flag = 0
string.insert(index, value)
index += is_value
else:
storage = index
while True:
if string[index] in ',}':
break
value += string.pop(index)
if value in mapping.index:
trans = mapping.loc[value][::-1]
else:
trans = '""'
length = len(trans)
for c in trans:
string.insert(index, c)
index += length
value_flag = 0
continue
if string[index] in '{,':
key_flag = 1
if string[index+1] in '{':
index += 1
if string[index+1] in '}':
key_flag = 0
elif string[index] in ':' and not is_value:
value_flag = 1
if string[index+1] in '"':
is_value = 1
elif string[index] in '"' and is_value:
is_value = 0
index += 1
''.join(string)
結(jié)果如下:

這時(shí)候就可以使用 json.loads 來提取數(shù)據(jù)了。
結(jié)果如下:

不足
其實(shí)上述代碼是有不足的地方的。因?yàn)檫@段 js 代碼是特殊的,是 在一行 ,且 沒有多余的空格 。
不過也是有解決辦法的:
如果 有空格 怎么辦? —> 在 提取形參 和 使用形參轉(zhuǎn)換實(shí)參 時(shí)使用 strip 去除兩側(cè)的空格即可。
代碼不在一行 怎么辦? —> 使用 .replace('\t', '').replace('\n', '') 去除 換行符 和 制表符 ,然后再進(jìn)行格式化工作。
其實(shí)如果只是 代碼不在一行 的問題的話,js.loads 會(huì)幫我們?nèi)コ?\n 之類的,直接提取重要部分。
不過如果要使用我的代碼的話,目前只支持 在一行 的 js 代碼。
還有就是有些 將鍵值對(duì)的值設(shè)置成邏輯運(yùn)算式 ,比如 a || "" 這種,也不太好提取,還得根據(jù)問題調(diào)整。
這些都只是思路,大家可以自行嘗試,如果有問題也及時(shí)提出來。
到此這篇關(guān)于JavaScript中的JSON轉(zhuǎn)為Python可讀取的文章就介紹到這了,更多相關(guān)JavaScript JSON轉(zhuǎn)為Python內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)數(shù)組深拷貝的方法分析
這篇文章主要介紹了JS實(shí)現(xiàn)數(shù)組深拷貝的方法,結(jié)合實(shí)例形式分析了javascript數(shù)組深拷貝的相關(guān)原理、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-03-03
javascript中的undefined和not defined區(qū)別示例介紹
這篇文章主要介紹了javascript中的undefined和not defined區(qū)別,需要的朋友可以參考下2014-02-02
微信小程序 頁(yè)面跳轉(zhuǎn)傳值實(shí)現(xiàn)代碼
這篇文章主要介紹了微信小程序 頁(yè)面跳轉(zhuǎn)傳值實(shí)現(xiàn)代碼的相關(guān)資料,這里分析實(shí)現(xiàn)的條件及實(shí)例代碼,需要的朋友可以參考下2017-07-07
asp.net+js 實(shí)現(xiàn)無(wú)刷新上傳解析csv文件的代碼
無(wú)刷新上傳解析csv文件的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2010-05-05
js實(shí)現(xiàn)兼容PC端和移動(dòng)端滑塊拖動(dòng)選擇數(shù)字效果
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)兼容PC端和移動(dòng)端滑塊拖動(dòng)選擇數(shù)字的效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
JavaScript封裝axios的實(shí)現(xiàn)詳解
這篇文章主要介紹了JavaScript封裝axios的實(shí)現(xiàn),Axios是一個(gè)開放源代碼庫(kù),使我們可以輕松地發(fā)出HTTP請(qǐng)求。 實(shí)際上,它是通過額外的超能力來fetch2022-09-09
JS實(shí)現(xiàn)的Object數(shù)組去重功能示例【數(shù)組成員為Object對(duì)象】
這篇文章主要介紹了JS實(shí)現(xiàn)的Object數(shù)組去重功能,可實(shí)現(xiàn)針對(duì)數(shù)組成員為Object對(duì)象的去重復(fù)功能,涉及javascript數(shù)組元素遍歷、屬性判斷等相關(guān)操作技巧,需要的朋友可以參考下2019-02-02
關(guān)于javascript sort()排序你可能忽略的一點(diǎn)理解
最近在研究Javascript發(fā)現(xiàn)了其中一些之前忽略的問題,所以想著總結(jié)分享出來,下面這篇文章主要給大家介紹了關(guān)于javascript sort()排序你可能忽略的一點(diǎn)理解,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-07-07

