已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > 其他编程语言 > 讨论/求助

如何解析不规范的JSON数据?


『回复列表(45|显示机器人聊天)』

20.

@老虎会游泳,试过了,客户端打开那本书,目录一片空白……是不是哪个实习生干的。。

(/@Ta/2022-06-06 13:04//)

21.

@无名啊,所以我认为社会工程学(提交Bug报告)是最佳解决方案,正则表达式可以作为备用方案。

(/@Ta/2022-06-06 13:06//)

22.

@老虎会游泳,他们会不会想“大部分书籍的目录都能正常生成,干嘛要改?。。。方便你爬我数据是吧?”

(/@Ta/2022-06-06 13:08//)

23.

@无名啊,你为什么不假装你是想看那本书的用户呢?让自己表现得不像一个程序员,只展示错误的现象,不要展示错误细节。

(/@Ta/2022-06-06 13:09//)

24.

@老虎会游泳,有没有容错性强的json解析库?

(/@Ta/2022-06-06 13:09//)

25.

@老虎会游泳

你为什么不假装你是想看那本书的用户呢?

有点道理,可以一试

(/@Ta/2022-06-06 13:10//)

26.

@无名啊,有,该工具通常叫做PCRE,也就是“Perl兼容正则表达式”,它允许你自行编写规则,解析任意格式的数据。

https://php.net/pcre


对于 http://indexinfo.cdn.bcebos.com/BookFiles/Html/421/420161/index.html 采用

/\{"id":(\d+),"name":"?(.*?)"?,"hasContent":(\d+)\},/s

<?php
$json = file_get_contents('http://indexinfo.cdn.bcebos.com/BookFiles/Html/421/420161/index.html');

$regexp = '/\{"id":(\d+),"name":"?(.*?)"?,"hasContent":(\d+)\},/s';

preg_match_all($regexp, $json, $results, PREG_SET_ORDER);

foreach ($results as $obj) {
    echo "$obj[1], $obj[2], $obj[3]\n";
}

Screenshot_20220606_131957.jpg

(/@Ta/2022-06-06 13:20//)

27.

@老虎会游泳,卷和章节的所属关系信息也能完好保留嘛?还是解析出[{章节1, 章节2, ...}]

(/@Ta/2022-06-06 13:18//)

28.

@无名啊,你可以先解析出卷(/\[(.*?)\]/),然后再解析出章节(/\{"xxx":"(.*)",……\}/)啊。

(/@Ta/2022-06-06 13:22//)

29.

@无名啊,JSON是嵌套格式,所以也要使用嵌套解析。先匹配最外层,然后逐层深入。

(/@Ta/2022-06-06 13:23//)

30.

@无名啊,我给你写个嵌套匹配的例子,你等等。

(/@Ta/2022-06-06 13:26//)

31.

@老虎会游泳,总觉得这样干,通用性不强,这网站的其他页面也要对应写规则。。

希望是能实现如下函数功能:

  • 输入:畸形json
  • 输出:尽最大可能矫正后的、一定能正常解析的json

我在考虑能否用正则逐json元素进行矫正

(/@Ta/2022-06-06 13:27//)

32.

@无名啊

<?php
$json = file_get_contents('http://indexinfo.cdn.bcebos.com/BookFiles/Html/545/544490/index.html');

$卷表达式 = '/\{"name":"?(.*?)"?,"list":\[(.*?)\]\},/s';

$章表达式 = '/\{"id":(\d+),"name":"?(.*?)"?,"hasContent":(\d+)\},/s';

preg_match_all($卷表达式, $json, $卷组, PREG_SET_ORDER);

foreach ($卷组 as $卷) {
    echo "卷: $卷[1]\n";
    preg_match_all($章表达式, $卷[2], $章组, PREG_SET_ORDER);
    foreach ($章组 as $章) {
        echo "章:$章[1], $章[2], $章[3]\n";
    }
    echo "\n";
}

Screenshot_20220606_133842_com.termux.jpg

(/@Ta/2022-06-06 13:39//)

33.

@无名啊,可以使用preg_replace_callback进行修复,但依然没有通用解决方案,因为你必须扩大匹配范围,才能防止错误匹配。

/"([^"]+)":"?(.*?)"?,/ 这不安全,如果标题中出现半角逗号,就会错误匹配。

/"name":"?(.*?)"?,"hasContent":/ 这样才安全,通过锚定前面的name和后面的hasContent,才能避免匹配到错误的地方。

(/@Ta/2022-06-06 13:49//)

34.

潜在的通用解决方案,但不成功,原因待查:

<?php
$json = file_get_contents('http://indexinfo.cdn.bcebos.com/BookFiles/Html/545/544490/index.html');

function 修复函数($arr) {
    $k = $arr[1];
    $v = $arr[2];
    var_dump($arr);die;
    $v = html_entity_decode($v); // 处理&转义
    $v = preg_replace('/[\x00-\x19]/', '', $v); // 删除不允许出现的字符
    $v = trim($v); // 删除前后空白
    return json_encode($k).':'.json_encode($v);
}

function 修复字符串字段($json, $要修复的字段) {
    $修复表达式 = '/"$要修复的字段":"?(.*?)",/s';
    return preg_replace_callback($修复表达式, '修复函数', $json);
}

function 删除数组结尾逗号($json) {
    return str_replace(',]', ']', $json);
}

function 修复JSON($json, $要修复的字符串字段列表) {
    foreach ($要修复的字符串字段列表 as $要修复的字段) {
        $json = 修复字符串字段($json, $要修复的字段);
    }
    $json = 删除数组结尾逗号($json);
    return $json;
}

//var_dump(json_decode($json));

$json = 修复JSON($json, ['name']);
echo /*"\n\n\n",*/ $json/*, "\n\n\n"*/;
ini_set('display_errors', true);
error_reporting(E_ALL);
var_dump(json_decode($json));
(/@Ta/2022-06-06 16:30//)

35. @老虎会游泳,缺点是正则效率有点低,你永远不知道得到的是什么数据。
我就遇到过 他自己拼接的json,先是json里面包含换行符 还包含emoji 还包含"号 还包含html代码
(/@Ta/2022-06-06 20:17//)

36.

@老虎会游泳,初步过滤成功,但仍有很多局限性

<?php

$s = '
[
    {"name": "第五卷:"世家"子弟"},         # 这种情况出现最多,没转义『"』
    {"name": "天下大乱(上)' . chr(6) . '"},    # 出现了 ASCII 0-31 的字符,json 规范是不允许的
    {"name": "\&quot;小白兔\&quot;少爷"},  # 转义了不该转义的字符
    {"name": "番外篇~\(≧▽≦)/~"},        # 同上
    {"name": "权力的Chun\药"},             # 同上
    {"name": 饮马江湖"},                   # 搞不明白这是怎么出现的。是标题含有“退格符”(\x08),吃掉上一个『"』吗?
    {"name": style="color:Gray;"},         # ???
    {"name": ""},                          # 最后一项末尾不能有『,』
]
';

$has_comma = false;

$r = preg_replace_callback(
    '/
    (?<ws>\s+) | # 空白字符
    (?<ks>[],:[{}]) | # 符号关键字
    (?<cm>\#.*?(?:\n|$)) | # 注释
    (?<kw>(?:true|false|null)(?=[]},\s]|$)) | # 关键字
    (?<num>-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(?=[]},\s]|$)) | # 数字
    (?<str>"?(?:\\\\["\\bfnrt]?|.)*?"(?=[]},:\s]|$)) # 字符串
    /sxDSu',
    function ($m) use (&$has_comma) {

        // 跳过 空白字符 或 注释
        if (!(is_null($m['ws']) && is_null($m['cm']))) {
            return '';
        }

        // 记录逗号,右中/大括号 前不输出逗号
        elseif (!is_null($m['ks'])) {
            if ($m[0] === ',') {
                $has_comma = true;
            }
            else {
                $r = ($has_comma && $m[0] !== ']' && $m[0] !== '}' ? ',' : '') . $m[0];
                $has_comma = false;
                return $r;
            }
        }

        // 原样输出 关键字、数字(若记录了逗号,则先输出)
        elseif (!(is_null($m['kw']) && is_null($m['num']))) {
            $r = ($has_comma ? ',' : '') . $m[0];
            $has_comma = false;
            return $r;
        }

        // 都不匹配的,认为是字符串
        else {
            $is_begin_quoted = false;
            $r = ($has_comma ? ',' : '') . '"' . preg_replace_callback(
                '/(?<bg>^")|(?<esc>\\\\["\\bfnrt])|(?<bs>\\\\)|(?<qe>"$)|(?<q>")|(?<ctrl>[\x00-\x1f])/Su',
                function ($m) use (&$is_begin_quoted) {
                    if (!is_null($m['bg'])) $is_begin_quoted = true;  // 开头引号
                    elseif (!is_null($m['q'])) return '\\"';  // 转义 "
                    elseif (!is_null($m['bs'])) return '\\\\';  // 转义 \
                    elseif (!is_null($m['esc'])) return $m[0];  // 原样输出转义字符(暂不考虑 \uxxxx)
                    elseif (!is_null($m['qe'])) return $is_begin_quoted ? '' : '\\"';   // 开头若有引号,末尾的引号不认为是内容之一
                    elseif (!is_null($m['ctrl'])) return '【净化】';  // ASCII 0-31 的字符,可以考虑 \uxxxx 回去
                },
                $m[0],
                flags: PREG_UNMATCHED_AS_NULL,
            ) . '"';
            $has_comma = false;
            return $r;
        }
    },
    $s,
    flags: PREG_UNMATCHED_AS_NULL,
);

echo $r . "\n";
var_dump(json_decode($r));
[{"name":"第五卷:\"世家\"子弟"},{"name":"天下大乱(上)【净化】"},{"name":"\\&quot;小白兔\\&quot;少爷"},{"name":"番外篇~\\(≧▽≦)/~"},{"name":"权力的Chun\\药"},{"name":"饮马江湖\""},{"name":"style=\"color:Gray;\""},{"name":""}]
(/@Ta/2022-06-06 23:36//)

37.

@淡然,体会到你的心情,已经开始生气了

™的,不好好用现成json库,偏要自己手动输出,还不好好转义,不处理异常结果

(/@Ta/2022-06-06 23:51//)

38.

@无名啊,人要有感激之情,你爬对方数据没有经过对方同意吧。不告而取谓之窃,别人没有批评你,你却批评起对方,不是很奇怪吗?升米恩,斗米仇,别人就不该暴露接口给你提供这么多数据。

(/@Ta/2022-06-07 14:58//)

39.

@老虎会游泳,嗯,感谢对面大大方方暴露接口,大部分接口也没有加密

其实,对面盗版站,也是不告而取(除非真的是起点养的)

前几天也遭到全网500多大神作家和各地协会联名抵制来着

(/@Ta/2022-06-07 15:13//)

下一页 上一页 2/3页,共45楼

回复需要登录

9月26日 04:15 星期五

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1