如何用Python爬取维基百科赫尔辛基邻域列表并转换为指定结构DataFrame
核心问题定位
你当前的抓取逻辑存在两个会导致数据混乱的问题:
- 直接切片提取全部
<li>元素,会同时抓取嵌套在主邻域节点内部的子邻域<li>、以及页面上重复渲染的同内容节点,出现重复数据(比如示例中的102号Kalasatama既出现在10号主邻域的嵌套结构中,又被作为独立顶层元素抓取) - 没有建立主邻域和子邻域的层级关联,无法自动把三位编码的细分项挂到对应的两位编码主邻域下
落地实现方案
调整抓取逻辑(从根源减少脏数据)
不要用全页面li切片的方式抓内容,直接定位存放邻域列表的无序列表容器,沿着DOM层级遍历元素,避免抓取到无关节点和重复嵌套项。
逐元素结构化提取
遍历每个邻域节点时,按照编码长度自动区分主邻域、子邻域的层级,实时维护当前所属的主邻域变量,逐行整理符合格式的数据。
参考可运行代码
import pandas as pd import requests from bs4 import BeautifulSoup url = 'https://en.wikipedia.org/wiki/Subdivisions_of_Helsinki' page = requests.get(url) soup = BeautifulSoup(page.content, "html.parser") # 定位邻域列表对应的无序列表,避开页面导航、目录等无关的ul元素 neighborhood_container = soup.find_all('ul')[3] # 初始化存储结构和当前主邻域追踪变量 record_list = [] active_main_neighborhood = None for list_item in neighborhood_container.find_all('li', recursive=False): # 提取编码:取条目开头的数字部分 item_text = list_item.get_text(strip=True) area_code = item_text.split()[0] # 提取正式邻域名:取条目内链接的文本,自动跳过后面的瑞典语对照标注 area_name = list_item.find('a').get_text(strip=True) # 按编码长度区分层级 if len(area_code) == 2: # 两位编码对应主邻域,更新当前追踪的主邻域值 active_main_neighborhood = area_name record_list.append({ "Code": area_code, "Main_neighborhood": area_name, "Sub_neighborhood": None }) elif len(area_code) == 3: # 三位编码对应子邻域,关联到当前激活的主邻域 record_list.append({ "Code": area_code, "Main_neighborhood": active_main_neighborhood, "Sub_neighborhood": area_name }) # 生成指定列名的最终DataFrame helsinki_neighborhood_df = pd.DataFrame( record_list, columns=["Code","Main_neighborhood", "Sub_neighborhood"] )
关键逻辑说明
- 遍历子元素时加
recursive=False参数,只取当前列表的直接子节点,不会把嵌套在主邻域内部的子节点重复抓取,从根本上解决示例中的重复条目问题 - 用
active_main_neighborhood变量实时记录最近遇到的主邻域,后续所有子邻域条目都会自动绑定到对应主邻域,无需手动匹配层级关系 - 邻域名称只提取链接文本,自动过滤斜体标注的瑞典语对照名,不会把无关内容混入字段
如果不调整初始抓取逻辑,也可以基于已抓回的helsinki_neiborhood_raw做后处理:遍历每个条目时跳过嵌套在其他<li>内部的重复元素,再套用上述层级判断和关联逻辑,一样可以得到规整的DataFrame。
内容的提问来源于stack exchange,提问作者muzi-chen
相关产品推荐
相关产品推荐

