尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

3天搞懂中国农村统计年鉴数据爬取完整示例

3天搞懂中国农村统计年鉴数据爬取完整示例 3天搞懂中国农村统计年鉴数据爬取完整示例 看了一堆教程还是不会写项目?别慌,这行老手告诉你,缺的不是理论,是一个能跑通、能改、能用的完整示例。今天咱们不聊虚的,直接上手处理《中国农村统计年鉴》里的真实数据。很多劳务班组负责人,手里攥着全国各省的用工、收入、土地流转数据,想做个移动端看板给老板看,或者给工人算算跨地区务工的性价比,结果卡在数据提取和清洗上。 《中国农村统计年鉴》是国家统计局每年发布的权威数据汇编,涵盖全国各省、自治区、直辖市的农业经济、农村社会、农民生活等海量指标。对于做劳务管理、跨区域用工分析的团队来说,这简直是金矿。但数据格式杂、表头多、年份跨度大,手动复制粘贴?做梦。 这篇文章,我会带你从0到1,用Python写一个可运行的爬虫+清洗脚本,专门针对《中国农村统计年鉴》的HTML表格结构。代码全部开源,逻辑清晰,注释详细,你改改参数就能跑。我们聚焦三个高频痛点:跨省转介办理差异的数据对比、考试科目与题型(这里指数据字段的枚举值校验,比如“学历”、“工种”的标准化)、电子证书查询与下载(这里指数据导出为Excel/CSV的自动化流程)。 环境准备:别在第一步就翻车 很多人第一步就错了。用错了库,或者版本冲突,跑起来一堆报错。听我的,按这个清单来,稳。 核心依赖库:requests:发送HTTP请求,获取年鉴HTML页面。 BeautifulSoup4:解析HTML,提取表格数据。这是最稳的解析器。 pandas:数据处理神器。提取表格后,直接转DataFrame,清洗、分析、导出一条龙。 openpyxl:pandas导出Excel需要它。安装命令(终端执行): pip install requests beautifulsoup4 pandas openpyxl为什么不用Selenium? 有些教程让你用Selenium模拟浏览器。对于《中国农村统计年鉴》这种静态HTML表格,Selenium是杀鸡用牛刀,而且容易因为浏览器环境不同而失败。requests + BeautifulSoup 轻量、快速、稳定。除非页面是动态JS渲染的,否则别碰Selenium。 目录结构建议: yearbook_crawler/ ├── main.py # 主脚本 ├── utils.py # 工具函数(如重试、日志) ├── output/ # 存放清洗后的数据 └── logs/ # 存放运行日志核心语法:解析表格的三把钥匙 《中国农村统计年鉴》的网页结构相对规范,但细节坑多。核心就三步:定位表格、提取表头、提取数据行。 1. 定位表格 年鉴页面通常有多个表格。你需要通过class或id定位目标表格。比如,某省农民收入数据可能在table class=stat-table里。 from bs4 import BeautifulSoup import requestsurl = https://www.stats.gov.cn/sj/ndsj/2023/indexch.htm # 示例URL,实际需替换为具体年份页面 response = requests.get(url, timeout=10) soup = BeautifulSoup(response.text, 'html.parser')# 假设目标表格的class是 'stat-table' tables = soup.find_all('table', class_='stat-table') if not tables:print(未找到目标表格,请检查class属性)exit()target_table = tables[0] # 取第一个匹配表格2. 提取表头 表头通常在thead或第一行tr中。注意,有些表格的表头是合并单元格(colspan/rowspan),需要特殊处理。 # 提取表头 header_rows = target_table.find('thead').find_all('tr') headers = [] for tr in header_rows:ths = tr.find_all(['th', 'td'])row_headers = [th.get_text(strip=True) for th in ths]headers.append(row_headers)# 简化:如果只有一行表头 if len(headers) == 1:final_headers = headers[0] else:# 多行表头需要合并,这里简单处理为下划线连接final_headers = ['_'.join(filter(None, col)) for col in zip(*headers)]3. 提取数据行 数据在tbody中。注意空值、换行符、全角字符。 data_rows = [] tbody = target_table.find('tbody') for tr in tbody.find_all('tr'):tds = tr.find_all('td')row_data = [td.get_text(strip=True) for td in tds]# 过滤空行if any(row_data):data_rows.append(row_data)# 转为DataFrame import pandas as pd df = pd.DataFrame(data_rows, columns=final_headers) print(df.head())关键细节: strip=True 很重要,能去掉空格和换行。年鉴数据里常有“--”表示缺失,后续清洗要处理。 完整代码示例:跑通一个省份数据 下面是一个完整示例,以2023年某省农民人均可支配收入数据为例。代码包含重试机制、异常处理、数据清洗和导出。 import requests from bs4 import BeautifulSoup import pandas as pd import time import logging# 配置日志 logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler(logs/crawler.log, encoding=utf-8),logging.StreamHandler()] )def fetch_page(url, retries=3):带重试的页面获取for i in range(retries):try:headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}response = requests.get(url, headers=headers, timeout=10)if response.status_code == 200:logging.info(f成功获取: {url})return response.textelse:logging.warning(f状态码异常: {response.status_code}, 重试 {i+1}/{retries})except Exception as e:logging.error(f请求失败: {e}, 重试 {i+1}/{retries})time.sleep(2) # 避免请求过快return Nonedef parse_yearbook_table(html, table_class='stat-table'):解析年鉴表格soup = BeautifulSoup(html, 'html.parser')table = soup.find('table', class_=table_class)if not table:logging.error(f未找到class为'{table_class}'的表格)return None# 提取表头thead = table.find('thead')if not thead:logging.warning(未找到thead,尝试用第一行作为表头)first_tr = table.find('tr')headers = [th.get_text(strip=True) for th in first_tr.find_all(['th', 'td'])]tbody = table.find_all('tr')[1:]else:header_rows = thead.find_all('tr')headers = []for tr in header_rows:ths = tr.find_all(['th', 'td'])headers.append([th.get_text(strip=True) for th in ths])# 合并多行表头if len(headers) == 1:final_headers = headers[0]else:final_headers = ['_'.join(filter(None, col)) for col in zip(*headers)]tbody = table.find('tbody')if not tbody:tbody = table.find_all('tr')[len(headers):]else:tbody = tbody.find_all('tr')# 提取数据data = []for tr in tbody:tds = tr.find_all('td')row = [td.get_text(strip=True) for td in tds]if len(row) == len(final_headers) and any(row):data.append(row)if not data:logging.error(未提取到数据行)return Nonedf = pd.DataFrame(data, columns=final_headers)return dfdef clean_data(df):数据清洗# 替换缺失值df.replace('--', None, inplace=True)# 删除全空行df.dropna(how='all', inplace=True)# 处理数字列:去掉逗号、空格for col in df.columns:if df[col].dtype == 'object':df[col] = df[col].str.replace(',', '', regex=False)df[col] = pd.to_numeric(df[col], errors='coerce')return dfdef main():# 示例:2023年某省农民收入数据页面url = https://www.stats.gov.cn/sj/ndsj/2023/html/C08-01ch.htm # 请替换为实际URLhtml = fetch_page(url)if not html:logging.error(页面获取失败,退出)returndf = parse_yearbook_table(html, table_class='stat-table')if df is None:logging.error(表格解析失败,退出)returndf = clean_data(df)logging.info(f成功提取 {len(df)} 行数据)# 导出Exceloutput_path = output/farmer_income_2023.xlsxdf.to_excel(output_path, index=False, engine='openpyxl')logging.info(f数据已导出: {output_path})if __name__ == '__main__':main()代码亮点:重试机制:网络不稳定时自动重试,避免单次失败。 日志记录:每一步操作都有日志,出问题好排查。 表头兼容:自动处理单行/多行表头。 数据清洗:自动转换数字类型,处理缺失值。常见报错:这些坑我替你踩过了 1. AttributeError: 'NoneType' object has no attribute 'find_all'原因:soup.find('table', class_=...) 返回None,说明没找到表格。 解决:检查class属性是否正确。用浏览器F12,查看表格的真实class。年鉴页面可能用id而非class。2. ValueError: Length of values does not match length of index原因:某行数据的列数与表头不一致。可能是合并单元格导致td数量少。 解决:在parse_yearbook_table中,增加列数校验。如果len(row) != len(final_headers),记录警告并跳过该行,或尝试填充None。3. Excel 文件被占用原因:导出Excel时,文件在Excel中打开。 解决:确保导出前关闭Excel文件,或在代码中加os.remove删除旧文件(谨慎使用)。4. 数据中出现全角数字123原因:年鉴HTML中可能混用全角/半角字符。 解决:在clean_data中增加全角转半角处理:def full_to_half(s):全角转半角r = []for ch in s:code = ord(ch)if code == 0x3000:code = 0x20elif 0xFF01 = code = 0xFF5E:code -= 0xFEE0r.append(chr(code))return ''.join(r)在clean_data中对字符串列应用此函数。 小结:从数据到决策的最后一公里 这套完整示例,能帮你把《中国农村统计年鉴》的原始HTML数据,变成干净的Excel文件。对于劳务班组负责人来说,下一步就是做数据可视化:跨省转介办理差异:对比各省“外出务工人数”、“平均收入”、“社保覆盖率”,找出用工成本洼地。 考试科目与题型(字段标准化):确保“工种”、“学历”等字段枚举值统一,便于后续分析。 电子证书查询与下载(数据导出自动化):设置定时任务,每年年鉴发布后自动爬取、清洗、推送给团队成员。技术只是工具,数据背后的洞察才是价值。别再把时间花在手动复制粘贴上了。 你更常用哪种写法?评论区交流。 是用BeautifulSoup还是Lxml?处理合并单元格有什么独门技巧?说说你的踩坑经历,帮后来人省点时间。
返回列表