Helpex - Trao đổi & giúp đỡ Đăng nhập
34

Tôi đã tải xuống dữ liệu Facebook Messenger của mình (trong tài khoản Facebook của bạn, đi tới cài đặt, sau đó đến Thông tin Facebook của bạn , sau đó Tải xuống thông tin của bạn , sau đó tạo tệp có ít nhất hộp Tin nhắn được chọn) để thực hiện một số thống kê thú vị

Tuy nhiên có một vấn đề nhỏ với mã hóa. Tôi không chắc nhưng có vẻ như Facebook đã sử dụng mã hóa không hợp lệ cho dữ liệu này. Khi tôi mở nó với soạn thảo văn bản tôi thấy một cái gì đó như thế này: Rados\u00c5\u0082aw. Khi tôi cố gắng mở nó bằng python (UTF-8), tôi nhận được RadosÅ\x82aw. Tuy nhiên tôi sẽ nhận được: Radosław.

Tập lệnh python của tôi:

text = open(os.path.join(subdir, file), encoding='utf-8')
conversations.append(json.load(text))

Tôi đã thử một vài mã hóa phổ biến nhất. Dữ liệu ví dụ là:

{
  "sender_name": "Rados\u00c5\u0082aw",
  "timestamp": 1524558089,
  "content": "No to trzeba ostatnie treningi zrobi\u00c4\u0087 xD",
  "type": "Generic"
}
34 hữu ích 5 bình luận 8.9k xem chia sẻ
47

Tôi thực sự có thể xác nhận rằng dữ liệu tải xuống Facebook được mã hóa không chính xác; một Mojibake . Dữ liệu gốc được mã hóa UTF-8 nhưng thay vào đó đã được giải mã thành tiếng Latinh -1. Tôi sẽ đảm bảo gửi một báo cáo lỗi.

Trong thời gian chờ đợi, bạn có thể sửa chữa hư hỏng theo hai cách:

  1. Giải mã dữ liệu dưới dạng JSON, sau đó mã hóa lại bất kỳ chuỗi nào dưới dạng Latin-1, giải mã lại thành UTF-8:

    >>> import json
    >>> data = r'"Rados\u00c5\u0082aw"'
    >>> json.loads(data).encode('latin1').decode('utf8')
    'Radosław'
    
  2. Tải dữ liệu dưới dạng nhị phân, thay thế tất cả các \u00hhchuỗi bằng byte mà hai chữ số hex cuối cùng đại diện, giải mã dưới dạng UTF-8 và sau đó giải mã dưới dạng JSON:

    import re
    from functools import partial
    
    fix_mojibake_escapes = partial(
         re.compile(rb'\\u00([\da-f]{2})').sub,
         lambda m: bytes.fromhex(m.group(1).decode()))
    
    with open(os.path.join(subdir, file), 'rb') as binary_data:
        repaired = fix_mojibake_escapes(binary_data.read())
    data = json.loads(repaired.decode('utf8'))
    

    Từ dữ liệu mẫu của bạn, điều này tạo ra:

    {'content': 'No to trzeba ostatnie treningi zrobić xD',
     'sender_name': 'Radosław',
     'timestamp': 1524558089,
     'type': 'Generic'}
    
47 hữu ích 2 bình luận chia sẻ
6

Giải pháp của tôi để phân tích cú pháp các đối tượng sử dụng hàm parse_hookgọi lại khi tải / tải :

import json


def parse_obj(dct):
    for key in dct:
        dct[key] = dct[key].encode('latin_1').decode('utf-8')
        pass
    return dct


data = '{"msg": "Ahoj sv\u00c4\u009bte"}'

# String
json.loads(data)  
# Out: {'msg': 'Ahoj svÄ\x9bte'}
json.loads(data, object_hook=parse_obj)  
# Out: {'msg': 'Ahoj světe'}

# File
with open('/path/to/file.json') as f:
     json.load(f, object_hook=parse_obj)
     # Out: {'msg': 'Ahoj světe'}
     pass

Cập nhật:

Giải pháp phân tích cú pháp danh sách với chuỗi không hoạt động. Vì vậy, đây là giải pháp cập nhật:

import json


def parse_obj(obj):
    for key in obj:
        if isinstance(obj[key], str):
            obj[key] = obj[key].encode('latin_1').decode('utf-8')
        elif isinstance(obj[key], list):
            obj[key] = list(map(lambda x: x if type(x) != str else x.encode('latin_1').decode('utf-8'), obj[key]))
        pass
    return obj
6 hữu ích 1 bình luận chia sẻ
4

Tôi muốn mở rộng câu trả lời của @Geekmoss 'bằng đoạn mã đệ quy sau, tôi đã sử dụng để giải mã dữ liệu facebook của mình.

import json

def parse_obj(obj):
    if isinstance(obj, str):
        return obj.encode('latin_1').decode('utf-8')

    if isinstance(obj, list):
        return [parse_obj(o) for o in obj]

    if isinstance(obj, dict):
        return {key: parse_obj(item) for key, item in obj.items()}

    return obj

decoded_data = parse_obj(json.loads(file))

Tôi nhận thấy điều này hoạt động tốt hơn, bởi vì dữ liệu facebook bạn tải xuống có thể chứa danh sách các phái, trong trường hợp đó, các phái đó sẽ được trả về 'nguyên trạng' nhờ vào chức năng nhận dạng lambda.

4 hữu ích 1 bình luận chia sẻ
3

Đây là một giải pháp dòng lệnh với jq và iconv. Đã thử nghiệm trên Linux.

cat message_1.json | jq . | iconv -f utf8 -t latin1 > m1.json

3 hữu ích 0 bình luận chia sẻ
0

Dựa trên giải pháp @Martijn Pieters, tôi đã viết một cái gì đó tương tự bằng Java.

public String getMessengerJson(Path path) throws IOException {
    String badlyEncoded = Files.readString(path, StandardCharsets.UTF_8);
    String unescaped = unescapeMessenger(badlyEncoded);
    byte[] bytes = unescaped.getBytes(StandardCharsets.ISO_8859_1);
    String fixed = new String(bytes, StandardCharsets.UTF_8);
    return fixed;
}

Phương thức unescape được lấy cảm hứng từ org.apache.commons.lang.StringEscapeUtils.

private String unescapeMessenger(String str) {
    if (str == null) {
        return null;
    }
    try {
        StringWriter writer = new StringWriter(str.length());
        unescapeMessenger(writer, str);
        return writer.toString();
    } catch (IOException ioe) {
        // this should never ever happen while writing to a StringWriter
        throw new UnhandledException(ioe);
    }
}

private void unescapeMessenger(Writer out, String str) throws IOException {
    if (out == null) {
        throw new IllegalArgumentException("The Writer must not be null");
    }
    if (str == null) {
        return;
    }
    int sz = str.length();
    StrBuilder unicode = new StrBuilder(4);
    boolean hadSlash = false;
    boolean inUnicode = false;
    for (int i = 0; i < sz; i++) {
        char ch = str.charAt(i);
        if (inUnicode) {
            unicode.append(ch);
            if (unicode.length() == 4) {
                // unicode now contains the four hex digits
                // which represents our unicode character
                try {
                    int value = Integer.parseInt(unicode.toString(), 16);
                    out.write((char) value);
                    unicode.setLength(0);
                    inUnicode = false;
                    hadSlash = false;
                } catch (NumberFormatException nfe) {
                    throw new NestableRuntimeException("Unable to parse unicode value: " + unicode, nfe);
                }
            }
            continue;
        }
        if (hadSlash) {
            hadSlash = false;
            if (ch == 'u') {
                inUnicode = true;
            } else {
                out.write("\\");
                out.write(ch);
            }
            continue;
        } else if (ch == '\\') {
            hadSlash = true;
            continue;
        }
        out.write(ch);
    }
    if (hadSlash) {
        // then we're in the weird case of a \ at the end of the
        // string, let's output it anyway.
        out.write('\\');
    }
}
0 hữu ích 3 bình luận chia sẻ
0

Các lập trình viên của Facebook dường như đã trộn lẫn các khái niệm về mã hóa Unicode và trình tự thoát , có thể là trong khi triển khai bộ tuần tự đặc biệt của riêng họ. Thông tin chi tiết khác về bảng mã Unicode không hợp lệ trong xuất dữ liệu Facebook .

Thử cái này:

import json
import io

class FacebookIO(io.FileIO):
    def read(self, size: int = -1) -> bytes:
        data: bytes = super(FacebookIO, self).readall()
        new_data: bytes = b''
        i: int = 0
        while i < len(data):
            # \u00c4\u0085
            # 0123456789ab
            if data[i:].startswith(b'\\u00'):
                u: int = 0
                new_char: bytes = b''
                while data[i+u:].startswith(b'\\u00'):
                    hex = int(bytes([data[i+u+4], data[i+u+5]]), 16)
                    new_char = b''.join([new_char, bytes([hex])])
                    u += 6

                char : str = new_char.decode('utf-8')
                new_chars: bytes = bytes(json.dumps(char).strip('"'), 'ascii')
                new_data += new_chars
                i += u
            else:
                new_data = b''.join([new_data, bytes([data[i]])])
                i += 1

        return new_data

if __name__ == '__main__':
    f = FacebookIO('data.json','rb')
    d = json.load(f)
    print(d)
0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ python python-3.x unicode mojibake , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading