質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

python3 BS4 find_all で<li>をスタイル(width)で判別、見出しと値の辞書を作成するには

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 104

score 5

Python3 beautiful soup4 で スクレイピングしているのですが
IDが元ソースに付与されておらず、取得したい部分が<li>  </li>ばかりで、またページにより 項目数<li>の数が変わるので、決まった値を取得するのに難航しています。

唯一  styleでwidth 40 width60 とで 見出しと値が対になって判別できる為
これを見出しと値を 対にして 辞書型にすれば解決できるのではと思いました。

<ul>

<li style="width: 40%;">見出し1</li>
<li style="width: 60%;">値1</li>
<li style="width: 40%;">見出し2</li>
<li style="width: 60%;">値2</li>
<li style="width: 40%;">見出し3</li>
<li style="width: 60%;">値3</li>
<li style="width: 40%;">見出し4</li>
<li style="width: 60%;">値4</li>
<li style="width: 40%;">見出し5</li>
<li style="width: 60%;">値5</li>

</ul>                                

<li><li>タグではなくテーブルタグ <th></th> <tr></tr>で見出しと値が対になっている場合は 下記方法でできるので、これを応用して<li>のスタイル width 40 width 60% で判別してできないかと考えたのですが・・

dict={}
for tag in soup.find_all(['th', 'td']):
    if tag.name == 'th':
        key = tag.get_text()
    elif tag.name == 'td':
        dict[key] = tag.get_text()

おなじようにliタグのスタイル  width 40 or width60  で判別して
見出し と値で辞書にしたいです

やってみたこと

dict={}
for tag in soup.find_all([('li',style="width: 40%;"), ('li',style="width: 60%;")]):
    if tag.name == ('li',style="width: 40%;"):
        key = tag.get_text()
    elif tag.name == ('li',style="width: 60%;"):
        dict[key] = tag.get_text()


if tag.name == ('li',style.value'width: 40%;'):
^
SyntaxError: invalid syntax

dict={}
for tag in soup.find_all([soup.find_all('li' ,style=lambda value: value and 'width: 40%;' in value and 'width: 60%;' in value)]):
    if tag.name == ('li',style.value'width: 40%;'):
        key = tag.get_text()
    elif tag.name == ('li',style="width: 60%;"):
        dict[key] = tag.get_text()


エラー内容
File "<ipython-input-51-b737eb51cd68>", line 3
if tag.name == ('li',style.value'width: 40%;'):
^
SyntaxError: invalid syntax

この方法に拘っていませんが・・

※参照元ページは各ページで <li>の数が異なるのでsoul.select でCSSセレクターで取得すると、各ページで変数に入る値が異なるので、見出しをキーにして値を取り出したいです

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

    patternA = 'width: 40%;'
    patternB = 'width: 60%;'

    lst = soup.find_all('li')
    for element in lst:
        if element.get('style') == patternA:
            # patternAだった場合の処理
        elif element.get('style') == patternB:
            # patternBだった場合の処理

.find_all()を使用する際にオプションでstyleを指定してあげても良いでしょうが
上記の様にまずはli要素を取得してから、for文でstyle属性を検証していく手もあるでしょう。

※参照元ページは各ページで <li>の数が異なるのでsoul.select でCSSセレクターで取得すると、各ページで変数に入る値が異なるので、見出しをキーにして値を取り出したいです

どの様なページなのかはわかりませんがコツとしては
li要素から探すよりも、そもそもそれらul要素を包み込んでいる
大元にある要素や属性を指定していくと余計なli要素を拾わず対象の要素だけを検証出来るでしょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/07/22 18:39

    ヒントをありがとうございます! なるほど、for文を使うとは考えつきませんでした。いただいたヒントをもとに 条件別の処理で 辞書を作ってみたところ、目的の辞書を作ることができました

    dict={}
    patternA = 'width: 40%;'
    patternB = 'width: 60%;'

    lst = soup.find_all('li')
    for element in lst:
    if element.get('style') == patternA:
    key = element.get_text()
    elif element.get('style') == patternB:
    dict[key] = element.get_text()

    キャンセル

  • 2021/07/23 17:50

    肝心なエラーの原因について説明をしておりませんでした。
    例えばif tag.name == ('li',style.value'width: 40%;'):について
    やりたい事はなんとなくわかります。

    この様な記述は、BeautifullSoupを使用してソースをパースしたものに対して使用出来る記述となります。
    パースしたものが変数soupとなります。
    変数soupの中からli要素且つstyle属性がwidth: 40%となっているものを探してねーという指示を行いたい場合には、変数soupに対してfind_all()というメソッドを使用します。
    soup.find_all()
    括弧の中に引数として検索したい条件を与えていきます。li要素を指定したいならば
    soup.find_all('li')と指定します。
    更にstyle属性なども指定していきたい場合には
    soup.find_all('li', {'style': 'width: 40%;})という様に指定していきます。
    これは第一引数にli要素を指定し、第2引数にStyle属性と指定の値を辞書形式で与えているというものとなります。
    あくまでもこれは.find_all()メソッドに対して引数を与えているというものであり
    dubさんが書いていたif tag.name == ('li',style.value'width: 40%;'):というのは
    あくまでもタプルでしかない為、コードの意味としては
    もしも タグネームが ('li', style.value'width: 40%;') というタプルと同じだったら~
    という条件式になってしまっており、またそのタプル自体もタプルとして機能していない為にシンタックスエラーが生じてしまっているのです。

    キャンセル

0

  • Syntax Errorの原因

('li',style="width: 40%;")

というtupleの後ろの要素のところに代入式を書いていることです。

理由
tupleと、関数などの呼び出しに与える引数リストは別物です。
引数リストの場合、name=argumentと書けますが、これはキーワード付引数です。

  • 見出しをキーにして値を取り出したいです

htmlを使う場合、構造を意識した書き方もあれば、表現を意識した書き方もあります。
テーブルタグを使ったものは構造を意識したhtmlですのでBeautifulSoupで処理しやすいですが、今回の場合のように構造を意識しない書き方の場合にはBeautifulSoupで処理するのは難しいでしょう。

Pythonで一次元配列を二次元配列に変換(numpy.ndarray、リスト) を参考にして考えてみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/07/22 18:41

    ありがとうございます!まだnumpyを使った事がないくらいの超初心者なので、numpy勉強してみます

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る