

Seleniumの勉強のためRubyでinstagramのWeb自動ツールの開発をしています。  
この中で、投稿にコメントを書き込むスクリプトの中で

  * コメント入力欄
  * 「投稿する」ボタン

の2つを取得するコードを書いています…が、なかなかきれいにまとまらなくて困っています。

**コメント欄・投稿するボタンのHTMLコード**

[![コメント欄・投稿するボタンのHTMLコード](https://i.stack.imgur.com/Fn4rd.png)](https://i.stack.imgur.com/Fn4rd.png)

instagramは定期的にHTMLの構造を細かく変更することで知られているため、単純に操作対象のタグを取得するコードを書くだけでなく、if文でそのタグが本当に存在しているのかどうかを確認する必要があります。

そのため、

  * formタグをclass値で取得できる場合は、formタグを取得した後にさらにfind_elementでtextareaタグ、buttonタグを取得
  * 上記の方法が成功できなかった場合は直接textarea、buttonのタグを取得する。  
※textareaはclassが唯一の存在であるが、buttonについては他にも同様のclassが使われている箇所があるので、念の為xpathを使って取得する

  * このどちらの方法でもタグを取得しきれなかった場合にはエラーを返す

というコードを書いてみました。

[code]

    def get_path_by_form
        comment_form = ''
        comment_textarea = ''
        comment_submit_button = ''
        if @driver.find_elements(COMMENT_FORM_ELEMENT[0], COMMENT_FORM_ELEMENT[1]).size > 0
            comment_form = @driver.find_element(COMMENT_FORM_ELEMENT[0], COMMENT_FORM_ELEMENT[1])
            # コメント入力欄のオブジェクト取得
            if comment_form.find_elements(:tag_name, 'textarea').size > 0
                comment_textarea = comment_form.find_element(:tag_name, 'textarea')
            end
            # 投稿ボタンのオブジェクト取得 buttonもしくはspanの可能性がある
            if comment_form.find_elements(:tag_name, 'button').size > 0
                comment_submit_button = comment_form.find_element(:tag_name, 'button')
            elsif comment_form.find_elements(:tag_name, 'span').size > 0
                comment_submit_button = comment_form.find_element(:tag_name, 'span')
            else
                send_error_mail
            end
        end
        return comment_form, comment_textarea, comment_submit_button
    end
    
    def get_path_indivisual
        comment_form = ''
        comment_textarea = ''
        comment_submit_button = ''
        if @driver.find_elements(COMMENT_TEXTAREA_ELEMENT[0], COMMENT_TEXTAREA_ELEMENT[1]).size > 0
            comment_textarea = @driver.find_element(COMMENT_TEXTAREA_ELEMENT[0], COMMENT_TEXTAREA_ELEMENT[1])
            if @driver.find_elements(COMMENT_SUBMIT_ELEMENT[0], COMMENT_SUBMIT_ELEMENT[1]).size > 0
                comment_submit_button = @driver.find_element(COMMENT_SUBMIT_ELEMENT[0], COMMENT_SUBMIT_ELEMENT[1])
            end
        end
        return comment_form, comment_textarea, comment_submit_button
    end
    
[/code]

ただ、このコードだとネストが深くなってしまいがちでものすごく読みにくいです。  
また、例えば、formから各オブジェクトを取得する処理に入った場合に、途中でオブジェクトを取得できないことがわかった場合に、もう一方の処理に切り替えることができません。  
※例えば、comment_textareaのオブジェクトは取得できたのに、comment_submit_buttonのオブジェクトは取得できなかった場合、もう一方のclass値やxpathで取得する方法に切り替えたいが、全部書いてしまうとかなりコードが複雑になる。

それぞれのコードを関数にまとめてみる方法もおそらくこんな感じだろう。。。と思い書いてみました。

[code]

    @comment_form = ''
    @comment_textarea = ''
    @comment_submit_button = ''
    
    get_object_by_form
    get_object_by_single
    if @comment_form != '' || @comment_textarea != '' || @comment_submit_button != ''
        raise
    end
    
    def get_object_by_form
        @driver.find_elements(:class, 'X7cDz').size > 0
        @comment_form = @driver.find_element(:class, 'X7cDz')
        # コメント入力欄のオブジェクト取得
        if @comment_form.find_elements(:tag_name, 'textarea').size > 0
            @comment_textarea = comment_form.find_element(:tag_name, 'textarea')
        end
        # 投稿ボタンのオブジェクト取得 buttonもしくはspanの可能性がある
        if @comment_form.find_elements(:tag_name, 'button').size > 0
            @comment_submit_button = comment_form.find_element(:tag_name, 'button')
        elsif @comment_form.find_elements(:tag_name, 'span').size > 0
            comment_submit_button = comment_form.find_element(:tag_name, 'span')
        else
            raise
        end
    end
    
    def get_object_by_single
        @driver.find_elements(:class, 'Ypffh').size > 0
        @comment_textarea = @driver.find_element(:class, 'Ypffh')
        if @driver.find_elements(:xpath, '/html/body/div[3]/div[2]/div/article/div[2]/section[3]/div/form/button').size > 0
            @comment_submit_button = @driver.find_element(:xpath, '/html/body/div[3]/div[2]/div/article/div[2]/section[3]/div/form/button')
        end
    end
    
[/code]

これも、オブジェクトの取得確認のif文のところがわかりにくい、各関数内のif文が少し複雑で、かつ、変更があった場合に修正が難しいのかな、と思ってしまいます。

コードの読みやすさの問題については個人の感性などもあるかと思いますので正解はないかと思いますが、より良いコードにするためにはどのようにまとめるべきかアドバイスを頂きたいです。よろしくお願い致します。


CC BY-SA 4.0
2019-04-20T08:24:59.857
54336
2020-08-04T04:13:43.813
2020-08-04T04:13:43.813
3060
33001
question
ruby selenium selenium-webdriver instagram
条件分岐が多くなりすぎてネストが深くなってしまったコードをスッキリさせたい


テスト対象のページを見てないので何とも言えないのですが、CSSセレクタやXPathを工夫すればユニークなロケータが作れそうな気がします。

[code]

    # アクセシビリティラベルが 'コメントを追加...' のものを取得
    driver.find_element(:class, 'textarea[aria-label=コメントを追加...]')
    
    # buttonまたはspan要素のうち、 '投稿する' を含むものを取得
    driver.find_element(
      :xpath, 
      '//*[self::button or self::span][contains(text(), 投稿する)]'
    )
    
[/code]


CC BY-SA 4.0
2019-04-20T09:49:48.283
54337
2019-04-20T09:49:48.283
24918
54336
answer