为什么我的for循环会覆盖字典中的先前值? (python3)

时间:2019-07-15 16:47:21

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

我正在为msn钱创建刮板。我从站点获取了值,并通过几个循环将它们运行以按年份对它们进行排序。当我的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日。

3 个答案:

答案 0 :(得分:0)

我不确定您是否在执行此操作。不过,使用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'}

答案 1 :(得分:0)

这是使用字典和css类型的第n个伪类的方法。 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:]) 

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

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的值)来填充每个内部dict。然后,我可以使用枚举通过键填充顶级词典内部年份词典的适当值

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

Py

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)

收入表结构和内容示例:

答案 2 :(得分:0)

您的代码中的特定问题是这样的:

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

这会将键2015到2018设置为引用与dict相同的values,而不是副本。因此,当您这样做时:

values_by_year[counter+y][temp] = value

您不仅在修改与dict关联的counter+y,而且还修改了与 all 初始化键相关的那个。

最低限度的解决方法是更改​​:

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

收件人:

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

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