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

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

ただいまの
回答率

88.35%

whileを使ってボタンをクリックし続け、ボタンが無くなったら処理をやめたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,383

yudai109

score 18

前提・実現したいこと

https://itp.ne.jp/topic/?topic=225%3B772&sort=01&sbmap=false
こちらのサイトで「さらに表示する」を表示されるだけクリックして、全てクリック仕切ったら次の処理に進みたいです。

発生している問題・エラーメッセージ

Traceback (most recent call last):
  File "itown.py", line 21, in <module>
    while driver.find_element_by_class_name("m-read-more"):
  File "/Users/yudai/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 564, in find_element_by_class_name
    return self.find_element(by=By.CLASS_NAME, value=name)
  File "/Users/yudai/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 978, in find_element
    'value': value})['value']
  File "/Users/yudai/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/Users/yudai/opt/anaconda3/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".m-read-more"}
  (Session info: chrome=78.0.3904.108)

該当のソースコード

# itown メールアドレス スクレイピング 完成ばん
from bs4 import BeautifulSoup
from selenium import webdriver
import requests
import time
import csv
import re
from selenium.common.exceptions import NoSuchElementException


url_list = []
hpurl_list = []
adress_list = []
url = "https://itp.ne.jp/genre/?area=27&genre=4&subgenre=1028&sort=01&sbmap=false"

# 「さらに表示」をクリックしまくる。
driver = webdriver.Chrome(executable_path='chromedriver')
driver.get(url)


while driver.find_element_by_class_name("m-read-more"):
    show_btn = driver.find_element_by_class_name("m-read-more")
    show_btn.click()
    # js実行までいっとき待つ。余裕を持って1ページ4秒の計算。
    time.sleep(3)

# ブラウザのHTMLを読み込む。
res = driver.page_source
soup = BeautifulSoup(res, "html.parser")

for i in soup.find_all("a", class_='m-article-card__header__title__link'):
    elem = i.get("href") + "shop/"
    # url_listに格納
    url_list.append(elem)


driver = webdriver.Chrome(executable_path='chromedriver')
for m in url_list:
    driver.get(m)
    res = requests.get(m)
    res.raise_for_status()
    time.sleep(1)
    soup = BeautifulSoup(res.text, "html.parser")

    # 企業名ゲット
    try:
        y = driver.find_element_by_css_selector("body > div.container > div > div > div > div.main > div > article.item.item-table > div > section.item-body.basic > dl:nth-child(1) > dd")
    except NoSuchElementException:
        adress_list.append("None")
    else:
        title = y.text
        # 配列に格納
        adress_list.append(title)

    # 会社HPのURLゲット
    try:
        z = driver.find_element_by_xpath("/html/body/div[3]/div/div/div/div[1]/div/article[1]/div/section[1]/dl[9]/dd/div[1]/p[2]/a")
    except NoSuchElementException:
        adress_list.append("None")
    else:
        hp_url = z.get_attribute("href")
        # 配列に格納
        adress_list.append(hp_url)

    # アドレスゲット
    if soup.find_all('a', text=re.compile('@')):
        for p in soup.find_all('a', text=re.compile('@')):
            adress = p.text
            # 配列に格納
            adress_list.append(adress)
    else:
        adress_list.append('None')

# 3列のcsv作成
with open('itown_name_hp_ad.csv', 'w', newline='')as f:
    w = csv.writer(f, quotechar='"', quoting=csv.QUOTE_MINIMAL)
    for i in range(0, len(adress_list), 3):
        w.writerow(adress_list[i:i+3])

試したこと

while driver.find_element_by_class_name("m-read-more"):
show_btn = driver.find_element_by_class_name("m-read-more")
show_btn.click()
time.sleep(3)

の部分で、driver.find_element_by_class_name("m-read-more")によっての戻り値がなくなった時、なぜ次の処理に行かないのかわかりません。
falseではなく、Noneが戻り値として返ってきているのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+3

本題と全然それますが、4点ほどおせっかいさせてください。


2回目のdriver = webdriver.Chrome(executable_path='chromedriver')の宣言の直前と
プログラムの最終行にdriver.quit()を入れてあげてください。
今のままだと実行の度メモリにガベージがたまります。


一覧にサムネイルが合ったり、遷移先のGUIの違いから察するに、
このページの上の方はiタウンにお金を払って掲載している美容室が並び、
下の方にはお金を払わずに簡易な情報だけを掲載している美容室が並んでいます。
あなたのプログラムを見る限り有料掲載の情報のみ
取得したいようなので、無料掲載店舗の項目まで来たら
終了にするといいでしょう。
これは全店舗一覧ページのサムネイルの有り無しで判断できそうです。


Selenium、BeautifulSoup、requestと煩雑に使いすぎています。
後から見直したときに分かりやすいように、
出来る限り簡素にまとめてあげてください。
そんなに規模の大きなプログラムではないので
Seleniumだけあればまず事足ります。


値を丸ごとリストに格納していますが、
HPやメールアドレスが掲載されていないお店は
穴抜けができるので不自然です。
pythonにはこういう時の為に辞書型が用意されているので
利用してください。2重に辞書を使う形が適切だと思います。

driver.get(url)

# 全て開く
while True:
    try:
        show_btn = driver.find_element_by_class_name("m-read-more")
        show_btn.click()
        time.sleep(3)

        # 現状一番最後に並んでいる店舗にサムネイルが無かったら終了
        if len(driver.find_elements_by_class_name('o-result-article-list__item')[-1].find_elements_by_tag_name('img')) == 0:
            break

    # これ以上読み込めなくても終了
    except NoSuchElementException:
        break

# 店名と個別ページの辞書を取得
shops_url = {}
for li_tag in driver.find_elements_by_class_name('o-result-article-list__item'):

    # サムネの無い店舗まで来たら取得終了
    if len(li_tag.find_elements_by_tag_name('img')) == 0:
        break

    # 情報が入った要素を取得
    a_tag = li_tag.find_element_by_class_name('m-article-card__header__title__link')

    # 辞書に店舗名とurlを格納
    shop_url[a_tag.text] = a_tag.get_attribute('href') + 'shop/'


# この時点でshops_urlには
# {'ビューティサロンマリ':'https://itp.ne.jp/info/277391709081780640/shop/', 'サロン・ド・シーマ':'https://itp.ne.jp/info/275752231114381200/shop/', ...}という形で格納されている

# 空の辞書を宣言
shops_info = {}

# 各遷移先について情報を取得しに行く
for shop_name, shop_url in shops_url.items():

    # 空の辞書を宣言
    shop_info = {}

    # 遷移
    driver.get(shop_url)

    # HPを取得
    try:
        hp_address = driver.find_element_by_xpath('//a[contains(text(), "http")]').text
    except NoSuchElementException:
        hp_address = ''

    # メールアドレスを取得
    try:
        mail_address = driver.find_element_by_xpath('//a[contains(text(), "@")]').text
    except NoSuchElementException:
        mail_address = ''

    # HPとメールアドレスを辞書に格納
    shop_info['HP'] = hp_address
    shop_info['メルアド'] = mail_address

    # 全ての情報を大元の辞書に格納
    shops_info[shop_name] = shop_info

# driverを終了
driver.quit()

# 以下csv書き込み処理
# shops_infoには{'ビューティサロンマリ':{'HP':'https://itp.ne.jp/info/277391709081780640/', 'メルアド':''}, 'サロン・ド・シーマ':{'HP':'http://www.seema.cc/index.html', 'メルアド':'seema514@zeus.eonet.ne.jp'}, ...}という形で格納されている

ちゃんとテストしていないので間違いがあったらすみません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/11/29 20:20

    ご回答ありがとうございます!
    頂いたアドバイスとガベージなどを調べてさらに理解が深まりました。

    完全にプログラミング勉強を始めて1か月、『動けばいい』の思考であり、コードレビューを受けることもなかったので、すべてのアドバイスがとても新鮮でした!
    また、わかりやすくコメントで解説して頂き本当にありがとうございます!
    とても分かり易かったです!!


    誰かが見たときやパソコンの負担や、カードの煩雑さに配慮したコードを書いていけるよう学んでいきます!
    これからもこの場で質問させて頂く機会も増えるかと思います。
    今後ともよろしくお願い致します!!

    キャンセル

+2

driver.find_element_by_class_name()などの要素を探すメソッドは、要素が見つからなかったらNoneを返すのではなく、エラーを出します。

以下のようにすると良いでしょう。

from selenium.common.exceptions import NoSuchElementException

try:
    while driver.find_element_by_class_name("m-read-more"):
        show_btn = driver.find_element_by_class_name("m-read-more")
        show_btn.click()
        # js実行までいっとき待つ。余裕を持って1ページ4秒の計算。
        time.sleep(3)
except NoSuchElementException:
    pass

ちなみにNoneのbool値はFalseです

bool(None)
# False

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/11/29 20:11

    こちらでも解決できました!
    ありがとうございます!

    キャンセル

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

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

関連した質問

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