GPS 위치 정보 설정 : Emulation.setGeolocationOverride

Sangyeon·2022년 3월 6일
0

Selenium

목록 보기
2/4
post-thumbnail

Selenium4 부터 CDP(Chrome DevTools Protocol) API를 사용하여 GPS 위치를 조작하는 게 가능합니다.

위치 서비스 활성화


우선, OS에서 GPS 위치 설정하려는 브라우저에 대해 위치 서비스 활성화가 되어있어야 합니다.

MacOS의 경우, 시스템 환경설정 > 보안 및 개인 정보 보호 > 개인 정보 보호 > 위치 서비스 에서 설정할 수 있습니다.

Selenium 설정

내 위치 확인 허용

브라우저 상에서 위 팝업창이 뜨지 않도록 크롬 웹 드라이버의 옵션을 설정해야합니다.(참고 링크)

selenium_func.py

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


def set_chrome_driver():
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_experimental_option("prefs", {
        "profile.default_content_setting_values.geolocation": 1  # 값이 1이면 허용, 2이면 차단
    })
    driver = webdriver.Chrome(service=Service(ChromeDriverManager(log_level=40).install()), options=chrome_options)
    return driver

참고로, 시크릿모드(Incognito mode)가 활성화되어 있는 경우, 위 옵션을 사용할 수 없습니다. 시크릿모드 비활성화 상태에서 크롬 웹 드라이버를 구동해야 합니다.(링크)

위치 설정

GPS 위치 정보를 조작하려면, 좌표정보(위도/경도)와 정확도를 딕셔너리에 담아 CDP(Chrome DevTools Protocol) API에서 Emulation.setGeolocationOverride 명령어를 실행해야 합니다.

GPS 위치 기반으로 지도에서 위치 정보를 알려주는 사이트(https://www.gps-coordinates.net/)에서 임의로 San Francisco의 좌표정보(위도/경도)와 정확도를 넣어 실행해보았습니다.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import selenium_func


if __name__ == '__main__':
    driver = selenium_func.set_chrome_driver()
    try:
        # Create Geo Location
        coordinates = dict({
            "latitude": 37.774929,  # 위도
            "longitude": -122.419416,  # 경도
            "accuracy": 100  # 정확도
        })

        # Emulate Location
        driver.execute_cdp_cmd("Emulation.setGeolocationOverride", coordinates)

        # Navigate to the Website
        driver.get("https://www.gps-coordinates.net/")
        driver.execute_script("arguments[0].scrollIntoView();", driver.find_element(By.ID, 'map_canvas'))
        location_name = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'iwtitle'))).get_attribute('innerText')
        print(location_name)
    finally:
        driver.quit()

실행 결과, 아래와 같이 해당 좌표값을 가진 위치 정보가 출력되었습니다.

Cistern at Kansas & Army, Market Street, San Francisco, CA 94102, United States of America

위 수행 과정은 크롬 브라우저 상에서 크롬 개발자 도구 > More Tools > Sensors에서 위치를 San Francisco로 설정하여 확인한 것과 동일합니다.

테스트 예제

이제 여러 지역의 GPS 위치 정보를 설정하여 테스트에 적용해봅니다.
대한민국 행정구역 17군데의 GPS 위치 정보를 설정하여 https://www.gps-coordinates.net/ 에서 해당 위치 정보에 행정구역명이 포함되어 있는지 확인하는 테스트를 진행해볼 것입니다.

지역의 좌표정보가 담긴 파일 준비

원래는 링크를 참고하여 지도 데이터가 담긴 shp 파일을 읽은 후, 원하는 형태에 맞게 바꿔서 사용하려고 했으나, shp 파일을 다루는게 복잡하여 여기로부터 대한민국 행정구역 17군데의 좌표정보(위도/경도)가 담긴 json 파일을 다운로드하였습니다.
그런데 다운받은 json 파일의 좌표정보가 지역과 맞지않는 경우가 있어 링크를 참고하여 구글맵에 지역을 검색하여 위도,경도 값을 찾아 json 파일을 재구성해보았습니다.

geolocation.json

{
  "regions": [
    {
      "properties":{
        "CTPRVN_CD":"41",
        "CTP_ENG_NM":"Gyeonggi-do",
        "CTP_KOR_NM":"경기도"
      },
      "geometry": {
        "coordinates": {
          "latitude": 37.5969947,
          "longitude": 126.5354486
        }
      }
    },
    {
      "properties":{
        "CTPRVN_CD":"11",
        "CTP_ENG_NM":"Seoul",
        "CTP_KOR_NM":"서울특별시"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": {
          "latitude": 37.5650172,
          "longitude": 126.8494645
        }
      }
    },
    (이하 생략)
  ]
}

GeolocationJSON.py : geolocation.json 파일로부터 각 지역별 좌표정보가 담긴 딕셔너리(region_dict)를 구성합니다. 접근하는 사이트가 영어 사이트라, 지역명은 영어 지역명을 사용합니다.

import json


class GeolocationJSON:
    def __init__(self):
        with open('geolocation.json') as jf:
            json_data = json.load(jf)
            regions = json_data["regions"]
            self._region_dict = dict()
            for region in regions:
                region_name = region['properties']['CTP_ENG_NM']  # 영어 지역명
                self._region_dict[region_name] = {
                    "latitude": region['geometry']['coordinates']["latitude"],  # 위도
                    "longitude": region['geometry']['coordinates']["longitude"],  # 경도
                    "accuracy": 100
                }

    @property
    def region_name_list(self):
        return list(self._region_dict.keys())

    def coordinate_dict(self, region_name):
        return self._region_dict[region_name]

테스트 코드

conftest.py : 테스트 시 사용할 webdriver를 세팅합니다.(selenium_func.py는 윗 내용의 Selenium 설정에 코드가 있습니다.)

import pytest
import selenium_func


@pytest.fixture(scope="module")
def driver():
    driver = selenium_func.set_chrome_driver()
    yield driver
    driver.quit()

test_geolocation.py : 테스트 코드를 작성합니다. 지역명으로 매개변수화하여 대한민국 행정구역 총 17곳에 대해 테스트를 진행합니다.

from GeolocationJSON import GeolocationJSON
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pytest
region_name_list = GeolocationJSON().region_name_list


@pytest.mark.parametrize('region_name', region_name_list)
def test_region_name(driver, region_name):
    coordinate_dict = GeolocationJSON().coordinate_dict(region_name=region_name)
    driver.execute_cdp_cmd("Emulation.setGeolocationOverride", coordinate_dict)

    url = "https://www.gps-coordinates.net/"
    driver.get(url)
    driver.execute_script("arguments[0].scrollIntoView();", driver.find_element(By.ID, 'map_canvas'))
    location_name = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'iwtitle'))).get_attribute('innerText')
    assert region_name in location_name

실행 결과

실행 결과, 총 17건 중 7 Passed, 10 Failed 되었습니다.
(생각보다 해당 사이트의 위치 정보에 행정구역명이 들어가는 경우가 별로 없었네요.. 행정구역의 위도/경도 값을 잘못 고른건가 싶습니다..)

다수 지역의 GPS 위치 조작이 잘 되는지 확인해보려고 해본 간단한 예제였는데, GPS 위치 정보를 설정하고, 기대결과에 맞게 서비스 화면에 보여지는지 확인하는데 유용할 것 같습니다 :)

Reference

profile
I'm a constant learner.

0개의 댓글