【问题标题】:Python 3 - import variable into dictionaryPython 3 - 将变量导入字典
【发布时间】:2016-01-05 08:40:17
【问题描述】:

我正在尝试将以下打印命令的输出放入字典中(没有成功),以便随后将其导出为 CSV。

如何将parseddata(下面的打印输出)输入字典?

示例输入文件:

<html>
<body>
<p>{ success:true ,results:3,rows:[{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"N‌​on-cumulative",Consolidated:"Non-Consolidated",FilingDate:"14-Aug-2015 15:39",SeqNumber:"1001577"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cu‌​mulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"30-May-2015 14:37",SeqNumber:"129901"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cum‌​ulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"17-Feb-2015 14:57",SeqNumber:"126171"}]}</p>
</body>
</html>

我的代码:

import requests
import re
from bs4 import BeautifulSoup
url = requests.get("http://. . .")
soup = BeautifulSoup(url.text, "lxml")
parseddata = soup.string.split(':[', 1)[1].lstrip(']')
print(parseddata)

print(parseddata) 的输出是:

{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"14-Aug-2015 15:39",SeqNumber:"1001577"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"30-May-2015 14:37",SeqNumber:"129901"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"17-Feb-2015 14:57",SeqNumber:"126171"}]}

【问题讨论】:

  • 但是parseddata 长什么样子??
  • yurib,我编辑了帖子以显示 parseddata 的样子。谢谢
  • @zs_python:您能否提供一个示例输入文件进行处理,以便人们可以针对它运行测试用例。
  • 上面有问题添加的示例输入文件,谢谢

标签: python python-3.x dictionary web-scraping yaml


【解决方案1】:

除了最后的大括号/括号外,这是有效的 JSON 这是有效的 YAML(我在最初的回答中犯了一个错误;可以在不引用属性的情况下声明 JavaScript 对象,但是可移植格式的 JSON 不允许这样做;YAML 可以)。

按照指令here 使用PyYAML 解析数据。手册split-ing 和lstrip 正在伤害您,并使这变得比需要的更难。只需获取text,然后用yaml 解析(这是必须单独安装的第三方模块):

import requests
import yaml
from bs4 import BeautifulSoup

url = requests.get("http://. . .")
soup = BeautifulSoup(url.text, "lxml")
# Use safe_load over load to avoid opening security holes; YAML can do
# a lot of unsafe things if the input isn't trusted, but handling JS
# object literals can be done safely with safe_load
response_object = yaml.safe_load(soup.string.strip())
data_rows = response_object['rows']

for row in data_rows:
    ... do stuff with each returned row ...

您可以在PyYAML tutorial 上阅读更多信息。

【讨论】:

  • 感谢ShadowRanger,我猜“最后的大括号/括号”是问题所在,请问我该如何摆脱它?
  • @zs_python:预料到这一点,并在您询问之前添加了一个示例。 :-)
  • 奇怪的是,原始数据是有效的json,只是将您感兴趣的对象作为只有一个属性的对象的数组属性中的唯一条目(保存一个元素数组)。你可能只是json.loads 整个事情,然后访问和分配data_as_dict = whole_thing_as_dict['name_of_singleton_key'][0] 并避免你明确的split-ing 和lstrip-ing。
  • 感谢您帮助移除流浪者 ShadowRanger。上面的例子给我一个错误: JSONDecodeError: Expecting property name included in double quotes: line 1 column 2 (char 1)
  • 我刚刚在问题中发布了示例输入文件,以便更清楚地了解我要解析的内容
【解决方案2】:

这看起来像一个键值映射,ISIN 是一个键,"INE134E01011" 是一个值。但它不是 JSON,因为键没有被引用,也不是 YAML,因为普通的标量键(即不带引号的字符串必须是 followed by colon + space (: )。

如果您将输出字符串分成几部分 ¹:

test_str = (
    '{ISIN:"INE134E01011",Ind:"-",'
    'Audited:"Un-Audited",'
    'Cumulative:"Non-cumulative",'
    'Consolidated:"Non-Consolidated",'
    'FilingDate:"14-Aug-2015 15:39",'
    'SeqNumber:"1001577"},'
    '{ISIN:"INE134E01011",'  # new mapping starts
    'Ind:"-",'
    'Audited:"Un-Audited",'
    'Cumulative:"Non-cumulative",'
    'Consolidated:"Non-Consolidated",'
    'FilingDate:"30-May-2015 14:37",'
    'SeqNumber:"129901"},'
    '{ISIN:"INE134E01011",'    # new mapping starts
    'Ind:"-",'
    'Audited:"Un-Audited",'
    'Cumulative:"Non-cumulative",'
    'Consolidated:"Non-Consolidated",'
    'FilingDate:"17-Feb-2015 14:57",'
    'SeqNumber:"126171"}]}'
)

它测试等于你的输入:

test_org = '{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"14-Aug-2015 15:39",SeqNumber:"1001577"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"30-May-2015 14:37",SeqNumber:"129901"},{ISIN:"INE134E01011",Ind:"-",Audited:"Un-Audited",Cumulative:"Non-cumulative",Consolidated:"Non-Consolidated",FilingDate:"17-Feb-2015 14:57",SeqNumber:"126171"}]}'
assert test_str == test_org

这种拆分清楚地表明实际上有 3 个映射,并且有一个尾随 ]}] 表示有一个列表,这与逗号分隔的 3 个映射一致。匹配的[ 丢失了,因为你在':[' 上分裂后,你lstrip() 它消失了。

您可以轻松地操作字符串以便 YAML 可以解析它,但结果是一个列表²:

import ruamel.yaml
test_str = '[' + test_str.replace(':"', ': "').rstrip('}')

data = ruamel.yaml.load(test_str)
print(type(data))

打印:

<class 'list'>

而且由于该列表所包含的 dicts 具有共同的键,因此您不能仅将它们组合在一起而不会丢失信息。

您可以将此列表映射到某个键(split 中有一个冒号,并且输出有一个尾随 } 表示 XML 中的指示),或者您可以使用具有唯一值的键 ( SeqNumber) 并将值提升为替换列表的字典中的键:

ddata = {}
for elem in data:
    k = elem.pop('SeqNumber')
    ddata[k] = elem

但如果您的最终目标是 CSV 文件,我认为没有理由从列表转到字典。如果您从 YAML 解析器获取输出,您可以这样做:

import csv
with open('output.csv', 'w', newline='') as fp:
    csvwriter = csv.writer(fp)
    csvwriter.writerow(data[0].keys())  # header of common dict keys
    for elem in data:
        csvwriter.writerow(elem.values())  # values

获取包含以下内容的 CSV 文件:

ISIN,Ind,Consolidated,Cumulative,Audited,FilingDate
INE134E01011,-,Non-Consolidated,Non-cumulative,Un-Audited,14-Aug-2015 15:39
INE134E01011,-,Non-Consolidated,Non-cumulative,Un-Audited,30-May-2015 14:37
INE134E01011,-,Non-Consolidated,Non-cumulative,Un-Audited,17-Feb-2015 14:57

¹ 我没有使用\ 转义换行符,而是使用括号将多行定义变成一个字符串,这样我可以更轻松地对行添加注释
² 而不是重新添加'[',你当然不应该首先删除它

【讨论】:

  • 感谢安东。那是完美的,只是为我做了准确的工作!非常感谢您为向我解释它所做的所有努力。感谢@ShadowRanger,你的努力已经增加了我的 Python 学习,也非常有帮助。你们为帮助我学习所付出的努力让这个菜鸟不知所措。谢谢你,继续!
  • @zs_python 如果这解决了您的问题,请考虑接受答案(通过单击此答案顶部旁边的标记)。这向其他人表明您的问题已经解决(他们可能不会一直阅读到您的评论),并在数据库中将其标记为这样。
  • 感谢@anthon 的牵手,已按照指导接受了答案。很快见到你们:)
猜你喜欢
  • 2016-08-10
  • 2016-01-23
  • 2019-02-19
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
  • 2016-06-18
  • 1970-01-01
相关资源
最近更新 更多