88785


pythonで以下のページをスクレイピングし、フライト情報を取得したいです。

[https://www.fukuoka-
airport.jp/flight/?depArv=D&domCode=all&domLocation=all&domNum=&domTime=all&intCode=all&intDepArv=D&intDom=D&intLocation=all](https://www.fukuoka-
airport.jp/flight/?depArv=D&domCode=all&domLocation=all&domNum=&domTime=all&intCode=all&intDepArv=D&intDom=D&intLocation=all)

selenium・BeautifulSoupを用いてコードを書いたのですが、  
該当ページの文字を抽出できず、困っている状況です。

すいません、コードのどこがいけないのかスクレイピングに詳しい方教えていただけませんでしょうか。

HTML

[code]

    <div class="flight-result departure"><div class="h-flight"><ul><li class="h-txt01">出発時刻</li><li class="h-txt02">行先・便名</li><li class="h-txt03"><span>チェックイン<br>カウンター</span><span>検査場</span><span>ゲート</span></li><li class="h-txt04">状況</li></ul></div><ul><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ANA-0244"><div class="flight-time"><span class="flight-time-init"><span>09:00</span></span><span class="flight-changetime">09:16</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></div><div><p class="flight-destination">東京</p><p class="flight-code">ANA244</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="north">北</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>8</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ANA-0422"><div class="flight-time"><span class="flight-time-init"><span>09:00</span></span><span class="flight-changetime">08:56</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></div><div><p class="flight-destination">大阪</p><p class="flight-code">ANA422</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="north">北</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>6</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-JAL-3581"><div class="flight-time"><span class="flight-time-init"><span>09:00</span></span><span class="flight-changetime">08:53</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_JL.jpg" alt=""></div><div><p class="flight-destination">高知</p><p class="flight-code">JAL3581</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>11</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-FDA-0302"><div class="flight-time"><span class="flight-time-init"><span>09:05</span></span><span class="flight-changetime">09:03</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_FH.jpg" alt=""></div><div><p class="flight-destination">名古屋/小牧</p><p class="flight-code">FDA302</p></div></div><div class="flight-codeshare"><ul><li><span class="flight-logo"><img src="/uploads/2020/07/img_flight_JL.jpg" alt=""></span><span class="flight-code">JAL4402</span></li></ul></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="north">北</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>3</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-JAL-0306"><div class="flight-time"><span><span>09:05</span></span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_JL.jpg" alt=""></div><div><p class="flight-destination">東京</p><p class="flight-code">JAL306</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>10</span></div></div><div class="flight-status">欠航</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-SFJ-0042"><div class="flight-time"><span class="flight-time-init"><span>09:10</span></span><span class="flight-changetime">09:07</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/12a917db8ed1f43045e51332c95d038b737d24e7.jpg" alt=""></div><div><p class="flight-destination">東京</p><p class="flight-code">SFJ42</p></div></div><div class="flight-codeshare"><ul><li><span class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></span><span class="flight-code">ANA3842</span></li></ul></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>10</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-JTA-0053"><div class="flight-time"><span class="flight-time-init"><span>09:25</span></span><span class="flight-changetime">09:20</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_NU.jpg" alt=""></div><div><p class="flight-destination">沖縄/那覇</p><p class="flight-code">JTA53</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>12</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ORC-0097"><div class="flight-time"><span><span>09:35</span></span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_OC.jpg" alt=""></div><div><p class="flight-destination">福江</p><p class="flight-code">ORC97</p></div></div><div class="flight-codeshare"><ul><li><span class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></span><span class="flight-code">ANA4697</span></li></ul></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>82</span></div></div><div class="flight-status">欠航</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ANA-1076"><div class="flight-time"><span class="flight-time-init"><span>09:40</span></span><span class="flight-changetime">09:35</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></div><div><p class="flight-destination">東京</p><p class="flight-code">ANA1076</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="north">北</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>7</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ANA-1203"><div class="flight-time"><span class="flight-time-init"><span>09:40</span></span><span class="flight-changetime">09:39</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></div><div><p class="flight-destination">沖縄/那覇</p><p class="flight-code">ANA1203</p></div></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>11</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-FDA-0142"><div class="flight-time"><span class="flight-time-init"><span>09:50</span></span><span class="flight-changetime">09:45</span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_FH.jpg" alt=""></div><div><p class="flight-destination">静岡</p><p class="flight-code">FDA142</p></div></div><div class="flight-codeshare"><ul><li><span class="flight-logo"><img src="/uploads/2020/07/img_flight_JL.jpg" alt=""></span><span class="flight-code">JAL3811</span></li></ul></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="north">北</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>5</span></div></div><div class="flight-status">出発済み</div></a></li><li><a href="/flight/detail.html?id=FUK-2022-05-12-2022-05-12-D-ORC-0065"><div class="flight-time"><span><span>09:50</span></span></div><div class="flight-main"><div class="flight-ttl"><div class="flight-logo"><img src="/uploads/2020/07/img_flight_OC.jpg" alt=""></div><div><p class="flight-destination">宮崎</p><p class="flight-code">ORC65</p></div></div><div class="flight-codeshare"><ul><li><span class="flight-logo"><img src="/uploads/2020/07/img_flight_NH.jpg" alt=""></span><span class="flight-code">ANA4665</span></li></ul></div></div><div class="flight-place"><div class="flight-map"><span class="txt-sp">チェックイン<br>カウンター</span><span class="icon-map">MAP</span></div><div class="flight-terminal"><span class="txt-sp">検査場</span><span class="south">南</span></div><div class="flight-gate"><span class="txt-sp">ゲート</span><span>82</span>....
    
[/code]

全体のコード

[code]

    import pandas as pd
    import time
    from datetime import datetime as dt, date, timedelta
    from dateutil.relativedelta import relativedelta
    from selenium import webdriver
    from bs4 import BeautifulSoup
    from webdriver_manager.chrome import ChromeDriverManager
    from dateutil.relativedelta import relativedelta
    
    today= dt.now().strftime("%Y%m%d")
    
    yesterday =dt.strftime( dt.today() - relativedelta( days = 1 ), "%Y%m%d" )
    
    #保存ファイル指定
    file="test"+today+".csv"
    
    #url指定
    url = 'https://www.fukuoka-airport.jp/flight/?depArv=D&domCode=all&domLocation=all&domNum=&domTime=all&intCode=all&intDepArv=D&intDom=D&intLocation=all'
    
    driver = webdriver.Chrome(executable_path=ChromeDriverManager().install())
    #driver = webdriver.Chrome(executable_path="C:\selenium\chromedriver.exe")
    
    #フライ情報のサイトを開く
    driver.get(url)
    
    #5秒待機
    time.sleep(5)
    #BeautifulSoup指定
    html = driver.page_source.encode('utf-8')
    soup = BeautifulSoup(html, 'html.parser')
    
    tbl = []
    for li in soup.select('div.flight-result departure ul li'):
    
        data = {}
    
        # 定刻[変更]
        ft = li.select_one('span.flight-time-init')
        if ft is None: continue
        data['定刻[変更]'] = ft.text
        ct = li.select_one('span.flight-changetime')
        if ct: data['定刻[変更]'] += f'\n{ct.text}'
        
        # 出発地[経由]
        st = li.select_one('p.flight-destination')
        data['出発地[経由]'] = st.text if st else ''
        
        #便名
        fn = li.select_one('p.flight-code')
        data['便名'] = fn.text if fn else ''
    
        # 航空会社(コードシェア)
        fa = li.select_one('div.flight-codeshare')
        cdshare = ','.join([s.text for s in fa.find_all(class_='flight-code') if s])
        data['航空会社(コードシェア)'] = cdshare if cdshare else ''
    
        # ターミナル
        term = li.select_one('flight-terminal')
        data['ターミナル'] = term.text if term else ''
    
        #ゲート
        model = li.select_one('flight-gate')
        data['ゲート'] = model.text.split(':')[-1].strip() if model else ''
    
        # 現在の状況
        status = li.select_one('flight-status')
        data['現在の状況'] = status.text if status else ''
        
        tbl.append(data)
    
    df = pd.DataFrame(tbl)
    print(df)
    
    # #Excel形式でハルエネ出力-------------------
    # #df.to_excel(file,index=False,encoding='utf-8')
    df.to_csv(file,index=False,encoding="utf_8_sig")
    
    #driver.close()
    
[/code]

現在の結果

[code]

    df = pd.DataFrame(tbl)
    print(df)
    
    Empty DataFrame
    Columns: []
    Index: []
    
[/code]

お手数ですが、よろしくお願い致します。


CC BY-SA 4.0
2022-05-12T10:17:50.217
88782
2022-05-15T04:53:11.013
2022-05-12T11:44:04.467
3060
18859
question
python python3 web-scraping
Pythonでスクレイピング方法について


3点あって、以下の最初の点がデータを取れない原因です。

指定したいクラス名に空白が入っている時は、そのまま空白を指定するのではなく`.`に置き換える必要があるみたいですね。

[code]

    for li in soup.select('div.flight-result departure ul li'):
    
[/code]

`flight-result departure`を`flight-result.departure`にする必要があるようです。

[code]

    for li in soup.select('div.flight-result.departure ul li'):
    
[/code]

**追記**  
@oriri さんの補足(注意?)記事が追加されたので：

上記を書いたのは開発者ツールで該当タグのCSS Selectorを取得した時に以下のように表示されたためです。

[code]

    #flight-search-area > div > div.flight-result.departure
    
[/code]

ただ、今調べたらこんな記事もあるので、そうそう変なやり方でもなさそうです。  
[BeautifulSoup and class with
spaces](https://stackoverflow.com/q/46718366/9014308)

* * *

上記が通るようになると、今度はコードシェア便情報で問題が出てきます。  
以前の質問と違って、こちらのページではコードシェア便が無い時は`div.flight-codeshare`そのものが存在しないようです。

[code]

    cdshare = ','.join([s.text for s in fa.find_all(class_='flight-code') if s])
    
[/code]

そうすると上記行でエラーになるので、以下のように変更する必要があるようです。

[code]

    cdshare = ','.join([s.text for s in fa.find_all(class_='flight-code') if s]) if fa else ''
    
[/code]

* * *

ターミナル、ゲート、現在の状況については、セレクタの頭に`div.`を付ける必要があるようですね。付けていないと空白のままになります。  
以下変更した該当行を抜粋します。

[code]

        term = li.select_one('div.flight-terminal')
    
        model = li.select_one('div.flight-gate')
    
        status = li.select_one('div.flight-status')
    
[/code]

* * *

**コメント対応**

そもそも質問のソースでは、時刻変更のあった便の情報しか取れていませんでした。  
コードシェア便の情報有無がどうなっているのかと同様に、空港ごとに細かい形式の違いがあるようですね。

ここの部分を：

[code]

        # 定刻[変更]
        ft = li.select_one('span.flight-time-init')
        if ft is None: continue
        data['定刻[変更]'] = ft.text
        ct = li.select_one('span.flight-changetime')
        if ct: data['定刻[変更]'] += f'\n{ct.text}'
    
[/code]

こちらのように変更する必要があります。

[code]

        # 定刻[変更]
        ft = li.select_one('span.flight-time-init')
        if ft is not None:
            data['定刻[変更]'] = ft.text
            ct = li.select_one('span.flight-changetime')
            if ct: data['定刻[変更]'] += f'\n{ct.text}'
        else:
            ft = li.select_one('div.flight-time')
            if ft is None: continue
            data['定刻[変更]'] = ft.text
    
[/code]


CC BY-SA 4.0
2022-05-12T11:08:23.590
88785
2022-05-15T04:53:11.013
2022-05-15T04:53:11.013
26370
26370
88782
answer


API を利用する場合。

[code]

    import pandas as pd
    
    pd.set_option('display.unicode.east_asian_width', True)
    
    url = 'https://www.fukuoka-airport.jp/api/flight_schedule/flight_schedule.json'
    df = pd.read_json(url, orient='index')
    
    #
    df = (
      df.query('deparv_div == "D" and remarks != "欠航"')
        .sort_values(['appointed_time', 'airline_cd'])
        .reset_index(drop=True))
    
    dfx = pd.DataFrame({
      '出発時刻(予定)': (df['appointed_time'].map('{:04d}'.format)
                           .replace(r'.*(..)(..)$', r'\1:\2', regex=True)),
      '出発時刻(実際)': df['true_ymdhm'].replace(r'.*(..)(..)$', r'\1:\2', regex=True),
                '行先': df['tofrom_name'],
                '便名': df['airline_cd'] + df['flt_num_no'].astype(str),
              'ゲート': df['spot_num'],
                '状況': df['remarks'],
    })
    
    print(dfx)
    
    #
        出発時刻(予定) 出発時刻(実際)       行先     便名 ゲート      状況
    0            07:00          07:03       東京   ANA240      6  出発済み
    1            07:00          06:57       東京   JAL300      9  出発済み
    2            07:00          06:52       大阪  JAL2050     11  出発済み
    3            07:00          06:59       東京    SFJ40      2  出発済み
    4            07:00          06:56       東京     SKY2      4  出発済み
    ..             ...            ...        ...      ...    ...       ...
    165          20:55          20:51  沖縄/那覇    JTA65     12  出発済み
    166          21:05          21:03       東京   JAL332     11  出発済み
    167          21:10          21:16       関西   APJ160     1A  出発済み
    168          21:15          21:10       東京   ANA274      7  出発済み
    169          21:20          21:19       東京    SKY26      3  出発済み
    
    [170 rows x 6 columns]
    
[/code]


CC BY-SA 4.0
2022-05-12T15:10:45.020
88788
2022-05-12T17:46:28.173
2022-05-12T17:46:28.173
47127
47127
88782
answer


(… 解決済みのようだけど)

### Beautiful Soup で HTML class属性を扱う

質問にある HTMLの先頭の要素は

[code]

    <div class="flight-result departure">
    
[/code]

先頭 div要素の class属性は以下の通り (クラスが 2つ)

[code]

    soup = BeautifulSoup(html, 'html.parser')
    firstdiv = soup.div
    display(firstdiv['class'])   # soup.div['class'] でも可
    # ['flight-result', 'departure']
    
[/code]

HTMLの classは
[要素のクラスを空白区切りで並べたリスト](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/class)
です (Python, Beautiful Soupでは, とかでなく HTMLとして)。

[CSS セレクター](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Selectors) の
クラスセレクターは (頭に `.` を付けて) `.classname` という構文

記述 | CSS セレクター  
---|---  
`elementname` | 要素型セレクター  
`.classname` | クラスセレクター  
`elementname.classname` | (両方指定する場合)  
  
Beautiful Soup の [CSS
selectors](https://www.crummy.com/software/BeautifulSoup/bs4/doc/#css-
selectors) (`.select()` メソッド / `.select_one()` メソッド) は CSSのように指定できるもので,  
先の div要素を指定する場合 何通りか指定方法があり 以下はその一部

[code]

    soup.select_one('div.flight-result.departure')   # 要素＆クラスセレクター x2
    soup.select_one('.flight-result.departure')      # クラスセレクター x2
    soup.select_one('.flight-result')                # クラスセレクター x1
    soup.select_one('div')                           # 要素セレクター
    
[/code]

要素を select(選択)する際に 他の要素と区別するため, 明確に指定するなら 要素型セレクター, クラスセレクター,
属性セレクターなど組み合わせるとよいでしょう。  
複数のクラスセレクターを指定すると, **2つ以上(複数)の CSSクラスに一致するタグを検索** する意味になります (or ではなく and)。  
(`.find_all()`系のメソッド, `.select()`系メソッドで, 順序関係なく複数クラスを検索するには select() 系がよい模様)

* * *

質問にある HTMLの構造は, Emmetで表すとこんな感じで, 後ろ側の項目を取り出すようです  
`div>(div>ul>li)+ul>li>a>div`

先頭の div要素を特定させないと他と区別できないなら, 先の指定が必要だけれど  
そうでもないなら以下のように可能

[code]

    for li in soup.div.div.select_one('+ ul')('li'):
    # あるいは soup.div.div.find_next_sibling('ul')('li'):
        print(li)
    
[/code]


CC BY-SA 4.0
2022-05-15T04:14:54.803
88823
2022-05-15T04:14:54.803
43025
88782
answer