83345


以前は上手くテキストファイルにアウトプットされていたのですが、最近になってエラーが出ます。

[code]

    エラー: stale element reference: element is not attached to the page document
    
[/code]

やりたい事は、テキストファイルのデータを一行ずつ読み込み、ループで処理、アウトプットファイルにGoogle検索結果を書き出すという事です。

一回のみの処理だと問題ないのですが、ループにするとウェブブラウザが閉じないです。  
特にインデントでは問題ないかと思いますが、driver.quit()の位置を変更してみたりしています。

input.txtの内容は以下のようなものです。

[code]

    XYZ corp
    株式会社 ○○○
    
[/code]

forループに関しては"for data in file:"の箇所を設けて複数のテキストを抽出しております。

forループの場所を抜きにして、インデントを消去し、一個のキーワードのみだと問題なく動作します。

[code]

    import time                                 # スリープを使うために必要
    from selenium import webdriver              # Webブラウザを自動操作する（python -m pip install selenium)
    import chromedriver_binary
    from selenium.common.exceptions import NoSuchElementException
    from webdriver_manager.chrome import ChromeDriverManager
    driver = webdriver.Chrome(ChromeDriverManager().install())
     
    # サンプルのHTMLを開く
    driver.get('https://www.google.com/')       # Googleを開く
    
    file = open('input.txt', 'r',encoding="utf-8")  #読み込みモードでオープン
    for data in file:
        search = driver.find_element_by_name('q')   # HTML内で検索ボックス(name='q')を指定する
        search.send_keys(data)             # 検索ワードを送信する
        search.submit()                             # 検索を実行
        def ranking(driver):
            i = 1               # ループ番号、ページ番号を定義
            i_max = 1          # 最大何ページまで分析するかを定義
            title_list = []     # タイトルを格納する空リストを用意
            link_list = []      # URLを格納する空リストを用意
            # 現在のページが指定した最大分析ページを超えるまでループする
            while i <= i_max:
                # タイトルとリンクはclass="yuRUbf"に入っている
                class_group = driver.find_elements_by_class_name('yuRUbf')
                # タイトルとリンクを抽出しリストに追加するforループ
                for elem in class_group:
                        title_list.append(elem.find_element_by_class_name('LC20lb').text)           #タイトル(class="LC20lb")
                        link_list.append(elem.find_element_by_tag_name('a').get_attribute('href'))  #リンク(aタグのhref属性)
                # 「次へ」は1つしかないが、あえてelementsで複数検索。空のリストであれば最終ページの意味になる。
                if driver.find_elements_by_id('pnnext') == []:
                    i = i_max + 1
                else:
                    # 次ページのURLはid="pnnext"のhref属性
                    next_page = driver.find_element_by_id('pnnext').get_attribute('href')
                    driver.get(next_page)   # 次ページへ遷移する
                    i = i + 1               # iを更新
                    time.sleep(10)           # 10秒間待機
            return title_list, link_list    # タイトルとリンクのリストを戻り値に指定
        # ranking関数を実行してタイトルとURLリストを取得する
        title, link = ranking(driver)
        # タイトルリストをテキストに保存
        with open('title＿.output.txt', mode='a', encoding='utf-8') as f:
            f.write("\n".join(title))
        # URLリストをテキストに保存
        with open('link__output.txt', mode='a', encoding='utf-8') as f:
            f.write("\n".join(link))
        driver.quit()                               # ブラウザを閉じる
    
[/code]


CC BY-SA 4.0
2021-10-29T14:19:12.400
83332
2021-10-31T05:16:05.407
2021-10-31T04:53:57.837
3060
25766
question
python selenium
エラー: stale element reference: element is not attached to the page document


まず発生している現象の原因は、`open`したファイルオブジェクトをそのまま`for`に渡していることです。

この部分を：

[code]

    file = open('input.txt', 'r',encoding="utf-8")  #読み込みモードでオープン
    for data in file:
    
[/code]

以下のようにテキストファイルの内容を読み込んでリスト化してから`for`に渡せば動作します。

[code]

    with open('input.txt', 'r',encoding="utf-8") as f:
        words = f.read().splitlines()
    
    for data in words:
    
[/code]

* * *

なお上記を解決しても、`for`ループの最後で`driver.quit()`しているために、処理は`input.txt`の最初の1行分しか成功しません。

対策は`driver.quit()`を`for`ループ終了後に移動させて別途行うことです。

この部分を：

[code]

        with open('link__output.txt', mode='a', encoding='utf-8') as f:
            f.write("\n".join(link))
        driver.quit()                               # ブラウザを閉じる
    
[/code]

以下のように変更します。

[code]

        with open('link__output.txt', mode='a', encoding='utf-8') as f:
            f.write("\n".join(link))
    
    driver.quit()                               # ブラウザを閉じる
    
[/code]

しかし単にそれだけだと、`input.txt`の2行目以降の処理の際に検索し指定する言葉が以前のものに続けて連結されて入力されたことになるので、そのままでは動作はしますが意図としては成功しません。  
それは検索のキーワード指定を以下のように検索ボックスへのキー入力エミュレーション(`send_keys(data)`と`submit()`)で行っているからでしょう。

[code]

        search = driver.find_element_by_name('q')   # HTML内で検索ボックス(name='q')を指定する
        search.send_keys(data)             # 検索ワードを送信する
        search.submit()                             # 検索を実行
    
[/code]

対策としては色々あるでしょうが、取り敢えずは例として以下の2つが考えられます。

  * urlパラメータ文字列として `r'https://www.google.com/search?q=' + data` を作成し、上記3行の代わりに`driver.get()`に指定して呼び出す。
  * `for`ループの先頭で`search = driver.find_element_by_name('q')`の直前に`driver.get('https://www.google.com/')`を呼んで毎回初期状態から始める。


CC BY-SA 4.0
2021-10-30T17:10:13.363
83345
2021-10-30T17:10:13.363
26370
83332
answer


最低限の変更で修正するとすれば以下の様になります。

###### diff -u scraping.py.org scraping.py

[code]

    --- scraping.py.org
    +++ scraping.py
    @@ -11,7 +11,8 @@
     file = open('input.txt', 'r',encoding="utf-8")  #読み込みモードでオープン
     for data in file:
         search = driver.find_element_by_name('q')   # HTML内で検索ボックス(name='q')を指定する
    -    search.send_keys(data)             # 検索ワードを送信する
    +    search.clear()
    +    search.send_keys(data.strip())     # 検索ワードを送信する
         search.submit()                             # 検索を実行
         def ranking(driver):
             i = 1               # ループ番号、ページ番号を定義
    @@ -28,12 +29,12 @@
                         link_list.append(elem.find_element_by_tag_name('a').get_attribute('href'))  #リンク(aタグのhref属性)
                 # 「次へ」は1つしかないが、あえてelementsで複数検索。空のリストであれば最終ページの意味になる。
                 if driver.find_elements_by_id('pnnext') == []:
    -                i = i_max + 1
    +                break
                 else:
                     # 次ページのURLはid="pnnext"のhref属性
                     next_page = driver.find_element_by_id('pnnext').get_attribute('href')
                     driver.get(next_page)   # 次ページへ遷移する
    -                i = i + 1               # iを更新
    +                i += 1                  # iを更新
                     time.sleep(10)           # 10秒間待機
             return title_list, link_list    # タイトルとリンクのリストを戻り値に指定
         # ranking関数を実行してタイトルとURLリストを取得する
    @@ -44,4 +45,6 @@
         # URLリストをテキストに保存
         with open('link__output.txt', mode='a', encoding='utf-8') as f:
             f.write("\n".join(link))
    -    driver.quit()                               # ブラウザを閉じる
    +
    +driver.quit()                               # ブラウザを閉じる
    +file.close()
    
[/code]

`file` インスタンスはイテレータでもあるので for
文で一行づつ取り出す事が可能ですが、改行コード(`newline(0x0a)`)が付いたままになるので、それを取り除いておきます。また、検索ワードが変更される毎に検索ボックスの内容をクリアしています。

※ `for data in file: 〜` の内部で `ranking` 関数を定義しているのは不可解ではありますけれども


CC BY-SA 4.0
2021-10-30T21:02:39.800
83346
2021-10-31T05:16:05.407
2021-10-31T05:16:05.407
47127
47127
83332
answer