
[code]

    <h1>タイトル</h1>
    <h2>サブタイトル1<h2>
    <ul>
    <li>本文</li>
    <li>本文</li>
    ...
    </ul>
    <h3>サブサブタイトル<h3>
    <ul>
    <li>本文</li>
    <li>本文</li>
    ...
    </ul>
    <h2>サブタイトル2<h2>
    ...
    <h2>サブタイトル3<h2>
    ...
    
[/code]

というような構造のHTMLからタイトル->サブタイトル->サブサブタイトルと指定した範囲内で本文を取得するパースをしたいのですがどのようにすればいいでしょうか。

[code]

    title_lv1 = soup.findAll('h1')
        for title1 in title_lv1:
            if title1.text == sys.argv[1]:
                print(title1.findAll('h2')
    
[/code]

というようにすると空のリストが出力されてしまいます。


CC BY-SA 3.0
2017-06-20T06:20:51.717
35741
2017-06-21T02:01:22.673
2017-06-20T13:37:13.340
23913
23913
question
python html python3 web-scraping
Python3とBeautifulsoup4を使ったHTMLのパースについて


質問で提示されたhtmlには閉じられていないタグが含まれますが、`<h2>...</h2>`のようにタグが閉じられているhtmlを用いる前提で記述します。

提示されたhtmlは入れ子になっていないので、findAllを使って`h1`の中にある`h2`は取得できません。  
下記のような構造ならば`title1.findAll('h2')`で`h2`が取得できます。

[code]

    <h1>
        <h2>入れ子!</h2>
    </h1>
    
[/code]

今回のような並列構造では、`find_next_sibling`を使って条件付きで隣のタグを取得する必要があります。  
下記のようなコードで、目的の本文を取得できそうです。

[code]

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(open('test.html'), 'html.parser')
    #textで指定する各タイトルは、変数やコマンドライン引数に書き換えてください
    h3 = soup.find('h1', text='タイトル').find_next_sibling('h2', text='サブタイトル').find_next_sibling('h3', text='サブサブタイトル')
    ul = h3.find_next_sibling('ul')
    for li in ul.findAll('li'):
      print(li.text)
    
[/code]

ただし上記のコードは`find_next_sibling`で、見つかったタグの下にあるタグをすべて見つけるため、下記のような問題が発生する可能性があります。  
タグが必ず存在する前提ならば問題ありませんが、そうでなければ`find_next_sibling`にtextの条件を追加せず、for文などを使って範囲の洗い出しをするよう改修してください。

[code]

    <h1>タイトル</h1>
    <h2>サブタイトル</h2>
    <!--サブサブタイトルがない-->
    <h2>サブタイトル2</h2>
    <h3>サブサブタイトル</h3> <!--←サンプルコードのNextSiblingでサブタイトル2の下にスキップしてしまう-->
    
[/code]


CC BY-SA 3.0
2017-06-21T02:01:22.673
35765
2017-06-21T02:01:22.673
9820
35741
answer