用BeautifulSoup爬取德国城市维基百科信息框存DataFrame遇问题
维基百科德国城市信息框爬取解决方案
问题分析
- pd.read_html数据量不足:维基百科页面包含多个嵌套表格,
pd.read_html默认抓取的表格并非目标城市列表,需指定表格的class属性筛选。 - 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
相关产品推荐
相关产品推荐

