AutoScraper를 이용한 웹 스크래핑 튜토리얼

AutoScraper는 초보자도 쉽게 사용할 수 있는 Python 도구로, 최소한의 코딩으로 구조화된 웹사이트와 동적 웹사이트를 처리하여 웹 스크래핑을 단순화합니다.
12 분 읽기
Web Scraping with AutoScraper

AutoScraper는 수동 HTML 검사 없이 웹사이트에서 데이터를 자동으로 식별 및 추출하여 웹 스크래핑을 간소화하는 Python 라이브러리입니다. 기존 스크래핑 도구와 달리 AutoScraper는 예제 쿼리를 기반으로 데이터 요소 구조를 학습하므로 초보자와 숙련된 개발자 모두에게 탁월한 선택입니다. 제품 정보 수집, 콘텐츠 집계, 시장 조사 수행과 같은 작업에 이상적인 AutoScraper는 복잡한 설정 없이 동적 웹사이트를 효율적으로 처리합니다.

이 글에서는 Python과 함께 AutoScraper를 사용하여 웹 스크래핑하는 방법을 배울 수 있습니다.

필수 조건

AutoScraper 설정은 간단합니다. 물론 로컬에 Python 3 이상 버전이 설치되어 있어야 합니다. 다른 Python 웹 스크래핑 프로젝트와 마찬가지로, 프로젝트 디렉터리를 생성하고 가상 환경을 생성 및 활성화하기 위해 몇 가지 명령만 실행하면 됩니다:

# 프로젝트 디렉터리 설정
mkdir auto-scrape
cd auto-scrape

# 가상 환경 생성
python -m venv env
# Mac 및 Linux 사용자용
source env/bin/activate
# Windows 사용자용
venvScriptsactivate

가상 환경을 사용하면 프로젝트 내 의존성 관리를 단순화할 수 있습니다.

다음으로, 다음 명령어를 실행하여 autoscraper 라이브러리를 설치하세요:

pip install autoscraper

스크래핑 결과를 최종적으로 CSV 파일로 저장하려면 pandas도 설치해야 합니다. pandas는 사용하기 쉬운 데이터 분석 및 조작 도구를 제공하는 Python 라이브러리입니다. 이를 통해 스크래핑된 결과를 CSV, XLSX, JSON 등 다양한 형식으로 쉽게 처리하고 저장할 수 있습니다. 다음 명령어를 실행하여 설치하세요:

pip install pandas

대상 웹사이트 선택

공개 웹사이트를 스크래핑할 때는 반드시 해당 사이트의 이용약관(ToS)이나 robots.txt 파일을 확인하여 스크래핑이 허용되는지 확인하세요. 이는 법적 또는 윤리적 문제를 피하는 데 도움이 됩니다. 또한 테이블이나 목록과 같이 구조화된 형식으로 데이터를 제공하는 웹사이트를 선택하는 것이 추출이 더 쉽습니다.

기존 스크래핑 도구는 대상 데이터 요소를 찾기 위해 웹 페이지의 HTML 구조를 분석해야 하는 경우가 많습니다. 이는 시간이 많이 소요되며 브라우저 개발자 콘솔 같은 도구에 익숙해야 합니다. 그러나 AutoScraper는 예제 데이터( wanted_list라고도 함)를 기반으로 데이터 구조를 자동으로 학습하여 이 단계를 간소화하므로 수동 검사가 필요하지 않습니다.

이 튜토리얼에서는 스크래핑 도구 테스트용으로 설계된 초보자 친화적 샌드박스인 ‘Scrape This Site’의 ‘세계 국가들: 간단한 예시’ 페이지에서 데이터 스크래핑을 시작합니다. 이 페이지는 기본적인 스크래핑 기법 시연에 이상적인 직관적인 구조를 가집니다. 기본 구조를 숙지한 후에는 더 복잡한 레이아웃을 가진 ‘하키 팀: 선수 명단, 검색 및 페이지네이션’ 페이지로 넘어갑니다.

AutoScraper로 간단한 데이터 스크래핑하기

스크래핑할 두 페이지를 확인했으니 이제 본격적으로 시작해 보겠습니다!

‘세계 국가: 간단한 예시’ 페이지는 구조가 단순하므로, 다음 스크립트를 사용해 국가 목록과 함께 수도, 인구, 면적을 스크래핑할 수 있습니다:

# 1. 종속성 임포트
from autoscraper import AutoScraper
import pandas as pd

# 2. 스크래핑 대상 사이트 URL 정의
url = "https://www.scrapethissite.com/pages/simple/"

# 3. AutoScraper 인스턴스 생성
scraper = AutoScraper()

# 4. 웹 페이지 예시를 활용해 원하는 목록 정의
# 이 목록에는 스크래핑할 텍스트나 값이 포함되어야 함
wanted_list = ["Andorra", "Andorra la Vella", "84000", "468.0"]

# 5. 원하는 목록과 URL을 기반으로 스크레이퍼 생성
scraper.build(url, wanted_list)

# 6. 일치하는 모든 요소에 대한 결과 가져오기
results = scraper.get_result_similar(url, grouped=True)

# 7. 구조 이해를 위한 키 및 샘플 데이터 표시
print("스크레이퍼가 찾은 키:", results.keys())

# 8. 스크레이퍼 키와 예상 데이터 순서에 따라 열 할당
columns = ["국가명", "수도", "면적 (㎢)", "인구"]

# 9. 추출된 데이터로 DataFrame 생성
data = {columns[i]: results[list(results.keys())[i]] for i in range(len(columns))}
df = pd.DataFrame(data)

# 10. DataFrame을 CSV 파일로 저장
csv_filename = 'countries_data.csv'
df.to_csv(csv_filename, index=False)

print(f"데이터가 {csv_filename}에 성공적으로 저장되었습니다")

이 코드에는 진행 과정을 설명하는 인라인 주석이 포함되어 있지만, 간단히 요약하면 다음과 같습니다: 스크립트는 AutoScraper와 pandas를 임포트하는 것으로 시작합니다. 다음으로 대상 웹사이트의 URL을 정의합니다. 그런 다음 스크레이퍼의 인스턴스를 생성합니다.

이제 흥미로운 부분이 시작됩니다: 웹사이트에서 대상 데이터가 있는 위치를 스크레이퍼에 상세히 지시하는 대신(다른 스크레이퍼에서는 XPath나 다른 선택자를 통해 수행할 수 있음), 단순히 찾고자 하는 데이터의 예시를 제공합니다. 네 번째 주석 아래에서, 한 국가의 데이터 포인트가 배열( wanted_list라고도 함) 형태로 스크레이퍼에 제공됩니다.

wanted_list가 준비되면 URL과 wanted_list를 사용해 스크레이퍼를 생성합니다. 스크레이퍼는 대상 웹사이트를 다운로드하고 스택 리스트에 저장할 규칙을 생성합니다. 이후 이 규칙들을 활용해 향후 어떤 대상 URL에서도 데이터를 추출합니다.

여섯 번째 코멘트 아래 코드에서는 AutoScraper 모델의 get_result_similar 메서드를 사용하여 원하는 목록의 데이터와 유사한 데이터를 대상 URL에서 추출합니다. 다음 줄은 대상 URL에서 데이터가 발견된 규칙의 ID를 표시하는 간단한 출력문입니다. 출력 결과는 다음과 같아야 합니다:

스크레이퍼가 찾은 키: dict_keys(['rule_4y6n', 'rule_gghn', 'rule_a6r9', 'rule_os29'])

8번과 9번 주석 아래 코드는 CSV 파일의 헤더 스키마를 생성하고 추출된 데이터를 pandas DataFrame 형식으로 포맷합니다. 마지막으로 10번 주석 아래 코드는 데이터를 CSV로 저장합니다.

이 스크립트를 실행하면(이전 스크립트를 script.py 파일로 저장하고 명령줄에서 python script.py 실행), 프로젝트 디렉터리에 countries_data.csv라는 새 파일이 생성되며 내용은 다음과 같습니다:

국가명,수도,면적(㎢),인구
안도라,안도라 라 벨라,84000,468.0
아랍에미리트,아부다비,4975593,82880.0
...246개 행 생략
잠비아,루사카,13460305,752614.0
짐바브웨,하라레,11651858,390580.0

이게 다입니다! AutoScraper로 간단한 웹사이트를 스크래핑하는 것이 이렇게 쉽습니다.

복잡한 디자인의 웹사이트에서 데이터 처리 및 추출

하키 팀: 양식, 검색 및 페이지 매김 페이지처럼 약간 더 복잡한 웹사이트의 경우, 비슷한 값이 많은 테이블이 포함되어 있어 앞서 보여드린 기술이 실패할 수 있습니다. 동일한 방법을 사용해 이 웹사이트에서 팀명, 연도, 승수, 패수 및 기타 필드를 추출해 보시면 문제를 직접 확인하실 수 있습니다.

다행히 AutoScraper는 모델을 사용하여 데이터를 추출하기 전 빌드 단계에서 수집된 규칙을 정제함으로써 더 정교한 모델 훈련을 지원합니다. 이를 위한 코드는 다음과 같습니다:

from autoscraper import AutoScraper
import pandas as pd

# 스크래핑 대상 사이트의 URL 정의
url = "https://www.scrapethissite.com/pages/forms/"

def setup_model():

    # AutoScraper 인스턴스 생성
    scraper = AutoScraper()

    # 웹 페이지의 예시를 활용하여 원하는 목록 정의
    # 이 목록에는 스크래핑하려는 텍스트나 값이 포함되어야 함
    wanted_list = ["Boston Bruins", "1990", "44", "24", "0.55", "299", "264", "35"]

    # 원하는 목록과 URL을 기반으로 스크레이퍼 생성
    scraper.build(url, wanted_list)

    # 일치하는 모든 요소에 대한 결과 가져오기
    results = scraper.get_result_similar(url, grouped=True)

    # 구조 이해를 위한 데이터 표시
    print(results)

    # 모델 저장
    scraper.save("teams_model.json")

def prune_rules():
    # Autoscraper 인스턴스 생성
    scraper = AutoScraper()
    
    # 이전에 저장한 모델 불러오기
    scraper.load("teams_model.json")

    # 필요한 규칙만 유지하도록 모델 업데이트
    scraper.keep_rules(['rule_hjk5', 'rule_9sty', 'rule_2hml', 'rule_3qvv', 'rule_e8x1', 'rule_mhl4', 'rule_h090', 'rule_xg34'])

    # 업데이트된 모델 다시 저장
    scraper.save("teams_model.json")
    
def load_and_run_model():
    # Autoscraper 인스턴스 생성
    scraper = AutoScraper()
    
    # 이전에 저장한 모델 로드
    scraper.load("teams_model.json")

    # 일치하는 모든 요소에 대한 결과 가져오기
    results = scraper.get_result_similar(url, grouped=True)

    # 스크레이퍼 키와 예상 데이터 순서에 따라 열 할당
    columns = ["팀명", "연도", "승리", "패배", "승률", "득점(GF)", "실점(GA)", "득실차(G/A)"]

    # 추출된 데이터로 DataFrame 생성
    data = {columns[i]: results[list(results.keys())[i]] for i in range(len(columns))}
    df = pd.DataFrame(data)

    # DataFrame을 CSV 파일로 저장
    csv_filename = 'teams_data.csv'
    df.to_csv(csv_filename, index=False)

    print(f"데이터가 {csv_filename}에 성공적으로 저장되었습니다")

# setup_model()
# prune_rules()
# load_and_run_model()

이 스크립트에는 세 가지 메서드가 정의되어 있습니다: setup_model, prune_rules, load_and_run_model. setup_model 메서드는 앞서 본 내용과 유사합니다. 스크레이퍼 인스턴스를 생성하고, 원하는 목록( wanted_list)을 생성한 후, 이 목록을 사용하여 스크레이퍼를 구축합니다. 이 스크레이퍼로 대상 URL에서 데이터를 추출하고, 추출 과정에서 수집된 규칙 ID(키)를 출력한, 모델을 프로젝트 디렉터리에 teams_model.json 파일로 저장합니다.

이 코드를 실행하려면 이전 스크립트에서 # setup_model() 라인의 주석을 해제하고, 완성된 스크립트를 파일(예: script.py)로 저장한`pythonscript.py`를 실행하세요.출력 결과는 다음과 같아야 합니다:

{'rule_hjk5': ['Boston Bruins', 'Buffalo Sabres', 'Calgary Flames', 'Chicago Blackhawks', 'Detroit Red Wings', 'Edmonton Oilers', 'Hartford Whalers', 'Los Angeles Kings', 'Minnesota North Stars', 'Montreal Canadiens', 'New Jersey Devils', 'New York Islanders', 'New York Rangers', 'Philadelphia Flyers', 'Pittsburgh Penguins', 'Quebec Nordiques', 'St. Louis Blues', 'Toronto Maple Leafs', 'Vancouver Canucks', 'Washington Capitals', '위니펙 제츠', '보스턴 브루인스', '버펄로 세이버스', '캘거리 플레임스', '시카고 블랙호크스'], 'rule_uuj6': ['보스턴 브루인스', '버펄로 세이버스', '캘거리 플레임스', '시카고 블랙호크스', '디트로이트 레드윙스', '에드먼턴 오일러스', '하트퍼드 웨일러스', '로스앤젤레스 킹스', '미네소타 노스 스타스', '몬트리올 캐나디언스', '뉴저지 데블스', '뉴욕 아일랜더스', '뉴욕 레인저스', '필라델피아 플라이어스', '피츠버그 펭귄스', '퀘벡 노르딕스', '세인트루이스 블루스', '토론토 메이플리프스', '밴쿠버 커넉스', '워싱턴 캐피털스', '위니펙 제츠', '보스턴 브루인스', '버펄로 세이버스', '캘거리 플레임스', '시카고 블랙호크스'], 'rule_9sty': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_9nie': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_41rr': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_ufil': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_ere2': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_w0vo': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_rba5': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_rmae': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_ccvi': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_3c34': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_4j80': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_oc36': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_93k1': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_d31n': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_ghh5': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_5rne': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_4p78': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_qr7s': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_60nk': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_wcj7': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_0x7y': ['1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1990', '1991', '1991', '1991', '1991'], 'rule_2hml': ['44', '31', '46', '49', '34', '37', '31', '46', '27', '39', '32', '25', '36', '33', '41', '16', '47', '23', '28', '37', '26', '36', '31', '31', '36'], 'rule_swtb': ['24'], 'rule_e8x1': ['0.55', '14', '0.575', '0.613', '-25', '0', '-38', '0.575', '-10', '24', '8', '-67', '32', '-15', '0.512', '-118', '0.588', '-77', '-72', '0', '-28', '-5', '-10', '-9', '21'], 'rule_3qvv': ['24', '30', '26', '23', '38', '37', '38', '24', '39', '30', '33', '45', '31', '37', '33', '50', '22', '46', '43', '36', '43', '32', '37', '37', '29'], 'rule_n07w': ['24', '30', '26', '23', '38', '37', '38', '24', '39', '30', '33', '45', '31', '37', '33', '50', '22', '46', '43', '36', '43', '32', '37', '37', '29'], 'rule_qmem': ['0.55', '0.388', '0.575', '0.613', '0.425', '0.463', '0.388', '0.575', '0.338', '0.487', '0.4', '0.312', '0.45', '0.412', '0.512', '0.2', '0.588', '0.287', '0.35', '0.463', '0.325', '0.45', '0.388', '0.388', '0.45'], 'rule_b9gx': ['264', '278', '263', '211', '298', '272', '276', '254', '266', '249', '264', '290', '265', '267', '305', '354', '250', '318', '315', '258', '288', '275', '299', '305', '236'], 'rule_mhl4': ['299', '292', '344', '284', '273', '272', '238', '340', '256', '273', '272', '223', '297', '252', '342', '236', '310', '241', '243', '258', '260', '270', '289', '296', '257'], 'rule_24nt': ['264', '278', '263', '211', '298', '272', '276', '254', '266', '249', '264', '290', '265', '267', '305', '354', '250', '318', '315', '258', '288', '275', '299', '305', '236'], 'rule_h090': ['264', '278', '263', '211', '298', '272', '276', '254', '266', '249', '264', '290', '265', '267', '305', '354', '250', '318', '315', '258', '288', '275', '299', '305', '236'], 'rule_xg34': ['35', '14', '81', '73', '-25', '0', '-38', '86', '-10', '24', '8', '-67', '32', '-15', '37', '-118', '60', '-77', '-72', '0', '-28', '-5', '-10', '-9', '21']}

이것은 AutoScraper가 대상 웹사이트에서 get_result_similar 호출을 통해 수집한 전체 데이터를 보여줍니다. 이 데이터에는 중복 항목이 많이 포함되어 있음을 알 수 있습니다. 이는 AutoScraper가 대상 웹사이트에서 데이터를 수집할 뿐만 아니라, 데이터 간의 관계를 추측하여 서로 관련이 있다고 판단되는 데이터 포인트 그룹(규칙)을 생성함으로써 데이터를 해석하려고 시도하기 때문입니다. 데이터를 올바르게 그룹화할 수 있다면, 이전 예제에서와 같이 유사한 웹사이트에서 데이터를 매우 쉽게 추출할 수 있습니다.

그러나 AutoScraper는 이 웹사이트 처리에서 어려움을 겪는 것으로 보입니다. 숫자가 많이 포함되어 있기 때문에 AutoScraper는 다양한 숫자들 사이에 수많은 상관관계가 있다고 가정하게 되고, 결국 중복 데이터 포인트가 포함된 대규모 규칙 데이터 세트를 생성하게 됩니다.

이제 이 데이터 세트를 신중하게 분석하여 스크래핑 작업에 적합한 데이터(, 올바른 순서로 한 열의 올바른 데이터만 포함된)를 가진 규칙을 선별해야 합니다.

이 출력 결과에서, 다음 규칙들은 우연히 올바른 데이터를 포함하고 있었습니다(일부 데이터 포인트를 수동으로 확인하고 선택한 각 규칙이 대상 페이지 테이블의 행 수인 25개의 데이터 요소를 받았는지 확인하여 발견됨):

['rule_hjk5', 'rule_9sty', 'rule_2hml', 'rule_3qvv', 'rule_e8x1', 'rule_mhl4', 'rule_h090', 'rule_xg34']

이 내용을 prune_rules 메서드에서 업데이트해야 합니다. 그런 다음 스크립트에서 setup_model() 라인을 주석 처리하고 prune_rules() 라인의 주석을 해제한 후 실행하세요. 이번에는 teams_model.json 파일에서 이전에 생성된 모델을 불러온 후, 나열된 규칙을 제외한 모든 항목을 제거하고 동일한 파일에 다시 저장합니다. teams_model.json 내용을 확인하여 현재 저장된 규칙을 확인할 수도 있습니다. 이 과정을 완료하면 모델이 준비됩니다.

이제 동일한 스크립트에서 prune_rulesprune_rules 라인을 주석 처리하고 load_and_run_model 라인의 주석을 해제한 후 다시 실행하여 load_and_run_model 메서드를 실행할 수 있습니다. 이 메서드는 프로젝트 디렉터리에 teams_data.csv라는 파일로 올바른 데이터를 추출하여 저장하며, 다음과 같은 출력을 표시합니다:

데이터가 teams_data.csv에 성공적으로 저장되었습니다

성공적으로 실행된 후 teams_data.csv 파일의 모습은 다음과 같습니다:

팀명,연도,승,패,승률(%),득점(GF),실점(GA),골득실차(+/-)
Boston Bruins,1990,44,0.55,24,299,264,35
Buffalo Sabres,1990,31,14,30,292,278,14
...21 more rows
Calgary Flames,1991,31,-9,37,296,305,-9
Chicago Blackhawks,1991,36,21,29,257,236,21

이 글에서 개발한 코드는 이 GitHub 저장소에서 확인할 수 있습니다.

AutoScraper의 일반적인 문제점

AutoScraper는 대상 웹사이트에 비교적 작은 데이터 세트와 명확한 데이터 포인트가 포함된 간단한 사용 사례에서는 효과적이지만, 앞서 본 테이블이 있는 웹사이트와 같은 복잡한 사용 사례에서는 설정하기 번거로울 수 있습니다. 또한 AutoScraper는 JavaScript 렌더링을 지원하지 않으므로 Splash와 같은 모듈이나 Selenium 또는 Puppeteer와 같은 완전한 라이브러리와 통합해야 합니다.

스크래핑 중 IP 차단과 같은 문제가 발생하거나 헤더를 커스터마이징해야 할 경우, AutoScraper는 요청 모듈에서 다음과 같이 추가 요청 매개변수 세트를 지정하는 기능을 지원합니다:

# 초기 URL로 스크레이퍼 생성
scraper.build(
    url,
    wanted_list=wanted_list,
    request_args=dict(proxies=proxies) # 프록시 목록이나 사용자 정의 헤더를 전달할 수 있는 위치
)

예를 들어, AutoScraper로 스크래핑할 때 사용자 에이전트와 프록시를 사용자 정의하는 방법은 다음과 같습니다:

request_args = { 
  "headers: {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 
            (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"  # 원하는 사용자 에이전트로 이 값을 커스터마이징할 수 있습니다. 이 값은 AutoScraper에서 기본으로 사용되는 값입니다.
  },
  "proxies": {
    "http": "http://user:[email protected]:3128/" # 프록시 포트, 호스트, 사용자 이름, 비밀번호 값 사용법을 보여주는 예시 프록시
  }
}
# 초기 URL로 스크레이퍼 생성
scraper.build(
    url,
    wanted_list=wanted_list,
    request_args=request_args
)

그러나 반복적으로 차단되는 것을 피하려면 웹 스크래핑에 최적화된 우수한 프록시가 필요합니다. 이를 위해 195개국에 걸쳐 1억 5천만 개의 주거용 IP 주소를 보유한Bright Data 주거용 프록시 사용을 고려해 보세요.

AutoScraper 라이브러리는 내부적으로 Python request 라이브러리를 사용해 대상 웹사이트에 요청을 전송하며, 기본적으로 속도 제한(rate-limiting)을 지원하지 않습니다. 웹사이트의 속도 제한 제약을 처리하려면 수동으로 스로틀링(throttling) 기능을 설정하거나 ratelimit 라이브러리 같은 사전 구축된 솔루션을 사용해야 합니다.

AutoScraper는 비동적 웹사이트에서만 작동하므로 CAPTCHA로 보호된 사이트는 전혀 처리할 수 없습니다. 이러한 경우 LinkedIn, Amazon, Zillow와 같은 사이트에서 구조화된 데이터를 제공하는 Bright Data 웹 스크래핑 API와 같은 보다 정교한 솔루션을 사용하는 것이 합리적입니다.

결론

이 글에서는 AutoScraper의 개념과 이를 활용해 단순 및 복잡한 웹사이트에서 데이터를 추출하는 방법을 알아보았습니다. 마지막 부분에서 보셨듯이, AutoScraper는 대상 웹사이트에 접근하기 위해 단순한 요청 호출에 의존합니다. 이는 동적 웹사이트나 CAPTCHA 같은 보안 장치가 적용된 사이트를 처리하는 데 종종 어려움을 겪는다는 의미입니다. 또한 대부분의 웹사이트는 비정상적으로 높은 트래픽을 가진 클라이언트를 식별할 수 있으므로 웹 스크래핑 시 프록시를 사용해야 합니다. 이러한 경우 Bright Data가 도움이 될 수 있습니다.

Bright Data는 프록시 네트워크, AI 기반 웹 스크레이퍼, 비즈니스용 데이터셋 분야의 선도적인 공급업체입니다. 지금 가입하여 무료 체험판을 포함한 Bright Data의 제품을 탐색해 보세요!