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

用BeautifulSoup爬取德国城市维基百科信息框存DataFrame遇问题

维基百科德国城市信息框爬取解决方案

问题分析

  1. pd.read_html数据量不足:维基百科页面包含多个嵌套表格,pd.read_html默认抓取的表格并非目标城市列表,需指定表格的class属性筛选。
  2. bs4全量爬取报错:部分城市页面可能缺失信息框或指定字段,导致findNext调用在NoneType对象上触发AttributeError,需添加异常处理与节点存在性判断。

解决方案

一、用pd.read_html快速获取城市列表基础数据

如果仅需城市列表的基础信息(如名称、州、人口),直接指定目标表格的class即可:

import pandas as pd

main_url = "https://zh.wikipedia.org/wiki/%E5%BE%B7%E5%9B%BD%E5%9F%8E%E5%B8%82%E5%88%97%E8%A1%A8"
# 抓取class为"wikitable sortable"的目标表格
city_df = pd.read_html(main_url, attrs={'class': 'wikitable sortable'})[0]
# 保存数据
city_df.to_csv('德国城市基础列表.csv', index=False, encoding='utf-8-sig')

二、用bs4完整爬取信息框全量数据

若需信息框中的详细数据(如联系信息、坐标、面积等),需遍历每个城市子页面,同时添加异常处理避免崩溃:

完整代码示例

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

# 配置请求头,模拟浏览器访问
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}

# 1. 爬取主页面的城市链接列表
main_url = "https://zh.wikipedia.org/wiki/%E5%BE%B7%E5%9B%BD%E5%9F%8E%E5%B8%82%E5%88%97%E8%A1%A8"
response = requests.get(main_url, headers=headers)
soup = BeautifulSoup(response.text, 'html.parser')

city_table = soup.find('table', class_='wikitable sortable')
city_links = []
for row in city_table.find_all('tr')[1:]:  # 跳过表头行
    cols = row.find_all('td')
    if cols:
        city_a_tag = cols[0].find('a')
        if city_a_tag and 'href' in city_a_tag.attrs:
            full_link = "https://zh.wikipedia.org" + city_a_tag['href']
            city_links.append(full_link)

print(f"共获取到 {len(city_links)} 个城市链接")

# 2. 遍历链接爬取每个城市的信息框数据
city_data_list = []
for link in city_links:
    try:
        # 发送请求并解析页面
        response = requests.get(link, headers=headers)
        response.raise_for_status()  # 抛出HTTP请求异常
        city_soup = BeautifulSoup(response.text, 'html.parser')
        
        # 查找信息框,不存在则跳过当前页面
        infobox = city_soup.find('table', class_='infobox geography vcard')
        if not infobox:
            print(f"跳过:{link} 未找到信息框")
            continue
        
        # 提取信息框数据
        city_info = {}
        # 提取城市名称
        title_tag = infobox.find('th', class_='infobox-title')
        if title_tag:
            city_info['城市名称'] = title_tag.get_text(strip=True)
        
        # 遍历信息框所有行,提取键值对
        for tr in infobox.find_all('tr'):
            th_tag = tr.find('th')
            td_tag = tr.find('td')
            if th_tag and td_tag:
                key = th_tag.get_text(strip=True)
                value = td_tag.get_text(strip=True).replace('\n', ' ')
                city_info[key] = value
        
        # 单独提取联系信息(部分页面可能放在特殊位置)
        contact_th = infobox.find('th', string=lambda x: x and '联系' in x)
        if contact_th:
            contact_tr = contact_th.find_next('tr')
            if contact_tr and contact_tr.find('td'):
                city_info['联系信息'] = contact_tr.find('td').get_text(strip=True).replace('\n', ' ')
        
        city_data_list.append(city_info)
        print(f"已爬取:{city_info.get('城市名称', link)}")
        
        # 延时1秒,避免触发反爬机制
        time.sleep(1)
    
    except Exception as e:
        print(f"爬取失败 {link}:{str(e)}")
        continue

# 3. 转换为DataFrame并保存
final_df = pd.DataFrame(city_data_list)
final_df.to_csv('德国城市完整信息.csv', index=False, encoding='utf-8-sig')
print("全量数据已保存至 德国城市完整信息.csv")

关键优化点

  • 请求头配置:模拟浏览器身份,避免被维基百科的反爬机制拦截。
  • 异常处理:用try-except捕获请求失败、节点缺失等异常,保证爬取流程不中断。
  • 节点存在性判断:每次调用find/findNext前先检查结果是否为None,避免AttributeError。
  • 请求延时:每次爬取后暂停1秒,降低服务器压力,减少被封风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 05:42:10