【问题标题】:Why is my for loop overwriting my previous values in the dictionary? (python3)为什么我的 for 循环会覆盖我以前在字典中的值? (python3)
【发布时间】:2019-11-24 09:50:12
【问题描述】:

我正在为 msn money 创建一个刮板。我从网站上获取值并通过几个 for 循环运行它们以按年份对它们进行排序。当我的 for 循环完成时,所有值都是 2018 年数据集中的值。我的代码有什么问题?

from urllib.request import urlopen
from bs4 import BeautifulSoup
from lxml import etree

values = {}
values_by_year = {}
counter = 2013
dict_index = 0
temp = ''

url = "https://www.msn.com/en-us/money/stockdetails/financials/nas-googl/fi-a1u3rw?symbol=GOOGL&form=PRFIHQ"
tree = etree.HTML(urlopen(url).read())

for section in tree.xpath('//*[@id="table-content-area"]'):
    for i in range(2, 32):
        for x in     section.xpath('./div/div/div[1]/div/ul[%s]/li[1]/p/text()'
% (i)):
                if i == 6:
                    values[i] = 0
                else:
                    values[x] = 0

for x in range(2015, 2019):
    values_by_year[x] = values


for section in tree.xpath('//*[@id="table-content-area"]'):
    for i in range(2, 32):
        for y in range(1, 6):
            for value in section.xpath(
                    './div/div/div[1]/div/ul[%s]/li[%s]/p/text()' %     (i,y)):

                if y == 1:
                    temp = value
                else:
                    print("value is ", counter+y, "y is ", y)
                    values_by_year[counter+y][temp] = value



print(values_by_year[2016])
print("\n------\n")
print(values_by_year[2017])

我没有收到任何错误消息。我的预期结果是程序输出一个字典名称 values_by_year ,其中包含每年的 4 个键。每一年都包含对应于年份的值的字典。例如,2015 年的“期间结束日期”为 2015 年 12 月 31 日,而 2016 年的“期间结束日期”为 2016 年 12 月 31 日。

【问题讨论】:

  • 您好,您应该在网上搜索如何调试代码。尝试打印代码正在做什么以获得一些见解。
  • 我已经将打印语句放在各处,以查看我的代码在做什么,它似乎在做正确的事情,但我最终用一本字典覆盖了其余的值。

标签: python python-3.x beautifulsoup lxml urllib


【解决方案1】:

这是一种使用字典和 css nth-of-type 伪类的方法。 BS4 4.7.1

row_dict 是一个字典,其所有键都从所有行列 1 值中提取,即 Period End Date , Stmt Source 等。

row_dict = dict.fromkeys([h.text.strip().replace('▶\n▼\n','') for h in table.select('.first-column')][1:]) 

它通过枚举循环,以便利用计数器传递给第 n 个类型来选择与该键关联的适当行

for index, header in enumerate(row_dict, 2):
    row = [item.text.strip() for item in table.select('[class^=level]:nth-of-type(' + str(index) + ') .financials-columns')]

所以,例如:

row_dict['Period End Date']

将会

['12/31/2015', '12/31/2016', '12/31/2017', '12/31/2018']

我用每年的键生成顶级字典income_statement

income_statement = dict.fromkeys([h.text for h in table.select('.column-heading')][1:])

然后我循环这些年份,生成与每个键关联的内部字典

for i,year in enumerate(income_statement):
    income_statement[year] = dict()

然后我通过向每个内部字典添加 row_dict 的键来填充每个内部字典,即所有第 1 列的值。然后使用 enumerate 我可以通过键填充顶级字典内年字典适当的值

for k,v in row_dict.items():
         income_statement[year][k] = row_dict[k][i]

import requests
from bs4 import BeautifulSoup as bs

r = requests.get('https://www.msn.com/en-us/money/stockdetails/financials/nas-googl/fi-a1u3rw?symbol=GOOGL&form=PRFIHQ')
soup = bs(r.content, 'lxml')
table = soup.select_one('#financials-partial-view')
income_statement = dict.fromkeys([h.text for h in table.select('.column-heading')][1:])
row_dict = dict.fromkeys([h.text.strip().replace('▶\n▼\n','') for h in table.select('.first-column')][1:]) 

for index, header in enumerate(row_dict, 2):
    row = [item.text.strip() for item in table.select('[class^=level]:nth-of-type(' + str(index) + ') .financials-columns')]
    row_dict[header] = row

for i,year in enumerate(income_statement):
    income_statement[year] = dict()
    for k,v in row_dict.items():
         income_statement[year][k] = row_dict[k][i]

print(income_statement)

income_statement 结构和内容示例:

【讨论】:

    【解决方案2】:

    您的代码中的具体问题是这样的:

    for x in range(2015, 2019):
        values_by_year[x] = values
    

    这会将键 2015 到 2018 设置为引用相同的 dictvalues,而不是副本。所以当你这样做时:

    values_by_year[counter+y][temp] = value
    

    您不只是修改与counter+y 关联的dict,而是与您初始化的所有键关联的dict

    最简单的解决方法是改变:

    for x in range(2015, 2019):
        values_by_year[x] = values
    

    到:

    for x in range(2015, 2019):
        values_by_year[x] = values.copy()
    

    因此您可以按预期初始化默认值,但插入默认值dict 的(浅)副本(因为其中的值是ints,这就足够了)。

    【讨论】:

      【解决方案3】:

      我不确定你是否在追求这个。但是使用Beautifulsoup 你可以做到这一点。

      from bs4 import BeautifulSoup
      import requests
      import re
      headers={'User-Agent':'Mozilla/5.0'}
      data=requests.get('https://www.msn.com/en-us/money/stockdetails/financials/nas-googl/fi-a1u3rw?symbol=GOOGL&form=PRFIHQ',headers=headers).text
      soup=BeautifulSoup(data,'html.parser')
      dict_data={}
      table=soup.select_one('div.table-rows')
      cols=table.select('.column-heading .truncated-string')
      for col in cols[1:]:
          year=col.text
          periodenddate=col.parent.find_next('div',class_='table-data-rows').find('p',title=re.compile(year)).text
          dict_data[year]=periodenddate
      
      print(dict_data)
      

      在控制台上打印输出:

      {'2015': '12/31/2015', '2018': '12/31/2018', '2016': '12/31/2016', '2017': '12/31/2017'}
      

      【讨论】:

      • 我试图按年份将所有数据组织到嵌套字典中。例如 {2015: {start:2015, end:2016, etc}}。我最终让每个字典中的所有值都被 2018 数据集中的值覆盖。所有字典最终都具有与 2018 字典中相同的值。例如 {2015:{{'Period End Date': '12/31/2018', 'Stmt Source': 'Annual'}, 2016:{{'Period End Date': '12/31/2018', ' Stmt Source': 'Annual'}, 2017:{{'Period End Date': '12/31/2018', 'Stmt Source': 'Annual'}, 2018:{{'Period End Date': '12/ 31/2018','Stmt Source':'年度'}}。
      猜你喜欢
      • 2012-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多