以下是针对爬取亚洲(或全球)各国人口数据的爬虫技术方案,结合合规性、反爬应对和数据解析策略,分步骤说明:
一、明确数据源与合规性
优先爬取「可公开获取的官方页面」:
- 联合国人口司(UNPD)
- 页面:https://population.un.org/wpp/Download/Standard/Population/ (年度估计数据,结构化表格)
- 特点:无反爬,数据权威,但需手动下载 Excel。可通过爬虫模拟点击下载(需处理表单提交)。
- Worldometer 实时数据
- 页面:https://www.worldometers.info/world-population/population-by-country/ (含亚洲各国实时估算)
- 特点:动态加载 JS 渲染,需模拟浏览器行为,反爬较严(限制 IP 频率)。
- 各国统计局公开表格
- 示例:中国国家统计局「年度数据」页面(http://data.stats.gov.cn/easyquery.htm?cn=C01),通过选择「人口」指标获取。
- 特点:页面结构稳定,适合定向爬取单一国家。
❗ 合规红线:
- 禁止爬取付费墙内容(如 Statista 付费数据)、需登录的系统(如部分国家统计局后台)。
- 遵守目标网站的
robots.txt(如 Worldometer 允许非商业爬取,但需控制频率:Crawl-delay: 10)。 - 避免爬取个人隐私数据(如具体年龄、性别分布,仅抓取宏观人口总数)。
二、技术方案与代码示例(以 Python 为例)
场景 1:爬取 UNPD 年度人口数据(静态表格)
python
import requests
from bs4 import BeautifulSoup
import pandas as pd
# 1. 访问UNPD数据下载页
url = "https://population.un.org/wpp/Download/Standard/Population/"
response = requests.get(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"})
soup = BeautifulSoup(response.text, "html.parser")
# 2. 定位最新年份的亚洲国家数据(假设2023年)
year_link = soup.find("a", string=lambda t: "2023" in t and "Asia" in t)
if year_link:
download_url = "https://population.un.org" + year_link["href"]
# 3. 下载Excel并解析(需处理XLSX格式)
excel_data = pd.read_excel(download_url, sheet_name="DATA", skiprows=18) # UN表格固定格式
asia_countries = excel_data[excel_data["Region, subregion, country or area *"] \
.str.contains("Asia|中国|印度|日本")] # 过滤亚洲国家
asia_countries.to_csv("unpd_asia_population_2023.csv", index=False)
else:
print("未找到2023年亚洲数据")
场景 2:爬取 Worldometer 实时数据(动态 JS 渲染)
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
# 1. 配置无头浏览器(规避反爬)
options = Options()
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0")
driver = webdriver.Chrome(options=options)
# 2. 访问亚洲国家列表页
driver.get("https://www.worldometers.info/world-population/asia-population/")
time.sleep(3) # 等待JS加载完成
# 3. 解析表格(XPath定位动态生成的tr)
rows = driver.find_elements("xpath", '//table[@id="example2"]/tbody/tr')
data = []
for row in rows:
country = row.find_element("xpath", './td[2]').text
population = row.find_element("xpath", './td[3]').text.replace(",", "")
data.append({"国家": country, "人口": population})
driver.quit()
pd.DataFrame(data).to_json("worldometer_asia_population.json", ensure_ascii=False)
场景 3:爬取中国统计局年度数据(POST 请求模拟)
python
import requests
import json
# 中国统计局API(需抓包获取真实请求参数)
url = "http://data.stats.gov.cn/easyquery.htm"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
params = {
"m": "QueryData",
"dbcode": "hgnd",
"rowcode": "zb",
"colcode": "sj",
"wds": json.dumps([{"wdcode": "reg", "valuecode": "150000"}]), # 亚洲地区代码(需查表)
"dfwds": json.dumps([]),
"k1": int(time.time() * 1000)
}
response = requests.post(url, headers=headers, data=params)
data = response.json()["returndata"]["datanodes"]
for node in data:
year = node["wds"][0]["valuecode"]
population = node["data"]["strdata"]
print(f"{year}年亚洲人口:{population}亿")
三、反爬策略与优化
- 伪装请求头:
- 随机 User-Agent(使用
fake-useragent库),模拟浏览器指纹。 - 加入
Referer(如从 Worldometer 首页跳转),避免直接访问数据页。
- 随机 User-Agent(使用
- 控制请求频率:
- 每请求间隔 3-5 秒(
time.sleep(random.uniform(3, 5))),或使用scrapy的AUTOTHROTTLE。 - 限制并发数(如 Selenium 单线程,避免 IP 被封)。
- 每请求间隔 3-5 秒(
- 应对验证码:
- 简单场景:使用
pytesseract识别图片验证码(需配合pillow库)。 - 复杂场景(如 Worldometer):购买第三方打码服务(如 Anti-Captcha),或放弃该数据源。
- 简单场景:使用
- 代理 IP 池:
- 免费代理:试用
free-proxy-list.net(稳定性差)。 - 付费代理:推荐 BrightData、Oxylabs(支持按流量付费,适合学术用途)。
- 免费代理:试用
四、替代方案:优先使用 API
若目标网站提供 API,优先调用而非爬虫,更稳定合规:
- 联合国 API:
- 接口:
https://data.un.org/api/sdmx/2.1/data/UN_Population,SP.POP.TOTL?format=json - 示例:获取中国 2023 年人口:python
requests.get("https://data.un.org/api/sdmx/2.1/data/UN_Population,SP.POP.TOTL.CN?startPeriod=2023&endPeriod=2023&format=json")
- 接口:
- 世界银行 API:
- 接口:
https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL?format=json&date=2023 - 响应:直接返回 JSON 格式的各国人口数据。
- 接口:
五、法律与道德风险提示
- 数据用途:仅限学术研究、个人分析,禁止商业售卖或用于盈利项目。
- 引用来源:爬取联合国、各国统计局数据时,需在报告中标注原始出处(如
数据来自UNPD 2023年估计)。 - 规避敏感数据:避免爬取涉及政治争议地区(如台湾、克什米尔)的独立人口统计,或按中国政府立场标注。
总结:爬虫的「正确姿势」
- 简单需求:优先下载官方 Excel/CSV(如 UNPD 的 WPP 数据集),无需爬虫。
- 动态更新:用 Selenium 模拟浏览器爬取 Worldometer,但控制频率(建议每日 1 次)。
- 技术进阶:结合 Scrapy 框架 + 代理池,实现分布式爬取(适合大规模数据)。
- 底线原则:不突破反爬规则,不侵犯隐私,数据仅供内部使用。
通过以上方案,可高效、合规地获取亚洲各国人口数据,同时降低法律风险。
了解亚洲各国最新人口数据,需结合官方渠道、国际组织、动态平台及数据工具,以下是具体方法及适用场景:
一、权威官方渠道(最准确,但更新周期较长)
- 各国统计局官网
- 中国:国家统计局(http://www.stats.gov.cn)发布年度统计公报及普查数据(如 2020 年七普数据)。
- 印度:人口普查局(https://censusindia.gov.in)每 10 年普查一次(最新 2021 年因疫情推迟,当前使用 2011 年数据 + 年度估计)。
- 日本:总务省统计局(https://www.stat.go.jp)每 5 年发布 “国势调查”(最新 2020 年数据,含详细年龄、性别分布)。
- 东南亚国家:如泰国统计局(https://www.nso.go.th)、越南统计局(https://www.gso.gov.vn),定期更新年度人口估计。
注:普查数据(如中国 10 年一次)最准确,但非普查年份依赖抽样估计。
- 政府工作报告或白皮书
例如:朝鲜每年在《朝鲜民主主义人民共和国国情》中公布人口数据,中东国家(如沙特、阿联酋)在年度财政报告中披露人口增长。
二、国际组织数据库(跨国比较首选,更新较及时)
- 联合国系统
- UN Data(https://data.un.org):提供 2023-2024 年各国人口估计(基于生育率、死亡率模型),含年龄结构、城乡分布。
- UNFPA(联合国人口基金):《世界人口状况报告》年度更新,重点关注亚洲人口趋势(如老龄化、移民)。
- 世界银行 WDI(https://data.worldbank.org)
收录 “总人口”“年增长率” 等指标,最新数据至 2023 年(部分国家为估计值,标注 “est.”)。 - CIA 世界概况(https://www.cia.gov/the-world-factbook)
提供各国人口实时估计(含性别比、年龄中位数),适合快速查询小国数据(如马尔代夫、文莱)。
三、动态统计平台(实时 / 近实时,适合快速参考)
- Worldometer(https://www.worldometers.info/world-population)
实时估算全球人口,细分至亚洲国家(如印度 2023 年已超中国的争议数据源于此),数据每 15 秒更新,但需注意方法论(基于出生率、死亡率模型)。 - Our World in Data(https://ourworldindata.org)
可视化展示亚洲国家人口历史趋势与预测(如日本人口老龄化曲线),数据来源标注清晰(联合国、各国统计局)。 - Statista(https://www.statista.com)
付费平台,但免费版可查看部分亚洲国家最新人口数据(如韩国 2024 年生育率、菲律宾人口年轻化趋势)。
四、数据工具与爬虫(适合技术人员,需注意合规性)
- 公开数据集
- 联合国人口司《世界人口展望》(https://population.un.org/wpp):每 2 年发布,含亚洲各国 2023-2100 年预测数据(Excel 可下载)。
- 世界银行 API:通过编程调用 “SP.POP.TOTL” 指标,获取亚洲国家最新人口数值(需基础代码能力)。
- 网络爬虫(谨慎使用)
- 爬取对象:各国统计局年报、国际组织表格(如 UN Data)、新闻稿(如中国国家统计局 “2023 年国民经济运行情况”)。
- 案例:CSDN 博客中用 Python 爬取 “亚洲国家 20 年人口数据”(需注意数据时效性,2015 年后数据需更新来源)。
注:需遵守网站 Robots 协议,避免法律风险。
五、学术与研究报告(深度分析)
- 亚洲开发银行(ADB)报告
《亚洲人口老龄化白皮书》《东南亚人口红利研究》等,含最新人口结构分析(如印尼迁都对人口分布的影响)。 - 智库出版物
布鲁金斯学会、日本国立社会保障・人口问题研究所(IPSS)的报告,提供亚洲国家人口政策与数据解读(如朝鲜粮食危机对人口的影响)。
六、注意事项:数据时效性与准确性
- 区分 “普查” 与 “估计”:普查数据(如中国 2020 年、缅甸 2014 年)精确但滞后;估计数据(如 2023 年 UN 数据)更新快但有误差。
- 战乱国家特殊处理:叙利亚、也门等因冲突数据缺失,需参考联合国难民署(UNHCR)的流离失所者统计。
- 地区争议:台湾、巴勒斯坦等人口数据需根据中国政府立场或国际共识标注(如巴勒斯坦仅含加沙 + 约旦河西岸)。
总结:按需选择
- 快速查询:Worldometer、CIA 世界概况(实时估计)。
- 深度研究:联合国 WPP、各国统计局(普查 + 年度估计)。
- 跨国比较:世界银行 WDI、ADB 报告(含经济关联数据)。
- 技术获取:爬取官方表格 + API 调用(需合规)。
通过多渠道交叉验证,可最大程度获取亚洲各国最新、可靠的人口数据。
以下是针对亚洲 / 全球人口数据的 Python 爬虫代码示例,涵盖静态页面、动态 JS 渲染、API 调用三种场景,附详细注释和合规说明:
一、联合国人口司(UNPD)年度数据(静态表格)
数据源:https://population.un.org/wpp/Download/Standard/Population/
特点:无反爬,数据权威(2023 年估计值),适合获取历史趋势
python
import requests
import pandas as pd
from datetime import datetime
# 1. 配置请求(模拟浏览器)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.137 Safari/537.36",
"Referer": "https://population.un.org/wpp/" # 从UNPD首页跳转
}
# 2. 获取最新年份的亚洲数据链接
url = "https://population.un.org/wpp/Download/Standard/Population/"
response = requests.get(url, headers=headers, timeout=10)
soup = pd.read_html(response.text)[0] # 直接解析页面表格
# 3. 筛选2023年亚洲国家数据
current_year = datetime.now().year
target_row = soup[soup["Indicator"].str.contains(f"Population, total \(Asia\), {current_year}")]
if target_row.empty:
print(f"未找到{current_year}年数据,尝试去年...")
target_row = soup[soup["Indicator"].str.contains(f"Population, total \(Asia\), {current_year-1}")]
if not target_row.empty:
download_url = target_row["Download"].values[0]
# 4. 下载并解析Excel(UN表格固定格式)
df = pd.read_excel(download_url, sheet_name="DATA", skiprows=18)
asia_countries = df[
df["Region, subregion, country or area *"].isin([
"China", "India", "Japan", "Indonesia", "Pakistan", # 亚洲主要国家
"Republic of Korea", "Thailand", "Vietnam"
])
].rename(columns={
"Region, subregion, country or area *": "国家",
f"2023": "2023年人口(百万)"
})[["国家", "2023年人口(百万)"]]
# 5. 保存结果(带时间戳防覆盖)
asia_countries.to_csv(
f"unpd_asia_pop_{current_year}.csv",
index=False,
encoding="utf-8-sig"
)
print(f"成功获取{len(asia_countries)}个亚洲国家2023年人口数据")
else:
print("未找到亚洲人口数据")
二、Worldometer 实时数据(动态 JS 渲染)
数据源:https://www.worldometers.info/world-population/asia-population/
特点:实时更新,但反爬严格(限制 IP 频率),需模拟浏览器
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pandas as pd
import time
# 1. 配置浏览器(规避反爬)
options = webdriver.ChromeOptions()
options.add_argument("--headless") # 无头模式
options.add_argument("--disable-blink-features=AutomationControlled") # 隐藏自动化特征
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/114.0")
driver = webdriver.Chrome(options=options)
driver.set_page_load_timeout(20)
try:
# 2. 访问亚洲人口页(等待表格加载)
driver.get("https://www.worldometers.info/world-population/asia-population/")
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "example2"))
)
time.sleep(2) # 确保JS执行完毕
# 3. 解析表格(跳过标题行)
rows = driver.find_elements(By.XPATH, '//table[@id="example2"]/tbody/tr[position()>1]')
data = []
for row in rows:
country = row.find_element(By.XPATH, './td[2]').text
population = row.find_element(By.XPATH, './td[3]').text.replace(",", "") # 清理千分位
growth = row.find_element(By.XPATH, './td[4]').text.strip()
# 过滤非主权国家(如关岛、北马里亚纳)
if "(" not in country and "Dependency" not in country:
data.append({
"国家": country,
"人口(2024年)": int(population),
"年增长率": growth
})
# 4. 保存结果(去重+排序)
df = pd.DataFrame(data).drop_duplicates(subset="国家")
df = df.sort_values(by="人口(2024年)", ascending=False)
df.to_excel("worldometer_asia_live.xlsx", index=False)
print(f"抓取{len(df)}条实时数据,中国排名第{df[df['国家']=='China'].index[0]+1}")
finally:
driver.quit()
三、中国国家统计局(API 模拟)
数据源:http://data.stats.gov.cn/ (需抓包获取真实 API)
特点:结构化数据,无反爬,但需正确构造请求参数
python
import requests
import json
from urllib.parse import urlencode
# 1. 构造POST请求(通过F12抓包获取参数)
url = "http://data.stats.gov.cn/easyquery.htm"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
}
params = {
"m": "QueryData",
"dbcode": "hgnd", # 年度数据
"rowcode": "zb", # 指标维度
"colcode": "sj", # 时间维度
"wds": json.dumps([ # 筛选条件:亚洲地区+人口指标
{"wdcode": "reg", "valuecode": "150000"}, # 亚洲地区代码(150000)
{"wdcode": "zb", "valuecode": "A0301"} # 人口总数指标(A0301)
]),
"dfwds": json.dumps([]),
"k1": int(time.time() * 1000) # 时间戳防缓存
}
# 2. 发送请求并解析JSON
response = requests.post(url, headers=headers, data=urlencode(params))
data = response.json()["returndata"]["datanodes"]
# 3. 提取中国历年人口数据
china_data = []
for node in data:
year = node["wds"][1]["valuecode"] # 时间维度
population = node["data"]["strdata"] # 人口数(万)
china_data.append({"年份": year, "人口(万)": int(population)})
# 4. 输出近5年数据
print("中国近5年人口数据:")
for item in sorted(china_data, key=lambda x: x["年份"], reverse=True)[:5]:
print(f"{item['年份']}年:{item['人口(万)']}万")
四、世界银行 API(合规首选)
数据源:https://api.worldbank.org/v2/ (无需爬虫,直接调用)
特点:官方 API,稳定高效,适合跨国比较
python
import requests
# 1. 构造API请求(获取2023年亚洲国家人口)
url = "https://api.worldbank.org/v2/country?region=AS&format=json&per_page=100"
response = requests.get(url)
countries = response.json()[1] # 第2层为国家列表
asia_country_codes = [c["id"] for c in countries if c["region"]["id"] == "AS"]
# 2. 批量获取人口数据(SP.POP.TOTL指标)
population_data = []
for code in asia_country_codes:
api_url = f"https://api.worldbank.org/v2/country/{code}/indicator/SP.POP.TOTL?date=2023&format=json"
res = requests.get(api_url)
if res.status_code == 200:
data = res.json()[1]
if data:
population = data[0]["value"]
population_data.append({
"国家代码": code,
"国家名称": data[0]["country"]["value"],
"2023人口": population
})
# 3. 保存为DataFrame
df = pd.DataFrame(population_data)
df.to_csv("worldbank_asia_pop_2023.csv", index=False)
print(f"通过API获取{len(df)}个亚洲国家人口数据")
五、关键反爬与优化技巧
- 伪装请求头(避免被识别为爬虫):python
from fake_useragent import UserAgent ua = UserAgent() headers = {"User-Agent": ua.random} # 每次随机UA - 控制请求频率(遵守 robots.txt):python
import time from random import uniform time.sleep(uniform(2, 5)) # 随机延迟2-5秒 - 使用代理 IP(防止 IP 被封):python
proxies = { "http": "http://user:pass@proxy.example.com:8080", "https": "https://user:pass@proxy.example.com:8080" } response = requests.get(url, proxies=proxies) - 处理动态渲染(Selenium+undetected-chromedriver):bash
pip install undetected-chromedriver
pythonimport undetected_chromedriver as uc driver = uc.Chrome() # 绕过大部分反爬检测
六、合规与道德提示
- 优先使用 API:世界银行、联合国均提供免费 API,避免法律风险。
- 标注数据源:保存数据时注明来源(如
# 数据来自UNPD 2023年估计)。 - 限制用途:仅限学术研究 / 个人分析,禁止商业使用。
- 排除敏感地区:爬取中国数据时,确保台湾、香港等地区按国内标准标注。
总结:按需选择
| 场景 | 推荐代码 | 优点 | 缺点 |
|---|---|---|---|
| 权威历史数据 | 联合国示例 | 无反爬,精度高 | 非实时,年度更新 |
| 实时动态数据 | Worldometer 示例 | 分钟级更新 | 反爬严格,需浏览器模拟 |
| 中国本地化数据 | 统计局 API 示例 | 官方口径,无延迟 | 参数需抓包获取 |
| 跨国快速对比 | 世界银行 API 示例 | 结构化数据,易分析 | 部分小国数据滞后 |
通过以上代码,可高效获取亚洲人口数据,同时遵守网络爬虫的伦理与法律边界。
在使用 Python 爬虫获取人口数据后,数据清洗和整理是保证分析质量的关键步骤。以下是结合人口数据特点的分步处理方案,附具体代码示例和常见问题解决方案:
一、数据清洗核心步骤(以联合国人口数据为例)
1. 读取原始数据(含爬虫结果)
python
import pandas as pd
import re
# 假设爬虫结果为乱序的DataFrame(含缺失值、格式混乱)
raw_data = pd.read_csv("population_raw.csv", encoding="utf-8")
print(raw_data.head())
2. 处理缺失值(人口数据敏感场景)
python
# 方法1:关键字段缺失直接删除(如国家、年份、人口)
clean_data = raw_data.dropna(subset=["country", "year", "population"])
# 方法2:填充合理默认值(适用于非关键字段,如地区分类)
clean_data["region"].fillna("未知地区", inplace=True)
# 日志记录缺失情况(便于回溯)
print(f"清洗前缺失行数:{raw_data.shape[0] - clean_data.shape[0]}")
3. 统一人口数值格式(处理「千 / 百万」单位、逗号分隔)
python
def convert_population(pop_str):
"""处理"1,234" → 1234,"456千" → 456000,"7.8百万" → 7800000"""
pop_str = str(pop_str).strip()
if "千" in pop_str:
return int(float(re.sub(r"千|,", "", pop_str)) * 1000)
elif "百万" in pop_str:
return int(float(re.sub(r"百万|,", "", pop_str)) * 1000000)
elif "," in pop_str:
return int(re.sub(",", "", pop_str))
else:
return pd.to_numeric(pop_str, errors="coerce") # 无法转换设为NaN
clean_data["population"] = clean_data["population"].apply(convert_population)
4. 标准化国家 / 地区名称(解决「中国」vs「中国大陆」等歧义)
python
# 建立标准化映射(参考ISO 3166-1标准)
country_mapping = {
"中国": "China",
"中国大陆": "China",
"香港": "Hong Kong",
"台湾": "Taiwan", # 注:需根据业务需求处理地区命名
"Korea, Republic of": "South Korea"
}
clean_data["country"] = clean_data["country"].map(country_mapping).fillna(clean_data["country"])
5. 过滤异常值(结合业务逻辑)
python
# 逻辑1:2023年人口不可能超过全球总人口(约80亿)
clean_data = clean_data[clean_data["population"] < 8_000_000_000]
# 逻辑2:删除重复记录(按国家+年份去重,保留最新数据)
clean_data = clean_data.sort_values("year", ascending=False).drop_duplicates(subset=["country"], keep="first")
二、人口数据特有清洗技巧
1. 处理时间维度(年份标准化)
python
# 处理"2023年估计" → 2023,"2020-2025" → 2023(取中间值)
clean_data["year"] = clean_data["year"].str.extract(r"(\d{4})").astype(int)
2. 处理地区层级(如亚洲细分至中亚 / 东南亚)
python
# 结合地理数据库补充层级(需额外数据源)
from pycountry_convert import country_alpha2_to_continent_code, country_name_to_alpha2
def get_region(country_name):
try:
alpha2 = country_name_to_alpha2(country_name)
continent = country_alpha2_to_continent_code(alpha2)
return "亚洲" if continent == "AS" else "其他洲" # 仅保留亚洲数据
except:
return "未知"
clean_data["asia_region"] = clean_data["country"].apply(get_region)
clean_data = clean_data[clean_data["asia_region"] == "亚洲"] # 过滤非亚洲国家
3. 处理爬虫误抓的注释(如「* 估计值」「** 临时数据」)
python
clean_data["notes"] = clean_data["notes"].str.replace(r"\*+", "", regex=True) # 去除星号注释
三、完整清洗流水线(含错误处理)
python
def clean_population_data(raw_df):
"""人口数据清洗全流程(含异常捕获)"""
try:
# 步骤1:基础清洗
cleaned = raw_df.copy().dropna(subset=["country", "population"])
# 步骤2:格式转换
cleaned["population"] = cleaned["population"].apply(convert_population)
cleaned["year"] = pd.to_numeric(cleaned["year"].str[:4], errors="coerce").astype(int)
# 步骤3:逻辑过滤
cleaned = cleaned[(cleaned["year"] >= 2000) & (cleaned["population"] > 1000)] # 合理年份+最小人口
# 步骤4:去重
cleaned = cleaned.sort_values("year", ascending=False).drop_duplicates(subset=["country"], keep="first")
return cleaned.reset_index(drop=True)
except Exception as e:
print(f"清洗失败:{str(e)}")
return pd.DataFrame()
# 执行清洗
final_data = clean_population_data(raw_data)
final_data.to_csv("asia_population_cleaned.csv", index=False)
四、常见问题解决方案
| 问题类型 | 人口数据案例 | 解决代码 |
|---|---|---|
| 单位不统一 | “1,234 千” vs “123.4 万” | 正则提取数字 × 单位转换(如示例中的 convert_population) |
| 国家名称歧义 | “Korea” 需区分南北 | 结合 ISO 代码映射(country_mapping) |
| 爬虫截断长数字 | “1,000,000…” 被截断为 “1,000” | 使用pd.read_csv(thousands=",")直接解析 |
| 时间戳混乱 | “2023Q1” “2022 年 7 月” | 统一提取年份(str.extract (r”(\d {4})”)) |
| 重复的地区层级 | “中国 – 北京” vs “北京(中国)” | 拆分国家和地区列(split (“-“, 1)) |
五、注意事项
- 保留原始数据:清洗前备份原始 CSV,避免不可逆错误
- 日志记录:记录清洗前后的数据量变化(如
print(f"清洗后剩余{len(final_data)}条")) - 人工校验:对 TOP10 人口国家手动核对(如中国、印度数据是否合理)
- 法律合规:确保清洗后的数据不包含个人隐私(联合国数据通常为聚合数据,可放心使用)
通过以上步骤,可将爬虫获取的「脏乱差」人口数据转化为适合分析的结构化数据集,为后续人口趋势分析、政策研究等提供可靠基础。
以下是针对人口数据的 Python 数据清洗代码示例,涵盖缺失值处理、格式标准化、异常值过滤、逻辑校验四大核心场景,附详细注释和典型问题解决方案:
一、基础清洗:缺失值 + 格式统一
场景:联合国人口数据(含country, year, population, region列)
问题:缺失值、人口单位混乱(千 / 百万)、年份格式不统一
python
import pandas as pd
import re
# 1. 读取原始数据(含缺失值和格式错误)
raw_data = pd.read_csv("unpd_population_raw.csv", encoding="utf-8")
print("清洗前数据量:", len(raw_data)) # 输出:清洗前数据量: 200
# 2. 处理缺失值(关键列缺失直接删除)
clean_data = raw_data.dropna(subset=["country", "year", "population"])
print("删除缺失值后:", len(clean_data)) # 输出:删除缺失值后: 185
# 3. 统一人口单位("1,234千" → 1234000,"56.7百万" → 56700000)
def convert_population(pop_str):
pop_str = str(pop_str).strip()
if "千" in pop_str:
return int(float(re.sub(r"千|,", "", pop_str)) * 1000)
elif "百万" in pop_str:
return int(float(re.sub(r"百万|,", "", pop_str)) * 1000000)
elif "," in pop_str: # 处理逗号分隔符
return int(re.sub(",", "", pop_str))
else:
return pd.to_numeric(pop_str, errors="coerce") # 无法转换设为NaN
clean_data["population"] = clean_data["population"].apply(convert_population)
clean_data = clean_data.dropna(subset=["population"]) # 二次删除无效值
print("单位转换后:", len(clean_data)) # 输出:单位转换后: 182
# 4. 标准化年份("2023年估计" → 2023,"2020-2025" → 2023)
clean_data["year"] = clean_data["year"].str.extract(r"(\d{4})").astype(int)
clean_data = clean_data[clean_data["year"] >= 2000] # 仅保留21世纪数据
print("年份标准化后:", len(clean_data)) # 输出:年份标准化后: 178
二、逻辑清洗:异常值 + 国家名称标准化
场景:Worldometer 实时数据(含country, population, growth_rate列)
问题:异常高值(如人口 1 亿但增长率 – 50%)、国家别名(如 “USA” vs “United States”)
python
# 1. 加载爬虫结果(含乱序和别名)
worldometer_data = pd.read_json("worldometer_raw.json")
# 2. 标准化国家名称(基于ISO 3166-1)
country_mapping = {
"USA": "United States",
"South Korea": "Republic of Korea",
"Congo": "Democratic Republic of the Congo",
"中国": "China",
"台湾": "Taiwan*" # 注:根据政策标注地区
}
worldometer_data["country"] = worldometer_data["country"].map(country_mapping).fillna(worldometer_data["country"])
# 3. 过滤逻辑异常(人口>0且增长率在合理范围)
clean_data = worldometer_data[
(worldometer_data["population"] > 1000) & # 最小人口1000
(worldometer_data["growth_rate"].between(-5, 5)) # 年增长率-5%~5%
]
print("逻辑过滤后:", len(clean_data)) # 输出:逻辑过滤后: 192
# 4. 处理重复记录(按国家保留最新/最大人口值)
clean_data = clean_data.sort_values("population", ascending=False).drop_duplicates(subset="country", keep="first")
三、深度清洗:地区层级 + 时间序列对齐
场景:中国分省人口数据(含province, year, population, source列)
问题:数据源混杂(统计局 vs 研究报告)、时间不连续
python
# 1. 加载多源数据(含不同统计口径)
china_data = pd.read_excel("china_province_raw.xlsx")
# 2. 统一地区层级(省级单位去重)
province_list = ["北京", "上海", "江苏", ...] # 完整省级列表
china_data = china_data[china_data["province"].isin(province_list)]
# 3. 对齐时间序列(补全缺失年份,使用线性插值)
def fill_year_gaps(df):
"""按省份填充连续年份的人口数据"""
df = df.set_index("year").reindex(range(2010, 2024)).rename_axis("year").reset_index()
df["population"] = df.groupby("province")["population"].transform(lambda x: x.interpolate(method="time"))
return df
clean_data = china_data.groupby("province", group_keys=False).apply(fill_year_gaps)
# 4. 标记数据来源(区分统计局vs估算数据)
clean_data["source_flag"] = clean_data["source"].apply(lambda x: "官方" if "统计局" in x else "估算")
四、完整清洗流水线(含错误处理)
场景:混合数据源(UN+Worldometer + 中国统计局)
代码:集成清洗逻辑,输出标准化数据集
python
def clean_population_pipeline(raw_df, source_type):
"""
人口数据清洗流水线
source_type: "un"(联合国)、"worldometer"、"china"
"""
try:
# 1. 基础清洗
cleaned = raw_df.copy().dropna(subset=["country" if source_type != "china" else "province", "population"])
# 2. 单位转换(根据数据源定制)
if source_type == "un":
cleaned["population"] = cleaned["population"] * 1000 # UN数据单位为千
elif source_type == "worldometer":
cleaned["population"] = cleaned["population"].astype(int)
# 3. 国家/地区标准化
if source_type in ["un", "worldometer"]:
cleaned["country"] = cleaned["country"].map(country_mapping).fillna(cleaned["country"])
# 4. 异常值过滤
cleaned = cleaned[
(cleaned["population"] < 1e10) & # 小于100亿(合理范围)
(cleaned["population"] > 0)
]
# 5. 时间处理
cleaned["year"] = pd.to_numeric(cleaned["year"].str[:4], errors="coerce").astype(int)
cleaned = cleaned[cleaned["year"].between(2000, 2024)]
return cleaned.reset_index(drop=True)
except Exception as e:
print(f"清洗失败({source_type}): {str(e)}")
return pd.DataFrame()
# 执行清洗
un_clean = clean_population_pipeline(un_raw, "un")
wm_clean = clean_population_pipeline(wm_raw, "worldometer")
china_clean = clean_population_pipeline(china_raw, "china")
# 合并多源数据
final_data = pd.concat([un_clean, wm_clean, china_clean], ignore_index=True)
final_data.to_csv("all_population_cleaned.csv", index=False)
五、常见问题解决方案(附代码)
| 问题类型 | 人口数据案例 | 解决代码(片段) |
|---|---|---|
| 单位混合(千 / 百万) | “1.234 千” vs “567 万” | convert_population函数(如示例 1) |
| 国家别名 | “UK” vs “United Kingdom” | country_mapping字典映射 |
| 负人口值 | 爬虫误抓 “-1,000” | cleaned = cleaned[cleaned["population"]>0] |
| 年份跳跃(如 2020→2022) | 缺失 2021 年数据 | 时间序列插值:df.interpolate(method="time") |
| 地区层级错误 | “亚洲 – 中国 – 北京” 拆分为三列 | df[["continent", "country", "region"]] = df["location"].str.split("-", expand=True) |
六、注意事项
- 保留清洗痕迹:
- 清洗前备份原始数据:
raw_data.to_csv("raw_backup.csv", index=False) - 记录清洗步骤:在代码中添加注释,说明每一步的目的
- 清洗前备份原始数据:
- 人工校验关键值:python
# 校验TOP5人口国家 print(final_data.sort_values("population", ascending=False).head(5)) - 合规处理地区:
- 台湾、香港等地区需按国内标准标注(如添加
*注释) - 避免使用争议性名称(如 “East Turkestan” 改为 “Xinjiang, China”)
- 台湾、香港等地区需按国内标准标注(如添加
- 性能优化:
- 大数据量时使用
chunksize分块清洗:pythonfor chunk in pd.read_csv("big_data.csv", chunksize=10000): cleaned_chunk = clean_population_pipeline(chunk, "un")
- 大数据量时使用
总结:清洗后的人口数据示例
| country | year | population | growth_rate | source |
|---|---|---|---|---|
| China | 2023 | 1441000000 | 0.03 | 国家统计局 |
| India | 2023 | 1428000000 | 0.68 | UN 2023 估计 |
| United States | 2023 | 334900000 | 0.59 | Worldometer |
| Indonesia | 2023 | 278300000 | 1.04 | 统计局 + 估算 |
通过以上代码,可将爬虫获取的原始数据转化为结构统一、逻辑合理、可分析的人口数据集,为后续的人口趋势预测、政策制定等提供可靠支持。
发表评论