0


将 JavaScript 中的 JSON 转为 Python 可读取

将 JavaScript 中的 JSON 转为 Python 可读取

创作背景

最近本菜鸡在爬一个网站的时候,意外发现所需

JSON

数据在页面前端,但是不易提取,写下此篇博客以记录解决方案。
如果觉得我这篇文章写的好的话,能不能给我

点个赞

评论

收藏

一条龙(☆▽☆)。如果要点个

关注

的话也不是不可以🤗。
请各位参加一下文末的

投票

哦,如果 有什么不足之处,还 请各位大佬在评论区提出,不胜感激。

问题再现

数据是通过

JS

代码传递的,大致格式(仅 作举例说明 ,方便查看层次,实际 在同一行 )如下:

function(a, b, c, d){return{
        title: a,
        data:[{
                data: b
            },{
                data: c
            },{
                data: d
            }]}}('title',2,3,4)

我要提取的是整个

JSON

格式的数据。

如果要直接提取,可以使用

re.findall('return (\{.*?\})\}\(', content)

得到结果,但如果要解析数据,会报以下的错:
请添加图片描述

意思是:键值对中的键需要被双引号包含

所以我们要完成任务的话,需要解决以下问题:

  • 键需要用 "" 包含。
  • 需要将形式参数 a, b, c, d 转化为实际参数 "title", 2, 3, 4

解决办法

形参与实参的对应关系容易解决,所以先解决这个问题。

形参与实参对应关系

可以使用以下代码得到 形式参数

''.join(re.findall('function\((.*?)\)\{', content)).split(',')

使用下述代码获得 实际参数

''.join(re.findall('}\((.*?)\)', content)).split(',')

因为 形式参数实际参数个数 一样,所以可以根据 列表索引 建立对应关系,使用

np.c_

可以进行 列表横向合并

代码如下:

# 获得实际参数
Argument =''.join(re.findall('}\((.*?)\)', content)).split(',')# 获得形式参数
Formal_parameter =''.join(re.findall('function\((.*?)\)\{', content)).split(',')# 建立对应关系
mapping = pd.DataFrame(np.c_[Formal_parameter, Argument])# 以形参作为索引
mapping.set_index(0, drop=True, inplace=True)

结果如下:
请添加图片描述

格式化 JSON

要解决这个问题,我们需要先提取出

JSON

字符串,代码如下:

string =''.join(re.findall('return (\{.*?\})\}\(', content))

结果如下:
请添加图片描述
然后,我们需要有个思路,如下:

  1. {, 后边,: 之前的部分是 ,需要加上双引号。
  2. :[{ 后边,], 之前的部分都是 ,需要识别且替换。

给键加上双引号

因为涉及到 插入元素

Python

中只有 列表 能担此重任,所以我们需要先 将字符串转为列表 ,代码如下:

string =list(''.join(re.findall('return (\{.*?\})\}\(', content)))

然后,我们需要设置一个变量,当识别出是 的时候就

1

,否则为

0

key_flag =0

如果我们按照上文中的规则识别出 ,就要从当前位开始,一直到

:

之前的这部分都用 双引号 包含。

还有些特殊情况,比如

  • 嵌套字典 ,比如一个列表中值均为字典 。
  • 空字典{ 后边是 }

考虑到特殊情况,代码如下:

index =0whileTrue:# 如果索引超出范围,就跳出循环if index >=len(string):break# 给键加双引号if key_flag:# 在当前位插入一个双引号
        string.insert(index,'"')
        index +=1# 循环读取 whileTrue:# 直到出现 ":",循环读取的部分为 键# 在 键 的最后添加一个双引号if string[index]==':':
                string.insert(index,'"')# 重置 key_flag
                key_flag =0# 终止循环break# 读取下一位
            index +=1# 当前字符为 "{" 或 ",",则后边的为 键if string[index]in'{,':
        key_flag =1# 嵌套时,则将索引移向下一位if string[index+1]in'{':
            index +=1# 如果为空字典,则重置 key_valueif string[index+1]in'}':
            key_flag =0
    
    index +=1

结果如下(因篇幅限制,代码无法截全):
请添加图片描述
可以看到,已经将所有的键用双引号包含。

识别且替换值

这一部分还是小有难度的。

首先,和上边一样,我们还是需要一个变量,记录当前识别 的状态,

1

代表识别出来了,

0

代表没有。

value_flag =0

不过也是有特殊情况:

  • 值已经是字符串 ,但 字符串中有:
  • 值是js语句 ,不过其不是我们要提取的数据。

考虑到特殊情况,代码如下:

whileTrue:if index >=len(string):break# 检测到 值if value_flag:# 取出当前字符
        value = string.pop(index)# 如果字符是数字、"[" 和 "{" 或者已经是字符串if value in'"1234567890[{'or is_value:
            value_flag =0
            string.insert(index, value)
            index += is_value
        # 不符合上述情况else:# 循环取出 值 字符串whileTrue:# 如果为 "," 或 "}",则代表已取完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 =0continue# 如果识别到 ":" 且该 ":" 不在值字符串中if string[index]in':'andnot is_value:
        value_flag =1# 如果是值是字符串,则设置 is_valueif string[index+1]in'"':
            is_value =1# 识别值字符串结束,并重置 is_valueelif string[index]in'"'and is_value:
        is_value =0

    index +=1

结果如下:
请添加图片描述
可以看到,转换的还是挺成功的。

总代码

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 =0whileTrue:if index >=len(string):breakif key_flag:
        
        string.insert(index,'"')
        index +=1whileTrue:if string[index]==':':
                string.insert(index,'"')
                key_flag =0break
            index +=1elif 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
            
            whileTrue: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 =0continueif string[index]in'{,':
        key_flag =1if string[index+1]in'{':
            index +=1if string[index+1]in'}':
            key_flag =0elif string[index]in':'andnot is_value:
        value_flag =1if string[index+1]in'"':
            is_value =1elif string[index]in'"'and is_value:
        is_value =0

    index +=1''.join(string)

结果如下:
请添加图片描述
这时候就可以使用

json.loads

来提取数据了。

结果如下:
请添加图片描述

不足

其实上述代码是有不足的地方的。因为这段

js

代码是特殊的,是 在一行 ,且 没有多余的空格

不过也是有解决办法的:

如果 有空格 怎么办? —> 在 提取形参使用形参转换实参 时使用

strip

去除两侧的空格即可。

代码不在一行 怎么办? —> 使用

.replace('\t', '').replace('\n', '')

去除 换行符制表符 ,然后再进行格式化工作。

其实如果只是 代码不在一行 的问题的话,

js.loads

会帮我们去除

\n

之类的,直接提取重要部分。

不过如果要使用我的代码的话,目前只支持 在一行

js

代码。

还有就是有些 将键值对的值设置成逻辑运算式 ,比如

a || ""

这种,也不太好提取,还得根据问题调整。

这些都只是思路,大家可以自行尝试,如果有问题也及时提出来。




结尾

有想要一起学习

python

的小伙伴可以

私信我

进群哦。

以上就是我要分享的内容,因为 学识尚浅会有不足,还 请各位大佬指正
有什么问题也可在评论区留言。
在这里插入图片描述

标签: javascript json python

本文转载自: https://blog.csdn.net/lhys666/article/details/122673194
版权归原作者 lhys666 所有, 如有侵权,请联系我们删除。

“将 JavaScript 中的 JSON 转为 Python 可读取”的评论:

还没有评论