Python으로 동적 웹사이트 스크래핑 가이드

댓글: 0

웹 페이지에서 데이터를 얻는 중요한 기능은 웹 스크래핑입니다. 사용자 상호작용을 통해 콘텐츠를 동적으로 로드하는 핀터레스트와 인스타그램이 이러한 유형의 웹사이트의 예입니다. 자바스크립트 기반 자료를 처리할 때는 일반적인 스크래핑 방법만으로는 충분하지 않습니다. 이 글에서는 자동화 도구로서 Playwright에 대해 자세히 설명하고, 자바스크립트가 제대로 작동해야 하는 이러한 동적 사이트에서 데이터를 추출하기 위해 lxml을 사용하겠습니다. 이 글에서는 봇 탐지를 피하기 위해 Playwright에서 프록시를 활용하는 방법에 대해 설명할 수 있습니다. 이 튜토리얼에서는 스크롤 및 게시물 로딩 대기 등의 사용자 행동을 시뮬레이션하여 Instagram 프로필을 스크래핑하여 모든 게시물 URL을 검색해 보겠습니다.

이 가이드에서 사용할 도구:

  • 플레이라이트(브라우저 자동화용);
  • lxml(XPath를 사용한 데이터 추출용);
  • Python(프로그래밍 언어).

인스타그램 게시물 스크래핑에 대한 단계별 가이드

Instagram 프로필을 스크랩하여 게시물 URL을 추출하는 예시를 통해 페이지를 스크롤하고 새 데이터가 로드될 때까지 기다리는 등의 사용자 행동을 시뮬레이션하여 프로세스를 설명하겠습니다. 동적 웹사이트는 AJAX 요청을 통해 콘텐츠를 비동기적으로 로드하므로 모든 페이지 콘텐츠에 즉시 액세스할 수 있는 것은 아닙니다.

1단계. 필요한 라이브러리 설치

시작하기 전에 필요한 패키지를 설치합니다:


pip install playwright
pip install lxml

또한 Playwright 브라우저를 설치해야 합니다:


playwright install

2단계. 동적 웹사이트 스크래핑을 위한 플레이라이터 설정

Playwright를 사용하여 브라우저를 자동화하고, Instagram의 동적 콘텐츠를 로드하고, 페이지를 스크롤하여 더 많은 게시물을 로드하겠습니다. 기본적인 자동화 스크립트를 만들어 보겠습니다:

자동화 스크립트(헤드리스 브라우저):


import asyncio
from playwright.async_api import async_playwright

async def scrape_instagram():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)  # 헤드리스 모드 시각적 피드백 없음
        page = await browser.new_page()
        
        # 프로필 URL 방문하기
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")

        # 더 많은 게시물을 로드하려면 버튼을 클릭하세요.
        await page.get_by_role("button", name="Show more posts from").click()
        
        # 페이지를 스크롤하여 동적 콘텐츠를 로드합니다.
        scroll_count = 5  # 스크롤 횟수에 따라 사용자 지정합니다.
        for _ in range(scroll_count):
            await page.evaluate('window.scrollBy(0, 700);')
            await page.wait_for_timeout(3000)  # Wait for posts to load
            await page.wait_for_load_state("networkidle")
        
        # 페이지 콘텐츠 가져오기
        content = await page.content()
        await browser.close()
        
        return content

# 비동기 함수 실행
asyncio.run(scrape_instagram())

3단계. lxml 및 XPath로 페이지 구문 분석

콘텐츠가 로드되면 lxml을 사용하여 HTML을 구문 분석하고 XPath를 사용하여 데이터를 추출할 수 있습니다. 이 경우 프로필에서 모든 글의 URL을 추출합니다.

페이지 콘텐츠를 구문 분석하고 글 URL을 추출합니다:


from lxml import html
import json

def extract_post_urls(page_content):
    # lxml을 사용하여 HTML 콘텐츠 구문 분석
    tree = html.fromstring(page_content)
    
    # 글 URL 추출을 위한 XPath
    post_urls_xpath = '//a[contains(@href, "/p/")]/@href'
    
    # URL 추출
    post_urls = tree.xpath(post_urls_xpath)
    
    # 상대 URL을 절대 URL로 변환
    base_url = "https://www.instagram.com"
    post_urls = [f"{base_url}{url}" for url in post_urls]
    
    return post_urls

추출된 데이터를 JSON 형식으로 저장하는 예제 함수입니다:


def save_data(profile_url, post_urls):
    data = {profile_url: post_urls}
    with open('instagram_posts.json', 'w') as json_file:
        json.dump(data, json_file, indent=4)

# URL 스크랩 및 추출
page_content = asyncio.run(scrape_instagram())
post_urls = extract_post_urls(page_content)

# 추출한 URL을 JSON 파일에 저장합니다.
save_data("https://www.instagram.com/profile name/", post_urls)

4단계. 플레이라이트로 무한 스크롤 처리하기

동적 웹사이트를 스크랩하려면 무한 스크롤을 시뮬레이션해야 하는 경우가 많습니다. 이 스크립트에서는 JavaScript를 사용하여 페이지를 스크롤합니다:


(window.scrollBy(0, 700))

그리고 이 명령을 사용하여 새 콘텐츠가 로드될 때까지 기다립니다:


 wait_for_load_state("networkidle")

5단계. Playwright에서 프록시 사용

Instagram에는 엄격한 속도 제한과 봇 방지 조치가 있습니다. 차단을 피하기 위해 프록시를 사용하여 IP 주소를 회전하고 요청을 분산할 수 있습니다. Playwright를 사용하면 스크래핑 자동화에 프록시를 쉽게 통합할 수 있습니다.

Playwright에서 프록시 구현하기:


async def scrape_with_proxy():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False, 
            proxy={"server": "http://your-proxy-server:port"}
        )
        page = await browser.new_page()
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")
        # 이전과 같이 스크래핑을 계속합니다...

또한 프록시를 사용자 아이디 비밀번호로 전달할 수 있도록 지원하며, 서버 예시는 아래와 같습니다.


async def scrape_with_proxy():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False, 
            proxy={"server": "http://your-proxy-server:port", "username": "username", "password": "password"}
        )
        page = await browser.new_page()
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")
        # 이전과 같이 스크래핑을 계속합니다...

프록시는 IP 차단, 캡차 문제를 방지하고 Instagram과 같이 데이터가 많거나 제한된 웹사이트를 원활하게 스크랩할 수 있도록 도와줍니다.

코드 완성


import asyncio
from playwright.async_api import async_playwright
from lxml import html
import json

# 프록시를 사용하여 브라우저를 자동화하고 동적 콘텐츠를 스크랩하는 기능
async def scrape_instagram(profile_url, proxy=None):
    async with async_playwright() as p:
        # 프록시가 제공된 경우 프록시로 브라우저 설정
        browser_options = {
            'headless': True,  # 헤드리스 브라우저를 사용하여 작업을 확인합니다(헤드리스 모드의 경우 True로 설정 가능).
        }
        if proxy:
            browser_options['proxy'] = proxy

        # 브라우저 실행
        browser = await p.chromium.launch(**browser_options)
        page = await browser.new_page()

        # Instagram 프로필 페이지 방문하기
        await page.goto(profile_url, wait_until="networkidle")
        
        # '게시물 더 보기' 버튼을 클릭해 보세요(선택 사항, 버튼을 찾을 수 없는 경우 실패할 수 있음).
        try:
           await page.click('button:has-text("Show more posts from")')
        except Exception as e:
           print(f"No 'Show more posts' button found: {e}")


        # 페이지를 스크롤하여 더 많은 게시물을 로드합니다.
        scroll_count = 5  # 게시물을 로드하는 스크롤 수
        for _ in range(scroll_count):
            await page.evaluate('window.scrollBy(0, 500);')
            await page.wait_for_timeout(3000)  # 새 글이 로드될 때까지 기다립니다.
            await page.wait_for_load_state("networkidle")

        # 스크롤 후 전체 페이지 콘텐츠 보기
        content = await page.content()
        await browser.close()  # 완료되면 브라우저를 닫습니다.
        
        return content

# 스크랩한 페이지 콘텐츠를 구문 분석하고 글 URL을 추출하는 기능
def extract_post_urls(page_content):
    # lxml을 사용하여 HTML 콘텐츠 구문 분석
    tree = html.fromstring(page_content)
    
    # 글 URL 추출을 위한 XPath
    post_urls_xpath = '//a[contains(@href, "/p/")]/@href'
    
    # XPath를 사용하여 게시물 URL 추출
    post_urls = tree.xpath(post_urls_xpath)
    
    # 상대 URL을 절대 URL로 변환
    base_url = "https://www.instagram.com"
    post_urls = [f"{base_url}{url}" for url in post_urls]
    
    return post_urls

# 추출된 글 URL을 JSON 파일로 저장하는 함수
def save_data(profile_url, post_urls):
    # JSON 형식으로 데이터 구조화
    data = {profile_url: post_urls}
    
    # 데이터를 파일에 저장
    with open('instagram_posts.json', 'w') as json_file:
        json.dump(data, json_file, indent=4)
    print(f"Data saved to instagram_posts.json")

# 스크레이퍼를 실행하고 데이터를 저장하는 주요 기능
async def main():
    # Instagram 프로필 URL 정의
    profile_url = "https://www.instagram.com/profile name/"
    
    # 선택 사항으로 프록시 설정
    proxy = {"server": "server", "username": "username", "password": "password"}  # 프록시가 필요하지 않은 경우 없음 사용
    
    # 프록시를 사용하여 Instagram 페이지 스크랩하기
    page_content = await scrape_instagram(profile_url, proxy=proxy)
    
    # 스크랩한 페이지 콘텐츠에서 게시물 URL 추출하기
    post_urls = extract_post_urls(page_content)
    
    # 추출한 글 URL을 JSON 파일에 저장합니다.
    save_data(profile_url, post_urls)

if __name__ == '__main__':
   asyncio.run(main())

웹 스크래핑을 위한 대체 자동화 도구

동적 웹사이트를 스크래핑하는 데는 Playwright가 탁월한 선택이지만, 다른 도구가 다른 시나리오에 적합할 수도 있습니다:

  1. Selenium: 셀레늄은 가장 오래된 브라우저 자동화 프레임워크 중 하나이며 Playwright와 유사하게 작동합니다. 매우 다재다능하지만 단일 API로 여러 브라우저를 처리하는 등 Playwright가 제공하는 일부 최신 기능이 부족합니다.
  2. 퍼피티: Puppeteer는 브라우저 자동화를 위한 또 다른 인기 도구로, 특히 자바스크립트가 많은 웹사이트를 스크랩하는 데 유용합니다. Playwright와 마찬가지로 헤드리스 브라우저를 제어하고 동적 콘텐츠와 상호 작용할 수 있습니다.
  3. Requests + BeautifulSoup: 콘텐츠를 로드하는 데 JavaScript가 필요하지 않은 간단한 웹사이트의 경우, Requests 라이브러리와 BeautifulSoup을 결합한 가벼운 대안이 될 수 있습니다. 하지만 동적 콘텐츠는 잘 처리하지 못합니다.

각 도구는 고유한 강점을 제공하며 프로젝트의 특정 요구 사항과 조건에 따라 선택할 수 있습니다.

JavaScript와 AJAX 요청을 적극적으로 사용하는 동적 웹사이트를 성공적으로 스크랩하려면 무한 스크롤과 복잡한 인터랙티브 요소를 효율적으로 처리할 수 있는 강력한 도구가 필요합니다. 이러한 솔루션 중 하나는 완전한 브라우저 자동화를 제공하는 Microsoft의 도구인 Playwright로, Instagram과 같은 플랫폼에 이상적인 선택입니다. HTML 구문 분석을 위한 lxml 라이브러리와 결합된 Playwright는 데이터 추출을 크게 간소화하여 수동 개입 없이 페이지 요소와의 상호 작용 및 구문 분석 프로세스를 자동화할 수 있습니다. 또한 프록시 서버를 사용하면 안티 봇 보호를 우회하고 IP 차단을 방지하여 안정적이고 중단 없는 스크래핑 작업을 보장합니다.

댓글:

0 댓글