끄적거림

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 4(feat. selenium) 본문

Python/Crawling

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 4(feat. selenium)

Signing 2020. 7. 28. 12:55
728x90
반응형

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 1(feat. selenium)

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 2(feat. selenium)

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 3(feat. selenium)

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 4(feat. selenium)

[리뷰 크롤링] PlayStore 댓글 크롤링하기 in python 5(feat. selenium)

 


 

다 된 줄 알았는데 보니까 그림처럼 펼쳐보기식의 버튼이 있음을 발견했다....

리뷰 전체 보기 버튼

쩝... 데이터 정합성과 신뢰도를 중요하게 생각하는 필자 입장에서는 이런것들도 매우 거슬린다..

 

어쨌든 문제를 발견했으니 해결하도록 노력해보자!!

 

 

 

 

1. 문제 정의

문제는 정의되었다.

: 중간 중간 껴있는 "전체 리뷰" 버튼을 모두 클릭하여 전체 리뷰를 긁어와야한다.

 

 

 

 

2. [전체 리뷰] 버튼 구조

그림과 같이 [전체 리뷰] 버튼의 구조와 위치를 확인 할 수 있다.

[전체 리뷰] 버튼

 

 

 

3. 버튼 조작

지난 시간에 했던대로 버튼의 위치와 구조를 파악했으니 xpath를 이용해 버튼을 클릭할 수 있었다.

tmp = driver.find_element_by_xpath("//button[@jsaction='click:TiglPc']")
tmp.click()

테스트성으로 처음 하나만 실행해보았는데 잘 되었다.

 

머야 쉽자나? 라고 생각하고 메크로를 돌려서 모든 리뷰 데이터를 로딩하여 아래의 코드를 실행시켜보았다.

spread_review = driver.find_elements_by_xpath("//button[@jsaction='click:TiglPc']")
spread_review.click()

안된다.

 

왜 안될까?ㅠㅠ

에러문구는 다음과 같았다.

다중 클릭 에러

쉽게 말해서, [전체 보기] 버튼을 갖고 있는 리뷰들의 집합인 리스트가 click이라는 함수를 갖고 있지 않기 때문이라고 한다.

 

그러면 쉽게 생각해볼 수 있는게 루프문을 이용해서 각각 실행해주면 되겠네?였다.

spread_review = driver.find_elements_by_xpath("//button[@jsaction='click:TiglPc']")
spread_review

for i in range(len(spread_review)):
    isTrue = spread_review[i].is_displayed()    # 보이는 것인지를 확인
    print("Element is visible? " + str(isTrue))
    if isTrue:
        spread_review[i].click()
        print(str(i)+"th more button is clicked and wait 2 secs...")
        time.sleep(2)

찾아보니 이런 에러 메세지(Message: element not interactable)가 나타나는 상황은 두 가지 상황이라고 했다.

  • 클릭할 수 없거나
  • 보이지 않을 때

그렇기 때문에 해당 element가 보이지는지를 확인하기 위한 함수 is_displayed( )를 사용했다.

사실 굳이 사용안해도 되긴된다. 이미 보이긴 하니까..

무튼 그렇게해서 [전체 리뷰]버튼을 모두 클릭할 수 있는 상황이 되었고 그에 대한 결과는 다음과 같다.

[전체 리뷰] 버튼 클릭 후

잉..? 기존에 안보이던 리뷰와 전부 보이는 리뷰를 모두 발견할 수 있었다.

 

이것이 버튼을 클릭함으로써 발생한 것인가? 하는 궁금증이 들어 새로고침하고 다시 해보니 이미 기존에 두가지 버전이 존재하고 있었다..(처음부터 이걸 알았으면 좋으련만..)

 

보아하니 short 버전과 full 버전은 html 구조와 위치도 다르다.

반면, 제한 글자를 넘지 않는 글(윗 글) 같은 경우 단일 버전으로만 존재한다.

 

하지만, 버튼을 클릭해야 full버전의 리뷰를 긁어올 수 있다. 그러므로 위의 3번 동작을 진행하고 이어가면 될 것 같다.

<span jsname='fbQN7e style='display: none'>

여기에서 'display: none'부분은 [전체 리뷰] 버튼을 클릭하는 순간 사라진다. 그리고 나서 스크래핑이 가능해진다.

 

 

4. 문제 해결 후 새로운 트러블 슈팅

1) 상위 태그 접근( X )

그럼 그 상위 태그로 하면 여전히 short 버전으로 스크래핑 되는 것을 확인할 수 있다. 실패다.

reviews = driver.find_elements_by_xpath("//div[@jscontroller='LVJlx']")
reviews[8].text

 

 

2) 첫 스크래핑과 [전체 리뷰] 버튼 클릭 후 두 번째 스크래핑( O )

원하는 부분까지의 리뷰까지 도달하고나서(전 포스팅 참고) 모든 [전체 리뷰] 버튼을 클릭한다.

[전체 리뷰] 버튼 클릭하는 모습

 

그 후 짧은 리뷰와 긴 리뷰를 따로 스크래핑한다.

reviews = driver.find_elements_by_xpath("//span[contains(@jsname, 'bN97Pc')]")
for i in range(len(reviews)):
    print(str(i) + "\t" + reviews[i].text)
    
long_reviews = driver.find_elements_by_xpath("//span[@jsname='fbQN7e']")
for i in range(len(reviews)):
    print(str(i) + "\t" + long_reviews[i].text)

짧은 리뷰와 긴 리뷰

그림에서 보이듯이 짧은 리뷰에서는 긴 리뷰가 빠져있고, 긴 리뷰에서는 짧은 리뷰가 빠져있다.(배반사건 마냥..)

 

empty = [i for i,t in enumerate(reviews) if t.text=='']
long = [i for i,t in enumerate(long_reviews) if t.text!='']
empty == long    # True

 

그러면 두 데이터를 하나의 리스트로 합쳐보자.

 

# merge two list
merged_review = [t.text if t.text!='' else long_reviews[i].text for i,t in enumerate(reviews)]
merged_review

합쳐진 리스트

그림과 같이 두 데이터가 합쳐졌다.

(합칠 때 comprehension과 enumerate를 사용하여 합쳤는데 간단한 로직이라고 생각했는데 혹시 더 좋은 아이디어가 있으면 댓글로 알려주심 감사하겠습니다ㅎㅎ)

 


 

이렇게 [전체 리뷰] 문제를 해결할 수 있었다.

이제 이 데이터와 나머지 내가 원했던 데이터를 합쳐서 dataframe으로 만들어 보기 좋게 만들어보면 아래와 같다.

(이전의 포스팅 참고)

 

 

최종 데이터

빨간색으로 밑줄 친 것처럼 원래는 그 부분이 숨겨져 있었지만 이제는 모든 완전한 문장단위의 데이터를 얻을 수 있었다.

이제 텍스트 마이닝을 진행해볼까한다.

 

텍마는 다음 포스팅에서...ㅎㅎ

 

728x90
반응형
Comments