You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环遍历URL高效爬取美国各州历年人口数据的代码调试

1991-2018年美国各州人口爬虫代码修正

原代码核心逻辑疏漏

  • 变量名重复覆盖:外层年份遍历使用i作为循环变量,内层提取州名、人口节点时又重复使用i作为循环变量,直接覆盖外层年份值,导致年份匹配完全错乱
  • 年份赋值逻辑错误:通过append向合并后的州-人口表单独插入一行仅含年份的记录,会导致所有州对应行的Year字段为空,最终多出一行无州名、无人口值的无效数据
  • 结果容器初始化位置错误:用于存储各年数据的df_lists被定义在年份循环内部,每次循环都会被重置为空列表,后续遍历空列表追加数据的代码完全不会执行,最终无法留存任何抓取结果
  • 缺少反爬适配:直接发起无标识的HTTP请求很容易被站点反爬规则拦截,返回非目标页面内容
  • 冗余代码:开头定义的空DataFramedf全程未参与数据读写,属于无效代码

修正后可运行代码

import numpy as np
import requests
from bs4 import BeautifulSoup
import pandas as pd

# 生成待抓取年份列表
years = np.arange(1991, 2019).tolist()
base_url = 'https://fred.stlouisfed.org/release/tables?rid=118&eid=259194'
# 加请求头模拟浏览器访问,避免被反爬拦截
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
}

# 初始化存储所有年份数据的列表,放在循环外避免被重置
df_lists = []

for year in years:
    # 拼接对应年份的URL
    full_link = f'{base_url}&od={year}-01-01#'
    page = requests.get(full_link, headers=headers)
    soup = BeautifulSoup(page.text, 'lxml')

    # 提取州名
    states = soup.find_all('span', class_='fred-rls-elm-nm')
    states_list = [s.text for s in states]
    states_df = pd.DataFrame(states_list, columns=['State'])
    states_df['index'] = np.arange(len(states_df))

    # 提取人口值
    pops = soup.find_all('td', class_='fred-rls-elm-vl-td')
    pop_list = [p.text for p in pops]
    pop_df = pd.DataFrame(pop_list, columns=['Population'])
    # 清洗人口数据
    pop_df['Population'] = pop_df['Population'].replace('\n', "", regex=True).str.strip()
    pop_df['Population'] = pop_df['Population'].replace(',', "", regex=True)
    pop_df = pop_df.apply(pd.to_numeric, errors='ignore')
    # 每3个值取第一个,对应人口绝对值,跳过变动值、变动率
    pop_df = pop_df.iloc[::3, :]
    pop_df['Population'] = (pop_df['Population'] * 1000).astype(int)
    pop_df['index'] = np.arange(len(pop_df))

    # 合并州名和人口数据
    full_df = pd.merge(states_df, pop_df, how='left', on=['index'])
    # 给当前年份所有行统一赋值年份
    full_df['Year'] = year
    # 只保留需要的三列
    full_df = full_df[['State', 'Year', 'Population']]
    # 追加到结果列表
    df_lists.append(full_df)
    print(f"已完成{year}年数据抓取,共获取{len(full_df)}条记录")

# 合并所有年份的数据为最终DataFrame
final_df = pd.concat(df_lists, ignore_index=True)
print(final_df)

关键修正点说明

  • 把存储结果的df_lists移到年份循环外初始化,每次循环抓取完当年数据直接追加到列表,不会被重置
  • 统一循环变量命名,外层年份遍历用year作为变量名,内层DOM遍历用s/p等独立变量名,避免变量值覆盖
  • 去掉错误的单行追加年份逻辑,合并完当年的州、人口数据后,直接给整个DataFrame的Year列赋值当前年份,保证每行数据都能匹配到正确年份
  • 新增请求头配置,模拟正常浏览器访问,降低被反爬拦截的概率
  • 所有年份抓取完成后,用pd.concat一次性合并所有子DataFrame,直接得到包含State/Year/Population三列的规整数据集
  • 删除无实际作用的初始空DataFrame定义,精简冗余逻辑

内容的提问来源于stack exchange,提问作者user2813606

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 09:57:19