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

如何用Python抓取航空事故数据并构建规范多列DataFrame?

航空事故数据抓取优化需求

该网站数据库涵盖1902至2022年的数据,目标是抓取2015和2016年所有事故的表格信息、事故经过(narrative)、可能原因(probable cause)及事故分类(classification)。现有代码仅能抓取表格数据,但生成的DataFrame数据杂乱无章,需调整代码以构建符合期望的多列规范DataFrame。

现有抓取代码

import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
import re
import concurrent.futures
import itertools
from random import randint
from time import sleep

def scraping(year):


    headers =   {
        'accept':'*/*',
        'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36',
        }

    url = f'https://aviation-safety.net/database/dblist.php?Year={year}&sorteer=datekey&page=1'
    #sleep(randint(1,3))
    req = requests.get(url, headers=headers)

    soup = BeautifulSoup(req.text,'html.parser')

    page_container = soup.find('div',{'class':'pagenumbers'})

    pages = max([int(page['href'].split('=')[-1]) for page in  page_container.find_all('a')])
        

    #info = []
    tl = []
    for page in range(1,pages+1):

        new_url = f'https://aviation-safety.net/database/dblist.php?Year={year}&lang=&page={page}'
        print(new_url)
        
        #sleep(randint(1,3))
        data = requests.get(new_url,headers=headers)
        soup = BeautifulSoup(data.text,'html.parser')


        table = soup.find('table')
   
    
        for index,row in enumerate(table.find_all('tr')):
            if index == 0:
                continue

            link_ = 'https://aviation-safety.net/'+row.find('a')['href']
            
            #sleep(randint(1,3))
            new_page = requests.get(link_, headers=headers)
            new_soup = BeautifulSoup(new_page.text, 'lxml')
            table1 = new_soup.find('table')
            
           
            for i in table1.find_all('tr'):
                title = i.text
                tl.append(title)
                
                
    df= pd.DataFrame(tl)
    df.columns = ['status'] 
    df.to_csv(f'{year}_aviation-safety_new.csv', encoding='utf-8-sig', index=False)    
          

if __name__ == "__main__":

    START = 2015
    STOP = 2016

    years = [year for year in range(START,STOP+1)]

    print(f'Scraping {len(years)} years of data')

    with concurrent.futures.ThreadPoolExecutor(max_workers=60) as executor:
        final_list = executor.map(scraping,years)

当前数据效果

当前DataFrame效果

期望数据效果

期望的DataFrame效果

解决方案:调整后的代码

原问题核心是未按事故维度分类整理数据,仅将详情页文本无序存入列表。调整思路为:按事故维度合并列表页表格数据与详情页指定字段,最终生成规范DataFrame。

import requests
from bs4 import BeautifulSoup
import pandas as pd
from random import randint
from time import sleep
import concurrent.futures

def get_detail_data(link, headers):
    """抓取事故详情页的narrative、probable cause、classification"""
    sleep(randint(1,3))
    new_page = requests.get(link, headers=headers)
    new_soup = BeautifulSoup(new_page.text, 'lxml')
    
    detail_data = {
        'narrative': '',
        'probable_cause': '',
        'classification': ''
    }
    
    # 提取事故分类
    class_elem = new_soup.find('div', {'class': 'listspan'})
    if class_elem:
        detail_data['classification'] = class_elem.get_text(strip=True).replace('Classification: ', '')
    
    # 提取事故经过和可能原因
    content_divs = new_soup.find_all('div', {'class': 'text16'})
    for div in content_divs:
        strong_tag = div.find('strong')
        if not strong_tag:
            continue
        if 'Narrative' in strong_tag.text:
            detail_data['narrative'] = div.get_text(strip=True).replace('Narrative: ', '')
        elif 'Probable Cause' in strong_tag.text:
            detail_data['probable_cause'] = div.get_text(strip=True).replace('Probable Cause: ', '')
    
    return detail_data

def scraping(year):
    headers = {
        'accept':'*/*',
        'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36',
    }

    # 获取总页数
    url = f'https://aviation-safety.net/database/dblist.php?Year={year}&sorteer=datekey&page=1'
    req = requests.get(url, headers=headers)
    soup = BeautifulSoup(req.text,'html.parser')
    page_container = soup.find('div',{'class':'pagenumbers'})
    pages = max([int(page['href'].split('=')[-1]) for page in page_container.find_all('a')]) if page_container else 1

    all_accidents = []
    
    for page in range(1,pages+1):
        new_url = f'https://aviation-safety.net/database/dblist.php?Year={year}&lang=&page={page}'
        print(f'Processing page {page}/{pages} for year {year}')
        sleep(randint(1,2))
        data = requests.get(new_url,headers=headers)
        soup = BeautifulSoup(data.text,'html.parser')
        table = soup.find('table')
        
        # 获取列表页表格的表头
        if page == 1:
            headers_list = [th.get_text(strip=True) for th in table.find_all('tr')[0].find_all('th')]
            headers_list.extend(['narrative', 'probable_cause', 'classification'])
        
        for index,row in enumerate(table.find_all('tr')):
            if index == 0:
                continue
            # 提取列表页行数据
            row_data = [td.get_text(strip=True) for td in row.find_all('td')]
            # 获取详情页链接
            link_ = 'https://aviation-safety.net' + row.find('a')['href']
            # 抓取详情页数据
            detail_data = get_detail_data(link_, headers)
            # 合并数据
            row_data.extend([detail_data['narrative'], detail_data['probable_cause'], detail_data['classification']])
            all_accidents.append(row_data)
    
    # 生成DataFrame并保存
    df = pd.DataFrame(all_accidents, columns=headers_list)
    df.to_csv(f'{year}_aviation-safety_clean.csv', encoding='utf-8-sig', index=False)
    print(f'Finish scraping year {year}, saved to {year}_aviation-safety_clean.csv')

if __name__ == "__main__":
    START = 2015
    STOP = 2016
    years = [year for year in range(START, STOP+1)]
    print(f'Scraping {len(years)} years of data')
    
    # 降低线程数,避免请求过于频繁被封
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        executor.map(scraping, years)

关键调整说明

  • 新增get_detail_data函数,精准提取详情页的目标字段
  • 按事故维度合并列表页与详情页数据,保证每条记录对应单个事故
  • 添加请求延迟、降低线程数,规避网站反爬机制
  • 动态获取列表页表头,确保DataFrame列名与数据一一对应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 03:01:14