본문 바로가기

Fast API를 이용한 인스타그램 크롤러 만들기

ironwhale 2021. 7. 28.

컴퓨터에서 파이썬 코드를 작성하여 크롤링 하는 프로그램을 만들어서 혼자 사용하는 것은 쉽다.
하지만 프로그램을 만들면 배포하여 어디서든 사용하고 싶은 욕구가 생기기 마련이다.
FASTAPI와 구글클라우드플래폼(GCP)를 이용하여 인스타그램 사진을 크롤링 하는 프로젝트를 진행해보았다.
결과적으로 80%정도는 만족스러운 결과를 얻었다.

나머지 20% 부족한 점은 두가지가 있다.

  1. 아무래도 실시간으로 인스타그램 크롤링하는 진행사항을 확인할 수 없다.
  2. 지속적인 크롤링으로 인스타그램에서 아이디와 IP를 차단 당한다.
    이부분은 time.sleep()으로 딜레이를 줘도 인스타의 차단을 막을순 없었다.

아래는 개발 중 이슈가 되었던 것과 해결과정을 작성한것 입니다.

인스타그램 크롤링 관련 이슈

  • 컴퓨터 사양에 따라 크롤링 성능은 다르다
  • 접속 시간 등 같은 도커 환경이어도 GCP에서는 안되고 함 특히 메모리 문제로 reachable 에러가 남
  • 커밋 add time: 나중에 하기 쪽에서 문제가 발생하여 대기 시간을 120초로 늘리고 시간 측정 코드를 입력함
    • 내 노트북으로 테스트 시 7.33초
    • 내 노트북으로 도커에서 테스트시 12초
    • 테스트 할때마자 접속 시간이 다르다

    2021년 7월 11일 인스타그램 크롤링 리뷰

  • 커밋 add time 테스트
  • 컴퓨터, 컴퓨터의 도커, GCP 모두 같은 환경이라고 생각했지만 확인해보니 다르다.
    그래서 각 환경별로 정확하게 하기 위해 스크린샷을 찍어야 한다.
    driver.save_screenshot("Logging_in.png") 으로 하고 png로 밖에 안된다.
  • 포스트로 json을 날릴때 맨 마지막 항목에 ,가 있으면 안된다.
  • 셀레니움은 적절한 타이밍에 driver.quit()을 해줘야 메모리를 아낄수 있다.
  • 셀레니움 등 크롤링 시간은 할때마다 다르다.
  • rm -r 폴더명 폴더 삭제법
  • 갑자기 잘된게 안되면 비정상적 로그인을 확인해라
  • GCP에서 안된 이유
  • 비정상적인 로그인 시도 감지에서 안넘어감
  • 나중에 하기가 영어로 됨-> 한글화면 또는 Not Now 추가
    WebDriverWait(driver, 120).until(EC.visibility_of_element_located((By.XPATH, "//button[contains(text(),'나중에') or contains(text(), 'Not Now')]"))).click()
    options.add_argument("lang=ko_KR")

로컬에서 도커 다 되는데 GCP에서 안되서 다른 GCP 인스턴스로 하니까 됨....

스크린샷으로 발견->비정상적인 로그인 시도 감지 때문에 GCP에서 안된것으로 추정됨

Xpath contains 활용

  • //*[contains(@속성,'btv')]
  • //*[contains(text(),'btv')]
    - driver.find_element_by_xpath("//button[contains(@class, '6CZ')]/div").click()
    - WebDriverWait(driver, 60).until(EC.visibility_of_all_elements_located((By.XPATH, "//img[contains(@srcset,'1080x1080')]"))) 
    - WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.XPATH, '//*[contains(@href, "/p/")]')))  
    - '//*[contains(@href, "/p/") or contains(text(),'나중에')]'

     

  • 이미지 로컬에 저장하고 템플릿으로 불러오기

    사전 작업 : pip install aiofiles

  • contains를 활용해라

서버에 로컬 경로를 마운트

    app.mount("/photos", StaticFiles(directory=f"../photo/{user_id}/"), name="photo")
  • app.mount("서버에 저장될 경로", StaticFiles(directory=로컬에 저장된 경로), name=템플릿 네이밍 url_for로 접근가능)

템플릿으로 넘겨주는 방법

  • 그냥 filtered_data = db.query(model.PhotoPath).filter(model.PhotoPath.user_id==user_id).all() 필터링 해서 리스트로 넘겨도 됨
  @app.get("/photo/{user_id}")
   async def get_photo_by_id(request:Request, user_id:str):
      app.mount("/photos", StaticFiles(directory=f"../photo/{user_id}/"), name="photo")
      filtered_data = db.query(model.PhotoPath).filter(model.PhotoPath.user_id==user_id).all() 
      return templates.TemplateResponse("all_photos.html",{"request":request,**"filtered_data":filtered_data**})
  • 넘겨준 리스트형은 {{ filtered_data[0].user_id }} 접근가능하다
  • 이미지 아이디 {{ filtered_data[0].user_id }}
  • 마운트한 이미지는 "{{ url_for('photo', path= data.file_name ) }}" 로 사용 가능하다
  • {% for data in filtered_data %} <div class = "row"> <div class = "col-sm-12" id = col1> **<img src= "{{ url_for('photo', path= data.file_name ) }}" class="img-fluid">** </div> </div> {% endfor %}

댓글