<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>r5_jun.log</title>
        <link>https://velog.io/</link>
        <description>나도 내가 뭔지 모르겠습니다</description>
        <lastBuildDate>Mon, 08 May 2023 13:52:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>r5_jun.log</title>
            <url>https://velog.velcdn.com/images/r5_jun/profile/62352b72-939a-4151-9d70-326de03df6a0/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. r5_jun.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/r5_jun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[웹 스크래핑(Web Scraping) ]]></title>
            <link>https://velog.io/@r5_jun/%EC%9B%B9-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91Web-Scraping</link>
            <guid>https://velog.io/@r5_jun/%EC%9B%B9-%EC%8A%A4%ED%81%AC%EB%9E%98%ED%95%91Web-Scraping</guid>
            <pubDate>Mon, 08 May 2023 13:52:54 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>글쓴이도 대부분의 사람들처럼 처음 웹 스크래핑을 배울때 네이버 뉴스로 연습을 했으며 연습문제들을 다 풀고나서 웹 스크래핑은 별것 아니라고 생각했었던 적이 있었다.
하지만 실무에서 데이터를 수집하기 위해 웹 스크래핑을 하다 보면 모든 웹 사이트가 네이버 뉴스, 네이버 쇼핑, 아마존의 웹사이트 처럼 스크래핑하기 편한 것은 아니라는 걸 깨닫게 된다 (대기업은 다르다...)</p>
<p>오늘은 웹 스크래핑하기 재미있어<del>(귀찮아)</del>보이는 웹 사이트를 발견해 글쓴이의 블로그 첫 게시글의 소재로 써보고자 한다.</p>
<h1 id="사전작업">사전작업</h1>
<h2 id="웹사이트-소개">웹사이트 소개</h2>
<p>스크래핑할 웹 사이트 : <a href="https://kdata.or.kr/datavoucher/index.do">데이터바우처</a>
오늘 스크래핑해볼 웹 사이트는 데이터바우처 사이트로 데이터바우처라는 정부지원사업에 대한 정보가 공지되는 사이트이다. 
<a href="https://kdata.or.kr/datavoucher/index.do"><img src="https://velog.velcdn.com/images/r5_jun/post/073386e7-b5bc-4dc2-ad0d-5957bf7905e8/image.png" alt="데이터바우처 루트 페이지"></a>루트 페이지를 보면 평범한 공공기관 웹사이트의 모습이다. <del>(악의 기운이 스멀스멀 올라온다)</del>
우리는 통합검색에 있는 모든 상품에 대한 <code>상품명</code>, <code>기업명(영문)</code>, <code>담당자연락처</code>, <code>이메일</code>, <code>태그</code>를 스크래핑 해보도록 하자 </p>
<h2 id="robotstxt-확인하기">Robots.txt 확인하기</h2>
<p>글쓴이가 웹 스크래핑하기 전 첫번째로 확인하는 것은 대상 웹 사이트의 웹 스크래핑 허용 여부이다
웹 스크래핑같은 자동화 코드는 해당 사이트의 서버에 과부하를 줄수도 있고 데이터는 해당기업의 중요한 자산이기 때문에 스크래핑이 허용이 되는지? 허용된다면 어디까지 허용이 되는지 살펴봐야한다. 
허용 여부는 <code>Robots.txt</code>를 보고 확인할 수 있다.
<code>Robots.txt</code>는 해당 사이트의 루트(root)페이지 url에 /robots.txt를 치면 다운로드 받을 수 있다.
예시 : <a href="https://www.naver.com/robots.txt">https://www.naver.com/robots.txt</a> (네이버의 robots.txt)</p>
<p>하지만 우리가 오늘 스크래핑하고자 하는 사이트는 아무리 시도를 해봐도 robots.txt를 찾을 수 없다...(어느정도 예상은 했었다)
<a href="https://kdata.or.kr/datavoucher/index.do/robots.txt">https://kdata.or.kr/datavoucher/index.do/robots.txt</a>
<a href="https://kdata.or.kr/datavoucher/robots.txt">https://kdata.or.kr/datavoucher/robots.txt</a>
<a href="https://kdata.or.kr/robots.txt">https://kdata.or.kr/robots.txt</a>
<img src="https://velog.velcdn.com/images/r5_jun/post/63ebf4ba-9068-48f6-9b73-db3c7c30aa88/image.png" alt="">그냥 공개 데이터니까 써도 된다고 생각하고 서버에 무리 주지 않도록 <code>time.sleep</code>을 많이 주고 해야겠다...
Robots.txt를 해석하는 방법은 구글에 이미 잘 설명한 글이 많기 때문에 검색해보시길 바란다.  </p>
<h2 id="새-탭으로-열리는지-확인">새 탭으로 열리는지 확인</h2>
<p>Robots.txt를 확인한 다음 개인적으로 중요하게 생각하는 것이 새 탭으로 열리는지 여부이다.
보통 ctrl을 누른채로 클릭을 하면 새 탭으로 열기가 된다.
웹 스크래핑을 할 때 글쓴이는 이 기능을 이용해 부모 페이지에서 자식 페이지를 열고 스크래핑 후 자식 페이지를 닫고 부모 페이지에서 또 다시 다른 자식 페이지를 열어 스크래핑을 반복하는 식으로 코드를 작성한다.
하지만 새 탭으로 열기가 되지 않는다면 코드를 다른식으로 작성해야하기 때문에 (글쓴이 기준에서) 코딩이 조금 귀찮아진다. 
그리고 역시 이 사이트는 새 탭으로 열리지 않는다... <del>젠장</del>
<img src="https://velog.velcdn.com/images/r5_jun/post/259b3735-78cc-4f97-bf3f-423165448e18/image.gif" alt=""> 일반적인 경우 ctrl+클릭을 하면 위와 같이 새 탭으로 열린다<img src="https://velog.velcdn.com/images/r5_jun/post/d5fbeefb-534c-42b7-b428-447ba1b13c50/image.gif" alt="">하지만 데이터바우처 사이트는 ctrl+클릭을 하더라도 기존 탭에서 클릭한 페이지를 열어준다.
이렇게되면 위에서 설명한 새 탭으로 열기 기능을 활용한 방법은 사용하지 못하고 클릭하고 뒤로가기를 반복해야한다. </p>
<blockquote>
<p>개인적으로 느끼는 뒤로가기의 단점</p>
<ul>
<li><code>부모-&gt;자식(뒤로가기 클릭)-&gt;부모 페이지</code>로 돌아왔을 때 부모 페이지가 이전에 접속했을 때와 달라질 수 있음  </li>
<li>뒤로가기 버튼을 누르면 탭을 닫는 것보다 속도가 느리다 (많은 데이터를 수집해야할 때 문제가 됨)</li>
</ul>
</blockquote>
<p>자세한 내용은 뒤에 코드를 설명에서 언급하겠다</p>
<h2 id="페이지-번호가-url에-있는지-확인">페이지 번호가 URL에 있는지 확인</h2>
<p>또 개인적으로 확인하는 것은 페이지 번호가 URL에 반영이 되어 있는지 여부이다.
네이버 뉴스의 정치 뉴스페이지를 예시로 들어보겠다.
<img src="https://velog.velcdn.com/images/r5_jun/post/8caf34bb-e761-47f1-8187-e72a543b390b/image.png" alt=""></p>
<blockquote>
<p>페이지1 URL: <a href="https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100">https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100</a>
페이지2 URL: <a href="https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=2">https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=2</a>
페이지3 URL: <a href="https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=3">https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=3</a>
페이지4 URL: <a href="https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=4">https://news.naver.com/main/main.naver?mode=LSD&amp;mid=shm&amp;sid1=100#&amp;date=%2000:00:00&amp;page=4</a></p>
</blockquote>
<p>페이지 마다의 URL을 확인하다보면 2페이지부터 URL 제일뒤에 <code>page=2</code>, <code>page=3</code>, <code>page=4</code>처럼 페이지 번호가 반영되어 있다
페이지1 URL 뒤에 <code>#&amp;date=%2000:00:00&amp;page=1</code>를 붙여줘도 똑같은 페이지나 나온다
이런식으로 URL에 페이지 번호가 반영이 되어있다면 URL에 숫자를 차례대로 변경하여 다음 페이지로 넘어갈 수 있어 편리하다.</p>
<p>하.지.만.
역시나 역시나 역시나 우리가 스크래핑하려는 사이트는 그런 기능따위는 없다.<img src="https://velog.velcdn.com/images/r5_jun/post/0eca2291-64c0-4ab2-91ad-cc515de4d697/image.png" alt=""></p>
<blockquote>
<p>페이지1: <a href="https://kdata.or.kr/datavoucher/is/selectPortalSearch.do">https://kdata.or.kr/datavoucher/is/selectPortalSearch.do</a>
페이지2: <a href="https://kdata.or.kr/datavoucher/is/selectPortalSearch.do">https://kdata.or.kr/datavoucher/is/selectPortalSearch.do</a>
페이지3: <a href="https://kdata.or.kr/datavoucher/is/selectPortalSearch.do">https://kdata.or.kr/datavoucher/is/selectPortalSearch.do</a>
...
페이지611: <a href="https://kdata.or.kr/datavoucher/is/selectPortalSearch.do">https://kdata.or.kr/datavoucher/is/selectPortalSearch.do</a></p>
</blockquote>
<p>모든 번호의 페이지 URL이 같다...
이렇게 되면 Selenium과 반복문을 이용해 페이지 버튼을 하나하나 클릭해 넘겨야해서 개인적으로 조금 귀찮아진다.</p>
<p>스크래핑을 간편하게 해줄 기능이 하나도 없다는 절망적인 사실을 확인했다.
눈물 닦고 코드 작성으로 넘어가보자</p>
<h1 id="코드">코드</h1>
<p>먼저 필요한 라이브러리를 import하자</p>
<h2 id="라이브러리-임포트import">라이브러리 임포트(import)</h2>
<pre><code class="language-python"># selenium의 Web Driver 관련 라이브러리
from selenium import webdriver
# chromedriver 설치경로를 매개변수로 받아서 chromedriver를 시작하고 중지하는 역할
from selenium.webdriver.chrome.service import Service as ChromeService
# selenium에서 키보드를 사용할 수 있게 해주는 역할
from selenium.webdriver.common.keys import Keys
# By를 이용해 웹의 element를 찾을 수 있게 해주는 역할
from selenium.webdriver.common.by import By 

# 안티 스크래핑 방지를 위해 중간에 잠시 멈추는 용도
import time 

# 데이터 프레임(Data Frame) 형식으로 저장 위한 라이브러리
import pandas as pd

# csv파일을 다루기 위한 라이브러리
import csv</code></pre>
<p>페이지 번호가 URL에 반영되어 있지 않아 Beautiful Soup은 쓰지 않고 Selenium을 이용해 모두 해결하겠다.</p>
<h2 id="빈empty-csv파일-만들기">빈(empty) CSV파일 만들기</h2>
<p>이번 스크래핑의 목적은 기업들의 정보를 수집하는 것이다.
<a href="https://kdata.or.kr/datavoucher/is/selectPortalSearch.do">데이터바우처 상품 리스트 페이지</a>에 들어가 아무 상품을 클릭하면 아래와 같은 상품 페이지를 볼 수 있다
스크래핑할 속성을 확인하고 미리 CSV파일 첫줄에 속성명을 적어주자
<img src="https://velog.velcdn.com/images/r5_jun/post/c814ac13-b957-44fc-b2d4-c1cbdf61751d/image.png" alt=""></p>
<pre><code class="language-python"># pandas(pd)를 이용해 DataFrame 만들어 주기
df = pd.DataFrame(columns=[&#39;idx&#39;,
                            &#39;상품명&#39;,
                            &#39;기업명 (영문)&#39;, 
                            &#39;담당자연락처&#39;, 
                            &#39;이메일&#39;, 
                            &#39;태그&#39;])

# 위에 만든 DataFrame을 이용해 원하는 경로에 &quot;데이터바우처_상품_1.csv&quot;이라는 csv 파일을 만들어줌
# 인덱스index는 따로 만들어줄거니까 False해줌
# 한글을 사용하니 encoding은 &quot;utf-8-sig&quot;를 주겠음
df.to_csv(&#39;C:/Users/r5_jun/Desktop/Workspace/데이터바우처_크롤링/데이터바우처_상품_1.csv&#39;, index=False, encoding=&#39;utf-8-sig&#39;)</code></pre>
<p><code>idx</code>인덱스, <code>상품명</code>, <code>기업명 (영문)</code>, <code>담당자연락처</code>, <code>이메일</code>, <code>태그</code>라는 데이터 속성명을 CSV파일에 넣어주었다
CSV파일을 열어보면 아래와 같다<img src="https://velog.velcdn.com/images/r5_jun/post/0565ef55-9e7c-4381-a33c-85c0773edf6b/image.png" alt=""></p>
<h2 id="chrome-web-driver로-크롬-브라우저-열기">Chrome Web driver로 크롬 브라우저 열기</h2>
<p>크롬 웹 드라이버(chromedriver.exe)를 이용해 빈 크롬 브라우저를 열어준다
chromedriver에 대한 내용은 설명하면 너무 장황해질거 같아서 줄였다. 인터넷이 이미 잘 설명해놓은 글이 많으니 모른다면 검색해보길 바란다
<del>(다음에 시간날때 chromedriver에 대한 글도 써보겠다)</del></p>
<pre><code class="language-python">#chromedriver.exe의 경로를 받아와서 driver에 선언
driver = webdriver.Chrome(&#39;chromedriver_win32/chromedriver&#39;)
#위 코드를 실행하면 빈 크롬 페이지가 열림

# 3초 기다리기
time.sleep(3)</code></pre>
<p>위 코드를 실행해 빈 크롬 브라우저가 열렸다</p>
<h2 id="스크래핑할-웹-페이지-열기">스크래핑할 웹 페이지 열기</h2>
<pre><code class="language-python"># url변수에 스크래핑할 웹 페이지의 url를 복사에 붙여줌
url = &quot;https://kdata.or.kr/datavoucher/is/selectPortalSearch.do&quot;

# url에 정의된 주소 열기
driver.get(url)
# 페이지가 다 열리도록 3초 기다려주기
time.sleep(3)</code></pre>
<p>이렇게 하면 빈 크롬 페이지가 url변수에 설정해둔 페이지를 열어준다!!<img src="https://velog.velcdn.com/images/r5_jun/post/32e5fa23-4a6d-4a06-b024-1130d26bbcbe/image.png" alt="">이.렇.게.</p>
<h2 id="상품-페이지-스크래핑-함수">상품 페이지 스크래핑 함수</h2>
<p>방금전 빈 CSV파일 만들던때 처럼 아무 상품이나 클릭해 X.path를 이용해 text를 가져오는 기능을 하는 함수 코드를 만들자</p>
<pre><code class="language-python">def prod_scraper(idx, product_cnt):

    # 상품 수 만큼 반복(product_cnt는 다음 상품 리스트 페이지 스크래핑 함수에서 받아올 것임)
    for j in range(1,product_cnt+1):

        # 상품 페이지(자식 페이지)에 접속
        driver.find_element(By.XPATH, &#39;//*[@id=&quot;listType&quot;]/div[&#39;+str(j)+&#39;]/div[2]/a&#39;).send_keys(Keys.ENTER)
        # 페이지에 접속할 수 있도록 3초 기다리기
        time.sleep(3)

        # 상품명(product_name) 가져오기
        product_name=driver.find_element(By.XPATH, &#39;//*[@id=&quot;prdcInfoDiv&quot;]/div[1]/ul/li[3]/dl/dt&#39;).text
        # 가져올 text가 없을때 &quot;가공서비스&quot;라고 값을 넣어주기
        if product_name!=&quot;&quot;:
            print(product_name)
        else:
            product_name=&quot;가공서비스&quot;
            print(product_name)

        # 기업명(company_name) 가져오기
        company_name=driver.find_element(By.XPATH, &#39;//*[@id=&quot;frm&quot;]/div[2]/div/div/div/div[2]/div[1]/ul[1]/li[1]/dl/dd&#39;).text
        print(company_name)

        # 담당자연락처(phone_num) 가져오기
        phone_num=driver.find_element(By.XPATH, &#39;//*[@id=&quot;frm&quot;]/div[2]/div/div/div/div[2]/div[1]/ul[2]/li[1]/dl/dd&#39;).text
        print(phone_num)

        # 이메일(email) 가져오기
        email=driver.find_element(By.XPATH, &#39;//*[@id=&quot;frm&quot;]/div[2]/div/div/div/div[2]/div[1]/ul[2]/li[2]/dl/dd&#39;).text
        print(email)

        # 태그(tag) 가져오기
        tag=driver.find_element(By.XPATH, &#39;//*[@id=&quot;prdcInfoDiv&quot;]/div[1]/ul/li[1]/span[1]&#39;).text
        # 가져올 text가 없을때 &quot;가공&quot;이라고 값을 넣어주기
        if tag!=&quot;&quot;:
            print(tag)
        else:
            tag=&quot;가공&quot;
            print(tag)

        # 위에서 만들어둔 빈 csv에 한 행씩 저장하기
        with open(&quot;C:/Users/r5_jun/Desktop/Workspace/데이터바우처_크롤링/데이터바우처_상품_1.csv&quot;, &#39;a+&#39;, newline=&quot;&quot;, encoding=&quot;utf-8-sig&quot;) as write_obj:
            csv_writer = csv.writer(write_obj)
            csv_writer.writerow([idx, product_name, company_name, phone_num, email, tag])

        # 인덱스 숫자 1 더하기
        idx+=1    

        # 뒤로가기 버튼 누르기 
        driver.back()
        # 상품 리스트 페이지(부모 페이지)로 완전히 넘어갈 수 있도록 3초 기다리기
        time.sleep(3)

        # 해당 상품 스크래핑 완료 표시 
        print(&quot;----------------------------&quot;+str(j)+&#39;번 상품 완료--------------------------------------&#39;)

    # idx를 리턴
    return idx</code></pre>
<h2 id="다음-페이지-이동하는-함수">다음 페이지 이동하는 함수</h2>
<pre><code class="language-python">def move_to_next_page(idx,page_cnt):

    # 페이지 수만큼 반복
    for i in range(1, page_cnt+1):

        # 현재 스크래핑하는 상품 리스트 페이지(부모 페이지) 번호를 page에 정의 
        page = driver.find_element(By.XPATH, &#39;//*[@id=&quot;paging&quot;]/strong&#39;).text
        print(page+&quot; 페이지&quot;)

        # 스크롤 다운코드
        for c in range(0,3):
            driver.find_element(By.TAG_NAME, &#39;body&#39;).send_keys(Keys.PAGE_DOWN)
            time.sleep(1)

        # 해당 페이지의 상품 갯수(product_cnt) 세기
        product_list=driver.find_elements(By.XPATH, &#39;//*[@id=&quot;listType&quot;]/div/div[2]/a&#39;)
        product_cnt=len(product_list)
        print(&quot;상품갯수 &quot;+str(product_cnt))

        # 상품 페이지 스크래핑는 함수 쓰기
        idx=prod_scraper(idx, product_cnt)

        ##################################################################
        # 페이지의 정보 가져오기
        # 페이지를 왔다갔다하면 a태그가 초기화되어 한번 더 a태그를 가져왔음
        a_tag_click=driver.find_elements(By.XPATH, &#39;//*[@id=&quot;paging&quot;]/a&#39;)
        ###################################################################

        # i(현재 페이지 순서)+1 = 다음페이지 순서 
        # 다음페이지로 이동 버튼 클릭
        a_tag_click[i+1].click()
        print(&quot;다음 페이지 이동 버튼 누름&quot;)
        time.sleep(5)

    return idx</code></pre>
<p><img src="https://velog.velcdn.com/images/r5_jun/post/b86a8354-aef3-4c9c-92c5-f139081a1932/image.png" alt="">
<code>a_tag_click[11]</code>일때 ▶(다음 10페이지 이동 버튼)을 눌러 11~20페이지를 가진 부모 페이지로 넘어가게 된다 </p>
<h2 id="끝내는-시점을-정하는-함수">끝내는 시점을 정하는 함수</h2>
<pre><code class="language-python"># 마지막 페이지 번호를 input해주자 
# 현재는 마지막 페이지가 611번이라 611를 input
total_page = input()
divid10_page = int(total_page)//10 + 1</code></pre>
<p>input입력창이 뜨면 마지막 페이지 번호를 입력해준다
<img src="https://velog.velcdn.com/images/r5_jun/post/941fea38-086b-44ee-965d-d6cda53b00f9/image.png" alt=""></p>
<pre><code class="language-python"># 인덱스 정의
idx = 1
# 총 페이지가 611이기 떄문에 페이지 10개를 62번 돌리도록 for문 작성
for a in range(1,divid10_page+1):

    # 모든 a태그를 리스트에 정의 
    a_tag_list=driver.find_elements(By.XPATH, &#39;//*[@id=&quot;paging&quot;]/a&#39;)
    # 페이지 갯수=리스트 갯수
    a_tag_cnt=len(a_tag_list)
    print(&quot;a태그수&quot;+str(a_tag_cnt))
    page_cnt=a_tag_cnt-3
    print(&quot;페이지수&quot;+str(page_cnt))

    # 10 페이지씩 돌리기 람수 쓰기
    idx=move_to_next_page(idx, page_cnt)</code></pre>
<p><code>끝내기 시점정하기 함수</code>에서 a태그를 가져오지만 <code>다음 페이지 이동하는 함수</code>에서 한번 더 a태그를 한번 더 가져오는 이유는 페이지를 이동할때마다 a태그 element가 변하게되기 때문이다.
아래에 보듯이 페이지 이동할때마다 페이지 버튼의 element가 변한다 (사실 이런 건 처음봐서 많이 헤매었다)</p>
<blockquote>
<p>처음 페이지1 버튼의 element: <strong>ce46d32f-3d13-40e0-8cb1-8f455bd9c81c</strong>
다음 페이지1 버튼의 element: <strong>ce46d32f-3d13-40e0-8cb1-8f455bd9c81c</strong>
그 다음 페이지1 버튼의 element: <strong>a5f73abc-2e09-4de7-bea9-765219ea5ac3</strong>
그 다음 페이지1 버튼의 element: <strong>b52a4385-805e-4cb7-95bb-ed1f1ca29814</strong>
그 다음 페이지1 버튼의 element: <strong>15860e6e-ea9f-46df-b102-08b2ad092ec2</strong>
...</p>
</blockquote>
<p>따라서 이전에 받아온 a태그(페이지 이동 버튼 element)를 사용하면 <code>StaleElementReferenceException</code>에러가 뜨기 때문에 사용할 수 없었고 다시 a태그를 받아와 페이지 이동을 할 수 있었다. </p>
<h1 id="결과">결과</h1>
<p>아래와 같이 모두 스크래핑 하였다
<img src="https://velog.velcdn.com/images/r5_jun/post/00aa49c3-7c98-4dea-8028-e523ed4b19bc/image.png" alt=""></p>
<h1 id="느낀점">느낀점</h1>
<p>사실 이번 웹 스크래핑 프로젝트를 하면서 옛 생각이 많이 났다. 학부 시절 첫 프로그래밍 과제도 Java 웹 스크래핑을 이용했었고 직장에서는 정보를 수집하기 위해 웹 스크래핑을 할 일이 많았다. 많은 양의 데이터를 빠짐없이 수집할 수 있는 코드를 작성하기 위해 몇 날 며칠을 야근했고 개발자 분들도 많이 귀찮게히곤 했었다. 
하지만 이제는 누군가 나에게 웹 스크래핑에 대해 물어보기도 하고 수집요청을 한다면 이렇게 반나절 만에 코드를 짤 수 있게 된 것이 개인적으로는 자랑스럽다. 
최근 제자리에 멈춰있는 느낌이라 불안감이 많았는데 이 프로젝트 덕분에 나름 위안을 많이 받았다.</p>
<p>다른 분야도 이런 식으로 발전하기를 바라며 이만 줄인다.</p>
<blockquote>
<p>마지막으로 이 글에 문제되는 점이 있으면 수정 또는 삭제할 것이며 
코드나 글 내용 중 이상한 점이 있다면 여러분의 생각이 무적권 맞으니 댓글을 남겨주시면 감사하겠습니다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>