Scrapy Tutorial - 다중페이지 크롤링

Page content

개요

  • 이번에는 Scrapy를 통해서 다중 페이지를 크롤링 하도록 한다.

Target 페이지

Untitled

프로젝트 시작

  • 프로젝트 시작은 다음과 같이 할 수 있다.
$ scrapy startproject multiCam_tutorial
New Scrapy project 'multiCam_tutorial', using template directory 'C:\Users\j2hoo\OneDrive\Desktop\your_project_folder\venv\Lib\site-packages\scrapy\templates\project', created in:
    C:\Users\j2hoo\OneDrive\Desktop\your_path\multiCam_tutorial

You can start your first spider with:
    cd multiCam_tutorial
    scrapy genspider example example.com
  • 해당 multiCam_tutorial 경로에서 다음 명령어를 실행하여 타겟 사이트를 설정한다.
$ scrapy genspider audible www.audible.com/search
Created spider 'audible' using template 'basic' in module:
  multiCam_tutorial.spiders.audible
  • audible.py 파일을 찾아 코드를 확인한다.

    import scrapy
    
    class AudibleSpider(scrapy.Spider):
        name = "audible"
        allowed_domains = ["www.audible.com"]
        start_urls = ["https://www.audible.com/search"]
    
        def parse(self, response):
            pass
    

웹사이트 분석

  • 페이지에서 전체 책 리스트를 포함하고 있는 div 태그를 찾는다.
      • 이를 XPath 코드로 변환하여 확인한다. (Ctrl + F)
      //div[@class="adbl-impression-container "]
      

      Untitled

      • /div/span[2]/ul/li 코드를 추가하면 20개의 element가 발견되는 것을 확인할 수 있다.
      //div[@class="adbl-impression-container "]/div/span[2]/ul/li
      

      Untitled

      1 페이지 데이터 조회

      • 각 li 태그에 담겨 있는 데이터를 가져오도록 한다.

      • audible.py 코드를 수정한다.

        • 기존
        import scrapy
        
        class AudibleSpider(scrapy.Spider):
            name = "audible"
            allowed_domains = ["www.audible.com"]
            start_urls = ["https://www.audible.com/search"]
        
            def parse(self, response):
                pass
        
        • 수정
          • Author 이름을 가져오려면 먼저 아래 이미지 처럼 li > span > a 순서대로 xpath 코드를 추가해야 한다.
          • li 클래스 이름은 authorLabel이다.

        Untitled

        import scrapy
        
        class AudibleSpider(scrapy.Spider):
            name = "audible"
            allowed_domains = ["www.audible.com"]
            start_urls = ["https://www.audible.com/search"]
        
            def parse(self, response):
                product_container = response.xpath('//div[@class="adbl-impression-container "]/div/span[2]/ul/li')
        
                for product in product_container:
                    book_title = product.xpath('.//h3[contains(@class, "bc-heading")]/a/text()').get()
        
        		        # 몇몇 도서의 저자는 2명이기도 하다. 
                    book_author = product.xpath('.//li[contains(@class, "authorLabel")]/span/a/text()').getall()
        
                    # 책의 길이
                    book_length = product.xpath('.//li[contains(@class, "runtimeLabel")]/span/text()').get
        
                    yield {
                        'title' : book_title, 
                        'author' : book_author, 
                        'length' : book_length
                    }
        
      • 이제 scrapy 명령어를 실행하여 전체 데이터가 잘 출력되는지 확인한다.

      scrapy crawl audible
      

      Untitled

      • 해당 코드를 이제 csv 파일로 내보낸다.
      scrapy crawl audible -o audible_onepage.csv
      

      Untitled

      다중 페이지 조회 (Pagination)

      • 다중 페이지를 조회하기 위해서는 웹사이트 분석을 다시 해야 한다.
      • 먼저 각 page 번호는 ul > li 태그로 구성되어 있는 것을 확인할 수 있다.

      Untitled

      • 이제 page 관련 ul 태그에 접근할 수 있도록 xpath 문법을 수정한다.
      //ul[contains(@class, "pagingElements")]
      

      Untitled

      • 두번째로 확인해야 할 것은 next 페이지로 이동할 수 있는 버튼을 클릭하는 것이다. 즉, 버튼 태그를 클릭한다.
        • 버튼은 ul > li > span > a 형태로 들어간다는 것을 확인할 수 있다.

      Untitled

      • 이를 xpath로 구현하면 다음과 같다.
      //ul[contains(@class, "pagingElements")]//span[contains(@class, "nextButton")]/a/@href
      

      Untitled

      • 이제 코드로 구현한다.
      import scrapy
      
      class AudibleSpider(scrapy.Spider):
          name = "audible"
          allowed_domains = ["www.audible.com"]
          start_urls = ["https://www.audible.com/search"]
      
          def parse(self, response):
              product_container = response.xpath('//div[@class="adbl-impression-container "]/div/span[2]/ul/li')
      
              for product in product_container:
                  book_title = product.xpath('.//h3[contains(@class, "bc-heading")]/a/text()').get()
      
      		    # 몇몇 도서의 저자는 2명이기도 하다. 
                  book_author = product.xpath('.//li[contains(@class, "authorLabel")]/span/a/text()').getall()
      
                  # 책의 길이
                  book_length = product.xpath('.//li[contains(@class, "runtimeLabel")]/span/text()').get()
      
                  yield {
                      'title' : book_title, 
                      'author' : book_author, 
                      'length' : book_length
                  }
              
              pagination = response.xpath('//ul[contains(@class, "pagingElements")]')
              next_page_url = pagination.xpath('.//span[contains(@class, "nextButton")]/a/@href').get()
      
              if next_page_url:
                  yield response.follow(url=next_page_url, callback=self.parse)
      
      • 코드가 완성이 되었다면, 이제 scrapy 명령어로 csv 파일까지 출력한다.
      scrapy crawl audible -o audible_multiplepage.csv
      

      Untitled

      User Agent 변경

      • User-Agent를 변경하도록 한다.

        • 파일 변경은 settings.py에서 한다.
        • 기존
        # Override the default request headers:
        #DEFAULT_REQUEST_HEADERS = {
        #    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        #    "Accept-Language": "en",
        #}
        
        • 수정
        # Override the default request headers:
        #DEFAULT_REQUEST_HEADERS = {
        #    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        #    "Accept-Language": "en",
        #}
        
        DEFAULT_REQUEST_HEADERS = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'}
        
      • 이후 audible.py를 수정한다.

      import scrapy
      
      class AudibleSpider(scrapy.Spider):
          name = "audible"
          allowed_domains = ["www.audible.com"]
          # start_urls = ["https://www.audible.com/search"]
      
          def start_requests(self):
              yield scrapy.Request(
                  url = 'https://www.audible.com/search', 
                  callback=self.parse, 
                  headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'}
              )
      
          def parse(self, response):
              product_container = response.xpath('//div[@class="adbl-impression-container "]/div/span[2]/ul/li')
      
              for product in product_container:
                  book_title = product.xpath('.//h3[contains(@class, "bc-heading")]/a/text()').get()
      
      		    # 몇몇 도서의 저자는 2명이기도 하다. 
                  book_author = product.xpath('.//li[contains(@class, "authorLabel")]/span/a/text()').getall()
      
                  # 책의 길이
                  book_length = product.xpath('.//li[contains(@class, "runtimeLabel")]/span/text()').get()
      
                  yield {
                      'title' : book_title, 
                      'author' : book_author, 
                      'length' : book_length, 
                      'User-Agent' : response.request.headers['User-Agent'] # 테스트 보여주기용
                  }
              
              pagination = response.xpath('//ul[contains(@class, "pagingElements")]')
              next_page_url = pagination.xpath('.//span[contains(@class, "nextButton")]/a/@href').get()
      
              if next_page_url:
                  yield response.follow(
                      url=next_page_url, 
                      callback=self.parse, 
                      headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'})
      

      (추가 공부 XPath)

      • Xpath(XML Path Language)
      • XML 문서에서 정보를 탐색하고 추출하는 데 사용되는 쿼리 언어
      • XML 트리에서 작동 경로와 유사한 구문 사용

      Untitled