在 Python 中将未知数据结构转换为已知数据结构

Converting an unknown data structure to a known data structure in Python

我最近使用 API 从我使用的平台中提取一些数据。

但问题是,我提取的数据不是可识别的数据结构。

这几乎是一个词典列表,还有一些额外的东西。

我需要知道如何将其转换为可识别的数据结构。我不一定需要代码来这样做,只要了解一下我需要研究的内容就已经很有帮助了。我是 Python 的新手,没有很多编码经验。

我有一个文件,每一行都有这些数据,这是文件中的示例行:

[<OrderProducts at 0x24333f0, {'price_ex_tax': '99.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 3, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '99.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 83, 'price_inc_tax': '99.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': None, 'option_set_id': 15, 'wrapping_message': '', 'weight': '3.0000', 'refund_amount': '0.0000', 'applied_discounts': [{'amount': 99, 'id': 'total-coupon'}], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '99.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '99.0000', 'total_ex_tax': '99.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'S-TIM-BAC-STD', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'University of Timbuktu Bachelor Set', 'is_bundled_product ': False, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [{'display_style': 'Pick list', 'type': 'Product list', 'product_option_id': 95, 'display_value': 'Cambridge-Style Bachelor Gown, Size L', 'id': 2, 'option_id': 19, 'value': '77', 'display_name': 'Gown size', 'name': 'Bachelor gown size', 'order_product_id': 3}, {'display_style': 'Pick list', 'type': 'Product list', 'product_option_id': 97, 'display_value': 'Bachelor and Masters Trencher, Size L', 'id': 3, 'option_id': 20, 'value': '80', 'display_name': 'Trencher size', 'name': 'Trencher size', 'order_product_id': 3}], 'base_cost_price': '0.0000'}>, <OrderProducts at 0x2433420, {'price_ex_tax': '0.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 4, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '0.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 80, 'price_inc_tax': '0.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': 3, 'option_set_id': None, 'wrapping_message': '', 'weight': '0.0000', 'refund_amount': '0.0000', 'applied_discounts': [], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '0.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '0.0000', 'total_ex_tax': '0.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'G-CAM-BAC-L', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'Cambridge-Style Bachelor Gown, Size L', 'is_bundled_product ': True, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [], 'base_cost_price': '0.0000'}>, <OrderProducts at 0x2433450, {'price_ex_tax': '0.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 5, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '0.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 87, 'price_inc_tax': '0.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': 3, 'option_set_id': None, 'wrapping_message': '', 'weight': '0.0000', 'refund_amount': '0.0000', 'applied_discounts': [], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '0.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '0.0000', 'total_ex_tax': '0.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'C-STD-B&M-L', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'Bachelor and Masters Trencher, Size L', 'is_bundled_product ': True, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [], 'base_cost_price': '0.0000'}>]

编辑:这是我得到的代码:

import ast
import re

order_item = re.compile("<OrderProducts at 0x[\da-f]+, ({.*?})>", re.I)

 with open('allOrderProducts2') as inf:
     for line in inf:
        order = [ast.literal_eval(op) for op in re.findall(order_item, line)]
        # ta-da! Now do something with the order
        f = open("test", "w", encoding='utf-8')
        f.write("\n".join(map(lambda x: str(x), order)))
        f.close()

这是对象列表的 python 字符串表示形式。表格似乎是:

[<_Classname_ at _memory_address_, _dict_>, ...]

下面将提取字典值。

import ast

def extract_dicts(data_string):
    s = data_string.strip()
    items = []
    while True:
         start = s.index("{")
         s = s[start:]
         try:
             ast.literal_eval(s)
             raise ValueError("malformed list")
         except SyntaxError as e:
             end = e.offset

         items.append(ast.literal_eval(s[:end-2]))
         s = s[end-2:]
         if s == ">]":
             break
    return items

您拥有的是一个包含三个 OrderProducts 对象的列表 ([ ... ]),这些对象在打印时将它们自己表示为字典 ({ key1: value1, key2: value2 })。


编辑: 好的,您有三个 OrderProducts 等的字符串表示

因此,首要任务是转换为实际的 Python 数据结构,如下所示:

import ast
import re

order_items = re.compile("<OrderProducts at 0x[\da-f]+, ({.*?})>", re.I).findall

with open(FILENAME) as inf:
    for line in inf:
        order = [ast.literal_eval(op) for op in order_items(line)]
        # ta-da! Now do something with the order

然后像以前一样继续:


编辑2:

清理了一下:

import re

DATA = "allOrderProducts2"
RESULT = "test"

order_items = re.compile("<OrderProducts at 0x[\da-f]+, ({.*?})>", re.I).findall

with open(DATA) as inf, open(RESULT, "w", "utf-8") as outf:
    # Instead of reading each line separately,
    # we can just parse the whole file in one gulp
    for item_str in order_items(inf.read()):
        # Also no need to convert the data
        # just to cast it back to a string again
        outf.write(item_str + "\n")

然后,当读回 RESULT 文件时,您可以将每一行传递给 ast.literal_eval 以将其转回字典。


查看 idparent_order_product_id 字段,看起来您拥有的是一个 "University of Timbuktu Bachelor Set",由 "Cambridge-Style Bachelor Gown, Size L" 和 "Bachelor and Masters Trencher, Size L" 组成包价 99.0(单位未知)且不含税。

我写了一个快速脚本来弄清楚默认的 OrderProduct 是什么样子的:

from collections import Counter
from pprint import pprint as pp

data = [
    {'price_ex_tax': '99.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 3, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '99.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 83, 'price_inc_tax': '99.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': None, 'option_set_id': 15, 'wrapping_message': '', 'weight': '3.0000', 'refund_amount': '0.0000', 'applied_discounts': [{'amount': 99, 'id': 'total-coupon'}], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '99.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '99.0000', 'total_ex_tax': '99.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'S-TIM-BAC-STD', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'University of Timbuktu Bachelor Set', 'is_bundled_product ': False, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [{'display_style': 'Pick list', 'type': 'Product list', 'product_option_id': 95, 'display_value': 'Cambridge-Style Bachelor Gown, Size L', 'id': 2, 'option_id': 19, 'value': '77', 'display_name': 'Gown size', 'name': 'Bachelor gown size', 'order_product_id': 3}, {'display_style': 'Pick list', 'type': 'Product list', 'product_option_id': 97, 'display_value': 'Bachelor and Masters Trencher, Size L', 'id': 3, 'option_id': 20, 'value': '80', 'display_name': 'Trencher size', 'name': 'Trencher size', 'order_product_id': 3}], 'base_cost_price': '0.0000'},
    {'price_ex_tax': '0.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 4, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '0.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 80, 'price_inc_tax': '0.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': 3, 'option_set_id': None, 'wrapping_message': '', 'weight': '0.0000', 'refund_amount': '0.0000', 'applied_discounts': [], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '0.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '0.0000', 'total_ex_tax': '0.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'G-CAM-BAC-L', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'Cambridge-Style Bachelor Gown, Size L', 'is_bundled_product ': True, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [], 'base_cost_price': '0.0000'}, 
    {'price_ex_tax': '0.0000', 'event_date': '', 'wrapping_name': '', 'price_tax': '0.0000', 'id': 5, 'cost_price_tax': '0.0000', 'bin_picking_number': '', 'ebay_transaction_id': '', 'wrapping_cost_ex_tax': '0.0000', 'base_total': '0.0000', 'quantity': 1, 'ebay_item_id': '', 'type': 'physical', 'product_id': 87, 'price_inc_tax': '0.0000', 'base_wrapping_cost': '0.0000', 'parent_order_product_id': 3, 'option_set_id': None, 'wrapping_message': '', 'weight': '0.0000', 'refund_amount': '0.0000', 'applied_discounts': [], 'event_name': None, 'cost_price_ex_tax': '0.0000', 'base_price': '0.0000', 'wrapping_cost_tax': '0.0000', 'total_inc_tax': '0.0000', 'total_ex_tax': '0.0000', 'quantity_shipped': 0, 'fixed_shipping_cost': '0.0000', 'total_tax': '0.0000', 'sku': 'C-STD-B&M-L', 'return_id': 0, 'wrapping_cost_inc_tax': '0.0000', 'cost_price_inc_tax': '0.0000', 'name': 'Bachelor and Masters Trencher, Size L', 'is_bundled_product ': True, 'order_id': 614534, 'configurable_fields': [], 'order_address_id': 2, 'is_refunded': False, 'product_options': [], 'base_cost_price': '0.0000'}
]

def get_defaults(lst_of_dct):
    defaults = {}
    majority = (len(data) + 1) // 2
    for key in lst_of_dct[0]:
        try:
            ctr = Counter(d[key] for d in lst_of_dct)
            value,count = ctr.most_common(1)[0]
            defaults[key] = value if count >= majority else ""
        except TypeError:
            # Counter doesn't like unhashable type ie lists
            defaults[key] = []
    return defaults

defaults = get_defaults(data)
pp(defaults)

这给出了

{'applied_discounts': [],
 'base_cost_price': '0.0000',
 'base_price': '0.0000',
 'base_total': '0.0000',
 'base_wrapping_cost': '0.0000',
 'bin_picking_number': '',
 'configurable_fields': [],
 'cost_price_ex_tax': '0.0000',
 'cost_price_inc_tax': '0.0000',
 'cost_price_tax': '0.0000',
 'ebay_item_id': '',
 'ebay_transaction_id': '',
 'event_date': '',
 'event_name': None,
 'fixed_shipping_cost': '0.0000',
 'id': '',
 'is_bundled_product ': True,
 'is_refunded': False,
 'name': '',
 'option_set_id': None,
 'order_address_id': 2,           # should be 0
 'order_id': 614534,              # should be 0
 'parent_order_product_id': 3,    # should be 0
 'price_ex_tax': '0.0000',
 'price_inc_tax': '0.0000',
 'price_tax': '0.0000',
 'product_id': '',
 'product_options': [],
 'quantity': 1,                   # should be 0
 'quantity_shipped': 0,
 'refund_amount': '0.0000',
 'return_id': 0,
 'sku': '',
 'total_ex_tax': '0.0000',
 'total_inc_tax': '0.0000',
 'total_tax': '0.0000',
 'type': 'physical',
 'weight': '0.0000',
 'wrapping_cost_ex_tax': '0.0000',
 'wrapping_cost_inc_tax': '0.0000',
 'wrapping_cost_tax': '0.0000',
 'wrapping_message': '',
 'wrapping_name': ''}

手动检查并修复一些错误的默认值后,

def strip_defaults(dct, defaults):
    return {key:value for key,value in dct.items() if value != defaults[key]}

res = [strip_defaults(d, defaults) for d in data]
pp(res)

它删除了所有默认值字段并为我们提供了一个稍微更具可读性的版本:

[{'applied_discounts': [{'amount': 99, 'id': 'total-coupon'}],
  'base_price': '99.0000',
  'base_total': '99.0000',
  'id': 3,
  'is_bundled_product ': False,
  'name': 'University of Timbuktu Bachelor Set',
  'option_set_id': 15,
  'order_address_id': 2,
  'order_id': 614534,
  'price_ex_tax': '99.0000',
  'price_inc_tax': '99.0000',
  'product_id': 83,
  'product_options': [{'display_name': 'Gown size',
                       'display_style': 'Pick list',
                       'display_value': 'Cambridge-Style Bachelor Gown, '
                                        'Size L',
                       'id': 2,
                       'name': 'Bachelor gown size',
                       'option_id': 19,
                       'order_product_id': 3,
                       'product_option_id': 95,
                       'type': 'Product list',
                       'value': '77'},
                      {'display_name': 'Trencher size',
                       'display_style': 'Pick list',
                       'display_value': 'Bachelor and Masters Trencher, '
                                        'Size L',
                       'id': 3,
                       'name': 'Trencher size',
                       'option_id': 20,
                       'order_product_id': 3,
                       'product_option_id': 97,
                       'type': 'Product list',
                       'value': '80'}],
  'quantity': 1,
  'sku': 'S-TIM-BAC-STD',
  'total_ex_tax': '99.0000',
  'total_inc_tax': '99.0000',
  'weight': '3.0000'},
 {'id': 4,
  'name': 'Cambridge-Style Bachelor Gown, Size L',
  'order_address_id': 2,
  'order_id': 614534,
  'parent_order_product_id': 3,
  'product_id': 80,
  'quantity': 1,
  'sku': 'G-CAM-BAC-L'},
 {'id': 5,
  'name': 'Bachelor and Masters Trencher, Size L',
  'order_address_id': 2,
  'order_id': 614534,
  'parent_order_product_id': 3,
  'product_id': 87,
  'quantity': 1,
  'sku': 'C-STD-B&M-L'}]

看起来像一个OrderProducts class个实例列表的列表,0x24333f0是那个实例在内存中的地址。它几乎没有用,因为它不是转换数据的数据结构,而是当前 运行 程序中现有实例的可读打印。
也许您的平台只是使用 print([OrderProducts_instance]) 打印出来,这不是为了转换数据而是为了读取。你永远不应该将它用于转换数据,因为它只用于 read 而不是用于 transform
但是,如果您仍想解析当前数据,则执行以下步骤可能是一个解决方案:

  • 转换为json格式
    • 你可以删除然后 [<OrderProducts at 0x24333f0,,只保留内容从 { 开始并以 } 结束,这可能是 json.
    • 中的字典
    • 将所有'替换为"为json格式只允许"引用数据
  • 使用pythonjson模块加载数据

虽然最好的解决方案是连接到您的平台以获取新的 api 用于拉取数据

这看起来像是 类 列表中未正确定义 strrepr 方法的稍微不稳定的输出。您似乎有三个对象,其内容看起来像字典(字典和列表),如下所示:

[<OrderProducts at 0x24333f0, {...},
 <OrderProducts at 0x2433420, {...},
 <OrderProducts at 0x2433450, {...}>
]

我猜 0x... 是生成输出时它们在内存中的位置。所以让你开始(快速而肮脏,没有退款保证)

import ast
import re
patt = patt = '<OrderProducts\sat\s0x.......,\s({.*})'
matches = re.findall(patt, data)
[ast.literal_eval(match)  for match in matches]

returns 包含您的数据的字典列表:

[{'applied_discounts': [{'amount': 99, 'id': 'total-coupon'}],
  'base_cost_price': '0.0000',
  'base_price': '99.0000',
  'base_total': '99.0000',
  'base_wrapping_cost': '0.0000',
  'bin_picking_number': '',
  'configurable_fields': [],

请注意,您还没有完成:其中很多字段看起来确实想要 floatint,但它们仍然是字符串。