为什么我的 for 循环会覆盖我以前在字典中的值? (python3)
Why is my for loop overwriting my previous values in the dictionary? (python3)
我正在为 msn money 创建一个 scraper。我从站点获取值,并通过几个 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 年的 "Period End Date" 是 2015 年 12 月 31 日,2016 年是 2016 年 12 月 31 日。
我不确定你是不是想要这个或者 not.however 使用 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'}
这是一种使用字典和css nth-of-type伪class的方法。 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-of-type 到 select 与该键关联的适当行
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 列的值。使用枚举然后我可以通过键
填充顶级字典内年字典适当的值
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)
income_statement结构和内容示例:
你代码中的具体问题是这样的:
for x in range(2015, 2019):
values_by_year[x] = values
这会将键 2015 到 2018 设置为引用 values
的相同 dict
,而不是副本。所以当你这样做时:
values_by_year[counter+y][temp] = value
您不仅修改了与 counter+y
关联的 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
的(浅)副本(因为其中的值是 int
s,就足够了)。
我正在为 msn money 创建一个 scraper。我从站点获取值,并通过几个 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 年的 "Period End Date" 是 2015 年 12 月 31 日,2016 年是 2016 年 12 月 31 日。
我不确定你是不是想要这个或者 not.however 使用 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'}
这是一种使用字典和css nth-of-type伪class的方法。 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-of-type 到 select 与该键关联的适当行
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 列的值。使用枚举然后我可以通过键
填充顶级字典内年字典适当的值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)
income_statement结构和内容示例:
你代码中的具体问题是这样的:
for x in range(2015, 2019):
values_by_year[x] = values
这会将键 2015 到 2018 设置为引用 values
的相同 dict
,而不是副本。所以当你这样做时:
values_by_year[counter+y][temp] = value
您不仅修改了与 counter+y
关联的 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
的(浅)副本(因为其中的值是 int
s,就足够了)。