アプリ版:「スタンプのみでお礼する」機能のリリースについて

python(3.10)のWebスクレイピングで、requestsとBeautifulSoup利用で、こんな現象。
find(最初の1行だけ)だとurl出るのに、find_allだとurlがNoneになる。
不思議です。
PCのpython-IDLEでも、googleのcolabでも結果は同じ。こんな経験された方、いませんか?
--------------------------------------------
PC Win10 python3.10 IDLE
urlはあるWebサイトで多数のリストを一覧表で掲載。

python プログラム

import requests
from bs4 import BeautifulSoup
load_url = "url"
html = requests.get(load_url)
soup = BeautifulSoup(html.content, "html.parser")
chap = soup.find('div', id="aaa")
names = chap.find("li") #findだとhrefが機能する!
for name in names:
n_url = name.get("href")
n_name = name.getText()
print(n_url,"\t",n_name)

#htmlの該当部分は,下記の構造 <url 名前 県名> を表示
<li class=""><a href="url/***.pdf" target="_blank">伊藤</a><span class="small">(愛知)</span></li>
---------------
結果 問題なし期待した内容だ、ただ意味不明なNoneが入ってる
url/aaa.pdf 伊藤 None (愛知)

#htmlの該当部分は,下記の構造で、soup結果が複数行配列となっていることはprintで確認済み。
<li class=""><a href="url/***.pdf" target="_blank">名前</a><span class="small">(県名)</span></li>

-----------------------------------
import requests
from bs4 import BeautifulSoup
load_url = "url"
html = requests.get(load_url)
soup = BeautifulSoup(html.content, "html.parser")
chap = soup.find('div', id="aaa")
names = chap.find_all("li") #find_allだとhrefがNoneに
for name in names:
n_url = name.get("href")
n_name = name.getText()
print(n_url,"\t",n_name)
------------------
結果 あるはずのurlが全てNoneになっている。それ以外は予定した内容。
None 伊藤(愛知)
None 吉田(東京)
None 佐藤(名古屋)
None 田中(広島)
以下同様
-----------------------------------

質問者からの補足コメント

  • HAPPY

    とりあえず、目的を達成できたけど、もやもやしてさらに調べているうちにこのサイトを発見。
    BeautifulSoupの使い方
    https://lets-hack.tech/programming/languages/pyt …
    解説分かりやすく、「こうすれば失敗」も書かれてる。
    これを参考にして、liのリストを作り、件数を数えwhileで、liごとに
    print(liのaのhref、liのテキスト)で、url 名前 県 を作ることに成功!
    liのリストの各要素は、<class 'bs4.element.Tag'>でした。単なる文字列(str)ではなかった。
    なのでbs4のメソッドが使えました。

      補足日時:2022/04/18 08:02
  • HAPPY

    まとめ
    最初にdivで抽出し
    その中のliを抽出し
    liの1つごとにメソッド適用し、対象全件の url 名前 県名 を抽出できました。
    load_url = "http://******"
    html = requests.get(load_url)
    soup = BeautifulSoup(html.content, "html.parser")
    div_abcd = soup.find('div', id="abcd") #soupメソッドで特定divタグ抽出
    lists = div_abcd.find_all("li") #soupメソッドでliタグ全件抽出
    for list in lists: #liタグリスト全体を
    print(list.a.get('href'),list.text) #抽出したliタグ一件ごとにa要素のurlと、textをprint

      補足日時:2022/04/18 09:07

A 回答 (4件)

pythonについての知識がありませんので回答できません。

    • good
    • 0

手持ちの環境にBeautifulSoupを入れてみてhelpをみたけど


find の返値は A PageElement で find_all の返値は A ResultSet of PageElements だった。後者はリストでなくセットですね。まあ大差ないでしょう。
PageElement から for で要素を取得した時に何が返るかは要確認だけど先の回答でおおよそ間違いないかと思う。
    • good
    • 0
この回答へのお礼

前の回答に記載しました。返答、感謝!

お礼日時:2022/04/17 23:19

うーん。

動作チェックしていないのでコードからの推測ですが……
names = chap.find("li")のときnamesは <li> <a href="url">xxx</a><span>yyy</span></li> というli要素だと思いますが、このときnameは何になっていますか? <a href="url">xxx</a>や<span>yyy</span>になっているのではないかと推測しますが。このとき前者のa要素はhrefタグを持つのでurlを返します。後者のspanはhrefを持たないのでNoneが返ります。getTextはそれぞれxxxとyyyを返します。
names = chap.find_all("li") のときのnamesはおそらく[<li> <a href="url">xxx</a><span>yyy</span></li>] という1要素のリストと思います。このときnameは <li> <a href="url">xxx</a><span>yyy</span></li> で、li要素はhrefタグを持たないのでNoneしか返しません。getTextはxxx yyyを返します。

要するにfindとfind_allは返る型が違うので、同じ操作で同じような結果にはなりません。findは中身かNoneが返るがfind_allは必ずリストが返るのでリストの中身を処理するように書かないと上手くいきません。
動作が分かりにくいようならところどころにprint文を入れて返値をチェックしながらコードを書くと良いでしょう。
    • good
    • 0
この回答へのお礼

返信、感謝!
小生、まだpython勉強始めて保育園以下です。
ネットと本で勉強中。今回、初めて壁に頭が当たって質問させてもらいました。
100件以上あるリストですが、配列3番目[2]でそれぞれをprintしたら中身は下記でした。1件目からすべて同じ構造でした。
1.find_all("li") →<li> <a href="url">xxx</a><span>yyy</span></li>
2.find_all("a") →<a href="url">xxx</a>
3.finde_all("span") →<span>yyy</span>

正解かどうかわかりませんが、1からurl, xxx, yyy 全てをまとめて取り出すのは諦め、
2と3をそれぞれ行って、2つの配列を作り、件数(2つとも同じ)をカウントし、
while文で
2から
 url  →2.get('href')
 xxx  →2.getText()
3から
 yyy  →3.getText()
を組み合わせて1行にprintすることで、目的の部分を取り出すことができました。
ただ、この方法はすべての行に同じ構造があったためにできたことで、汎用性はありません。スマートでもありません。
きっと何か良い方法があるはずです。

いつもやってる手動スクレイピングで、Webをコピーし、エディターで整形した方が正確かつ早くできました。自動スクレイピング前途遼遠!

お礼日時:2022/04/17 23:19

liタグの中にはaタグだけで無くspanタグも入っているので、


for name in names.find_all("a"):
でしょう。
    • good
    • 0
この回答へのお礼

さっそくのご教示に感謝します。(投稿からわずか1時間以内!)
a要素のurlと名前は、教えていただいた方法で、表示できるようになりました。大進歩です!!!
ありがとうございます(^-^;

for name in names.find_all("a"):
で取り出せたのは、配列で
[<a href="url">xxx</a>,---,---,---]
で、取り出せたのは、url xxx だけ。

names = chap.find_all("li")  で取り出された
<li> <a href="url">xxx</a><span>yyy</span></li>
のyyy部分が抽出できません。
ただ、こっちでは getText()で、None xxx yyy が抽出されています。Noneは謎!

names = chap.find("li") (最初の1件だけ)では 
<li> <a href="url">xxx</a><span>yyy</span></li>
から、url xxx yyy が取り出せました。
find_allで配列要素になったら<span>が邪魔で取り出せないって理解困難。
これは、なんかのバグでしょうか?

ご教示いただいた方法+li要素で抽出しgetTextで取り出しものを加工しクエリーでくっつけることは可能です。

でも、これはpythonの簡潔さ方針とは反します。
難しいですね。

お礼日時:2022/04/17 20:27

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


このQ&Aを見た人がよく見るQ&A