웹 페이지를 수집한 후에, 웹 페이지 내에서 원하는 정보를 추출하는 방법에 대해서 알아봅니다.
HTML은 Hyper-text Markup Language로, 문서 표현 언어의 하나입니다. 인터넷 문서들의 대부분이 이 언어로 이루어져 있죠.
HTML의 구조를 이해하면 필요한 정보를 추출하는데 도움이 됩니다.
<html>
<head>
</head>
<body>
<div id="main-container" style="color: black;">This is a container</div>
<div class="article header">Webpage scrapying</div>
</body>
</html>HTML은 전반적으로 태그로 이루어져 있습니다. 태그란 <html></html> 처럼, 괄호(<>) 사이에 태그 이름이 들어가고, 태그는 태그 열기(<html>)와 태그 닫기(</html>)로 이루어져 있습니다. 태그 사이에는 다른 태그가 들어올 수도 있고 최종 문자열 값이 들어올 수도 있습니다.
태그의 종류에는 여러 가지가 있는데, 웹 페이지에서 정보를 추출할 때 눈여겨볼만한 유용한 태그는 다음과 같은 것들이 있습니다.
- div
- span
- p
태그에는 속성이 기재될 수 있습니다. 속성은 태그명 뒤에 자리잡고, 속성명과 값으로 이루어져 있습니다. (ex. id="main-container")
웹 페이지 추출에서 유용한 속성의 종류에는 다음과 같은 것들이 있습니다.
- id
- class
- style
우리가 흔히 보는 웹사이트는, 실제로는 여러 태그들로 구성되어 있습니다.
커다란 HTML 문서에서 내가 원하는 정보가 있는 위치를 어떻게 지칭할 수 있을까요? 모든 줄을 검사하면서 특정 문자열이 포함된 줄을 포착할 수도 있겠지만, 조금 더 쉬운 방법들이 있습니다. 그중, 웹에서 jquery 라이브러리가 등장한 이후로는 CSS selector를 지정하는 방법이 많이 사용됩니다.
가장 많이 활용되는 CSS selector 의 기본 형태는 아래와 같은 것들이 있습니다:
div: 모든div태그를 선택div#main-container:div중,id가main-container인div만을 선택div.header:div중,class가header인div들만을 선택div[style="color: black;"]:div중,style이라는 속성이color: black;이라는 값을 가지는div만을 선택
그런데, 위의 selector만으로는 지정하기 어려운 경우가 있습니다. 하나의 태그 정보만으로는 지정하기 어려운 경우에는, 두세개의 태그의 연관관계를 이용합니다. 아래와 같은 combinator selector를 사용하면, 웬만한 경우는 처리할 수 있습니다:
div.header li:div.header태그 하위에 있는 모든li를 선택div.header > ul:div.header태그의 1단계 하위에 있는 모든ul를 선택div.header li:nth-child(2):div.header태그 하위에 있는 태그 중,li가 2번째 자식인 모든li를 선택div.header li:first-child:div.header태그 하위에 있는 태그 중,li가 첫번째 자식인 모든li를 선택div.header li:last-child:div.header태그 하위에 있는 태그 중,li가 마지막 자식인 모든li를 선택
<html>
<head>
</head>
<body>
<div id="main-container" style="color: black;">This is a container</div>
<div class="article header">Webpage scrapying <a href="http://jsoup.org">Visit JSoup!</a></div>
</body>
</html>위의 HTML 코드에서, This is a container 라는 문자열을 지칭하는 CSS selector는 아래와 같습니다.
div#main-containerdiv 태그 중에서, main-container 라는 id 값을 가지고 있는 것을 지칭합니다.
Webpage scrapying 이라는 문자열을 지칭하는 CSS selector는 아래와 같습니다.
div.article그 중에서 Visit JSoup! 이라는 문자열을 지칭하는 CSS selector는 아래와 같습니다.
div.article > a태그와 CSS Selector에 대해 얼마나 이해하고 있는지 간단한 퀴즈를 통해 점검하고 지나갈까요?
BeautifulSoup은 웹 페이지 내에서 원하는 문서 구성 요소를 CSS selector 형식으로 특정할 수 있도록 도와줍니다.
BeautifulSoup은 외부 라이브러리이지만, Anaconda에 기본적으로 포함되어 있기 때문에 별도로 설치할 필요는 없습니다. Anaconda를 사용하지 않고 순수 Python 배포본을 사용하는 경우에는 아래와 같이 설치할 수 있습니다.
pip install beautifulsoup4BeautifulSoup에는 다양한 메소드들이 있는데, CSS selector를 사용하기 위해서는 select() 메소드를 사용합니다.
from bs4 import BeautifulSoup
html = '''<html>
<head>
</head>
<body>
<div id="main-container" style="color: black;">This is a container</div>
<div class="article header">Webpage scrapying</div>
</body>
</html>
'''
soup = BeautifulSoup(html, 'html5lib') # BeautifulSoup에 문서를 적재합니다
print(soup.select('#main-container')[0].string)
print(soup.select('#main-container')[0]['style'])
print(soup.select('.article')[0].string)
print(soup.select('.article')[0]['class'])This is a container color: black; Webpage scrapying ['article', 'header']
각 element에 대해서는 string 속성을 통해 값을 참조할 수 있고, dict 처럼 [] 참조를 통해 각 속성에 접근할 수 있습니다.
응용문제로 아래 URL의 HTML에서 정보를 추출해보겠습니다.
웹 문서, 특히 게시판 형태의 정보를 가져올 때는, 다음과 같은 세 부분이 필요합니다:
- 글 목록 페이지에서, 글들의 제목, URL 등을 가져오는 부분
- 글 목록 페이지 자체를, 2페이지, 3페이지 등을 거쳐서 마지막 페이지까지 순회하는 부분
- 글의 상세 내용 페이지에서, 원하는 내용 (글 본문 등)을 추출하는 부분
그래서, 글 목록 페이지로부터 수집할 문서들의 목록을 구성하고, 글 목록 페이지를 다음 페이지로 이어가면서 마지막 페이지까지 진행합니다. 그러면 해당 웹사이트의 모든 글 목록을 수집하게 됩니다. 그 후에, 각 글 목록을 순회하면서 글의 상세정보 페이지의 내용으로부터 실제 글 본문에서 필요한 내용들을 추출합니다.
우선, 하나의 글 목록 페이지에서 원하는 정보를 가져오려면 아래와 같이 진행합니다.
아래의 URL은 포털 서비스인 다음 아고라 서비스의 주소입니다. 여기에서 글 제목과 글쓴이, 글의 URL 주소를 가져오겠습니다.
http://bbs3.agora.media.daum.net/gaia/do/petition/list?bbsId=P001&objCate1=1
우선 위의 URL에 접속한 후, Chrome에서 개발자 도구를 엽니다. Ctrl-Shift-I를 누릅니다. Elements 탭에서 제일 왼쪽에 있는 마우스 모양 아이콘을 선택한 후, 확인하고자 하는 HTML 요소를 클릭합니다. 지금은 글 제목을 클릭하겠습니다. 해당 요소를 특정할 수 있는 태그 및 속성을 확인합니다.
글 제목을 클릭해보면, span 이라는 태그가 sbj 클래스(<span class="sbj">)를 가지고 있는 것을 볼 수 있습니다. 그리고 그 아래에 a 태그에 제목 문자열이 들어있습니다. 따라서 제목을 지칭하는 CSS selector는 다음과 같이 쓸 수 있습니다.
span.sbj > a개발자 도구의 Console 탭에서 $$('span.sbj > a') 라고 입력해봅시다.
이와 비슷하게, 글쓴이를 지칭하는 CSS selector는 다음과 같이 쓸 수 있습니다.
span.sbj > span.name > a개발자 도구의 Console 탭에서 $$('span.sbj > span.name > a') 라고 입력해봅시다.
이러한 CSS selector를 사용하여, 아고라 글의 제목과 글쓴이, 글의 URL 주소를 가져오는 위치는 다음과 같습니다:
- subject
- span.sbj > a
- date
- span.date
- count
- span.cnt > em
- writer
- span.sbj > span.name > a
이를 활용하여, 글 목록을 가져오는 의사 코드는 아래와 같이 표현할 수 있습니다:
글 목록 URL을 변수에 넣는다
해당 URL에서 값을 가져온다
BeautifulSoup으로 파서를 준비한다
제목, 작성일시, 청원수, 작성자 목록을 CSS Selector로 가져온다
CSS Selector로 가져온 목록을 순회하면서
제목, 작성일시, 청원수, 작성자를 출력한다
이것을 파이썬 코드로 옮겨보면 다음과 같습니다:
import requests
from bs4 import BeautifulSoup
url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/list?pageIndex=1&bbsId=P001&objCate1=1'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html5lib')
subjects = soup.select('span.sbj > a')
dates = soup.select('span.date')
counts = soup.select('span.cnt > em')
writers = soup.select('span.sbj > span.name > a')
entry_size = len(subjects)
for idx in range(entry_size):
subject = subjects[idx].string
date = dates[idx].string
writer = writers[idx].string
count = counts[idx].string
href = subjects[idx].attrs['href']
print(subject, date, writer, count, href)국정원 4대강 민간인 사찰 문건 나와있어 진실규명 해야 합니다 2018.07.04 22:49 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215990&pageIndex=1 아시아나 기내식 대란.. 사과했지만 엄중히 책임을 물어야 합니다 2018.07.04 22:33 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215989&pageIndex=1 공사대금 못받아 건설 하청업체 대표 분신사망 경찰은 조사하라 2018.07.04 22:18 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215988&pageIndex=1 2018 아시안게임 마장마술 선발전 재심사 청원합니다!적폐청산! 2018.07.04 20:58 쏭쏭쏭 0 read?bbsId=P001&objCate1=1&articleId=215987&pageIndex=1 양산대방노블랜드 8차 관리사무소에서 유독성 물질보관 2018.07.04 13:57 햇님이 방긋 0 read?bbsId=P001&objCate1=1&articleId=215985&pageIndex=1 [국민감사] '민생' 문제를 청와대 와 거래한 대법관을 2018.07.04 13:08 서재황 0 read?bbsId=P001&objCate1=1&articleId=215984&pageIndex=1 난민이 제주도 출도제한 소송. 이거 막아야하고 관련법개정촉구 2018.07.04 12:59 색불루 6 read?bbsId=P001&objCate1=1&articleId=215983&pageIndex=1 [국민감사] 청와대 게시글을 '통편집' 한 청와대직원을 2018.07.04 12:09 서재황 0 read?bbsId=P001&objCate1=1&articleId=215982&pageIndex=1 여고생 2명 서울 아파트 옥상 동반 투신.. 진상규명 해야 합니다 2018.07.03 22:23 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215981&pageIndex=1 홍준표 전 경남지사 채무제로 표지석 철거해야 합니다 2018.07.03 22:12 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215980&pageIndex=1 [국민감사] 대법관 조재연,고영한,김소영,권순일 을 직권남용, 2018.07.03 21:24 서재황 0 read?bbsId=P001&objCate1=1&articleId=215979&pageIndex=1 트럼프대통령님 저의집 인권좀 살펴주세요 2018.07.03 21:23 ww8401 0 read?bbsId=P001&objCate1=1&articleId=215978&pageIndex=1 세월호 유가족 조직적 사찰한 기무사.. 사과로 끝날일이 아니다 2018.07.03 16:37 햇살마당 4 read?bbsId=P001&objCate1=1&articleId=215977&pageIndex=1 국민의 적 금융감독원 임흥진 이와 조현재 2018.07.03 16:24 이천곤 0 read?bbsId=P001&objCate1=1&articleId=215976&pageIndex=1 무소속 3인방 더불어민주당 입당설.. 받아들이면 절대 안됩니다 2018.07.03 16:18 햇살마당 1 read?bbsId=P001&objCate1=1&articleId=215975&pageIndex=1 [국민감사] 서울고등법원 2018초재1814 사건관련 제30형사부 를 2018.07.03 13:22 서재황 0 read?bbsId=P001&objCate1=1&articleId=215973&pageIndex=1 [국민감사] 서울고등법원 2018초재1798 사건관련 제30형사부 를 2018.07.03 13:19 서재황 0 read?bbsId=P001&objCate1=1&articleId=215972&pageIndex=1 자신의 잘못을 모르는 교사에게 죄를 묻습니다 2018.07.03 12:27 천사들의둥지 20 read?bbsId=P001&objCate1=1&articleId=215971&pageIndex=1 언론과 표현의자유 탄압 국가인권정책 기본계획 철회해야합니다 2018.07.03 11:43 색불루 3 read?bbsId=P001&objCate1=1&articleId=215970&pageIndex=1 [국민감사] '민생' 문제를 청와대 와 거래한 대법관을 2018.07.03 10:39 서재황 0 read?bbsId=P001&objCate1=1&articleId=215969&pageIndex=1
우선, 위에서 글 하나의 링크 주소(href)를 살펴봅시다.
read?bbsId=P001&objCate1=1&articleId=215978&pageIndex=1
이 주소는 상대주소입니다. 이것에 아고라 주소를 이어붙여서 절대경로로 만들면 아래와 같이 될겁니다.
http://bbs3.agora.media.daum.net/gaia/do/petition/read?bbsId=P001&objCate1=1&articleId=215978&pageIndex=1
그래서, 기존의 href 앞에 http 경로를 붙여서 반환해줍니다.
import requests
from bs4 import BeautifulSoup
url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/list?pageIndex=1&bbsId=P001&objCate1=1'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html5lib')
subjects = soup.select('span.sbj > a')
dates = soup.select('span.date')
counts = soup.select('span.cnt > em')
writers = soup.select('span.sbj > span.name > a')
entry_size = len(subjects)
for idx in range(entry_size):
subject = subjects[idx].string
date = dates[idx].string
writer = writers[idx].string
count = counts[idx].string
href = subjects[idx].attrs['href']
print(subject, date, writer, count, 'http://bbs3.agora.media.daum.net/gaia/do/petition/' + href)글의 목록은 이렇게 가져올 수 있고, 다음 페이지의 글 목록을 가져오려면 어떻게 해야 할까요? 다음 페이지 링크를 어떻게 구할 수 있는지 살펴봅시다.
개발자 도구에서 살펴보니, #num > a 라는 CSS selector로 페이지 지시자를 가져올 수 있을 것 같습니다.
import requests
from bs4 import BeautifulSoup
url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/list?bbsId=P001&objCate1=1'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html5lib')
next_urls = soup.select('#num > a')
entry_size = len(next_urls)
for idx in range(entry_size):
next_url = next_urls[idx].attrs['href']
print(next_url)list?pageIndex=2&objCate1=1&bbsId=P001 list?pageIndex=3&objCate1=1&bbsId=P001 list?pageIndex=4&objCate1=1&bbsId=P001 list?pageIndex=5&objCate1=1&bbsId=P001 list?pageIndex=6&objCate1=1&bbsId=P001 list?pageIndex=7&objCate1=1&bbsId=P001 list?pageIndex=8&objCate1=1&bbsId=P001 list?pageIndex=9&objCate1=1&bbsId=P001 list?pageIndex=10&objCate1=1&bbsId=P001
pageIndex 가 2, 3, 4, ... 10 인 URL을 얻었습니다.
여기서 역시 절대 경로로 만들어주기 위해 경로 앞에 http 주소를 붙입니다.
import requests
from bs4 import BeautifulSoup
url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/list?bbsId=P001&objCate1=1'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html5lib')
next_urls = soup.select('#num > a')
entry_size = len(next_urls)
for idx in range(entry_size):
next_url = next_urls[idx].attrs['href']
print('http://bbs3.agora.media.daum.net/gaia/do/petition/' + next_url)이번에는 특정한 게시물 하나를 방문해보겠습니다.
게시물 주소 하나를 웹브라우저로 열고 개발자 도구를 띄워서, 본문 및 원하는 정보를 어떻게 추출할 수 있는지 살펴봅니다.
http://bbs3.agora.media.daum.net/gaia/do/petition/read?bbsId=P001&objCate1=1&articleId=215978&pageIndex=1
그렇게 살펴보니, 아고라 글에서는 .article 이라는 CSS selector로 본문을 추출할 수 있을 것으로 보입니다.
import requests
from bs4 import BeautifulSoup
url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/read?bbsId=P001&objCate1=1&articleId=215978&pageIndex=1'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html5lib')
contents = soup.select('.article')
content = contents[0].text
print(content)이명박 정권부터 금감원 조사국 사람들로 의심돼는 괴한들이 아이들이 쓰는 컴퓨터 와 저가쓰는컴퓨터 을 악성코드심어 사용못하게만들업읍니다 내용은다음과 같읍니다하드디스크 고장내고요크래픽카드 고장내고요USB꼽으면 내용물 싹지우고요 더이상못쓰게 포멧도않돼게안듭니다겜 접속하면 그림깨트리고 요로그인 안돼게만들고요강제종료 시키고 요안켜지게 만들고 요무한 반복종료 시키고 요터보빽업 프로그램 안돼게만들고 요화면 멈추게만들고요마우스 포인트 안움직이게 만들고요인테넷 부라우즈 뛰우면 수십페지 뜨게만들어 인테넷 못하게 만들고요악성코드을심어 컴퓨터 성능을 3분의1로 떨어터려 놓읍니다 요때XP쓰다가 2000으로 다운그래이 시키면 컴토가 너무빨라져서 마우스에 손을 못올릴지경됌니다즐겨찿기해놓은 페지 차단하여 안뜨게만들고요케이벤치 각종 자료 들을 다운로드 안돼게 만들고요케이벤치 기사클릭하면 중단됏다고 뜨고요 G마켓 면도기사는 데 2시간해도 않돼 피방가서 사게만들고요국민인권위 에 위내용으로 진정서 올릴려고하면 글 안올라가게 만들고요 그래서 요즘은 컴퓨터 로 인터넷 을 일절 사용안햇더니요2014.6월부턴 밥통에 유해물질을 뿌려 냄세나게 만들고요2015년1월 부턴요 1차세계 대전때 독일군이 쓰던 독가스 을 뿌리는지가슴이 답답하면서 숨쉬기가 않돼고요눈 이 충혈 돼면서 눈에서 진물이 나오고 시력이 나빠짐니다학생들 학교 마치고 집에와 먹어라 음식물 해놓면 유해물질 뿌려 냄새나게 만들고요이사람들이 얼마나 신출 귀몰한지 CCTV 카메라 달아놔도 나오지않코요 절대로 열수업다고 열쇠업자가 말해서 달아논 디지털키 8자리 암호도 가볍게통과하고요귀신이 아니면 피할수업다고하던 무인전자 경비 시스템 캡스도 귀신같이 피하고요특히 매일들와서 뿌려도 그어떤 침입 흔적을 남겨놓치않읍니다 투명망토을쓰고 마술사 데이비드 카퍼필드 처럼1406호 옆집 벽을 뚤고 들어와 뿌리고 나가나 봅니다 사람이 어떻케 무인경비시스템 과 CCTV 와 8자리암호 디지털 잠금장치 등 (매일암호을봐꿔봣음니다) 이세가지 을 통과할수잇읍니까 ㅠㅠ 특수목적으로 훈련됀 요원들을 무고한 어린학생 을 테러하는데 쓰는건 아닌지요첨엔 유독가스 용액을 그냥 막뿌려 제가 물걸래질 하여 닥아냇더니 요 요즘은 먼지가 오래동안 쌓인것처럼 위장하여 온집안 에 엄청 뿌려 놓읍니다 저가 IMF 때 실직하여 노점상을 하엿으나 돈을 못벌자 아이엄마 가 10여년전 가출하여 아이들을 돌봐줄 사람이 업읍니다 고등3학년 딸아이가 이 독가스을 마시고 감기인줄알고 병원치료을 한동안 받앗읍니다2015.1월초 인터넷 두어번 비슷한 내용글을 올린후 제가노점상하는곳에와서 유독가스을 뿌리는데요 주로 장사마치고난후 밤에 와서 뿌리는데요 아침에와서 대충청소하고 그냥 대충 살고 잇읍니다 늙거죽을때 다대 그렇읍니다실직하여 직장도업고 마누라도 도망가고 업고 그렇타고 돈이잘벌리는것도아니고 하루수입(1만원~2만원) 사는게 너무힘들어 그냥 살다죽을려고 요2015.7월초 부턴요 낯에도 와서 뿌리는데요 잠시졸거나 TV에 정신팔려잇음 반경1미터 이내 저가잇는곳에 뿌려놓코 가는데요 사방을둘러봐도 사람이 안보입니다 저멀리서오는사람들은 보여도 지나가는사람들은 업는거죠특수작전 하는사람들은 투명망토을 만들어 쓰고 마술사처럼옆집140 6호에서 벽을통과해 다는지 보이질않읍니다 정말 신출귀몰합니다아이들만 잇는 집에도 뿌리는걸로봐선 아이들을 표적으로 하는것같읍니다2015.1월1일 부터유독 물질을 뿌렷는데요 검은색 갈색 빨간색 투명한액체 등을 뿌리더니요 2015. 6월28일부턴 한가지색을추가해 흰색 유해물질을 뿌립니다 유리창공사후 실리콘 총으로 쏘는것 같이 발라놓는데요 떨어지지도안읍니다 눈에보이지않는 유령처럼 흔적업이 침입하여 무고한 저와 어린학생들을 테러하는 데요 어떻케하면 이런일을 해결할수잇을가요 좀알려주세요위글을 올린후 2015.9.초부턴 저가쓰는 옷 이불 수건 그릇 의자 주전자 남비 등등 입고잇는 옷빼고 유독물질을 다뿌려놓읍니다 정말죽을지경입니다 카메라로 보면서 유독물질을 뿌리는지 유독물질을 피해 다른곳으로 피해자면 담날 그기뿌려놓코그럽니다지난9년동안 괴롭희면서 조사해도 노점상하는 죄 박에 업어서 노점상도 못하게 다른사람시켜 구청에신고하고 그럽니다 이명박 대통령부터시작해 3번 바꿧는데요 3천배 더 괴롭힙니다결국 노점상 철거 돼 아이들 대학도 못보네고 롯데그룹 산하 롯데슈퍼 주차장 차량 유도원 취직햇는데요 여기기지 와서 유독물지로 추정대는 물질과 유독가스 을뿌립니다 일도못하게합니다 노점상할때 엄청 보고 냄새 맏타 왓읍니다유해물질을 머리부터 발끝까지 뿌리는데요 패 눈 코 아래잇몸 위 장대장 콩팥 요 부위 위주로 공격해옴니다 살다가 병들어 죽은것처럼 보이게 내장들을 공격 하는것 같읍니다 피부는 손상업이 내 장기만 상하게 만드는 유해 물질과 세균이 잇다는것에 놀라울 뿐 입니다트럼프대통령님 저의집 인권좀 살펴주세요
여기까지 해서, 글 목록 가져오기, 글 목록 페이지의 다음 페이지로 계속 진행하기, 특정 글 페이지에서 원하는 정보 가져오기를 각각 살펴보았습니다. 웹 스크래핑 프로그램은 대부분의 경우 이 틀을 크게 벗어나지 않습니다. 이것들을 유기적으로 조합해서, 해당 웹사이트의 전체 글을 가져오는 프로그램을 작성할 수 있습니다.
위에서 작성한 코드를 함수로 한번 만들어보겠습니다.
import requests
from bs4 import BeautifulSoup
def get_article_list(list_url):
response = requests.get(list_url)
soup = BeautifulSoup(response.text, 'html5lib')
subjects = soup.select('span.sbj > a')
dates = soup.select('span.date')
counts = soup.select('span.cnt > em')
writers = soup.select('span.sbj > span.name > a')
entry_size = len(subjects)
result = []
for idx in range(entry_size):
subject = subjects[idx].string
date = dates[idx].string
writer = writers[idx].string
count = counts[idx].string
href = subjects[idx].attrs['href']
result.append([subject, date, writer, count, 'http://bbs3.agora.media.daum.net/gaia/do/petition/' + href])
return result
def get_next_pages(list_url):
response = requests.get(list_url)
soup = BeautifulSoup(response.text, 'html5lib')
next_urls = soup.select('#num > a')
entry_size = len(next_urls)
result = []
for idx in range(entry_size):
next_url = next_urls[idx].attrs['href']
result.append('http://bbs3.agora.media.daum.net/gaia/do/petition/' + next_url)
return result
def get_article_content(content_url):
response = requests.get(content_url)
soup = BeautifulSoup(response.text, 'html5lib')
contents = soup.select('.article')
content = contents[0].text
return content이것들을 조합해서 유기적으로 돌아가도록 만드는 것까지는 조금 더 복잡한 작업을 필요로 합니다.
첫 시작 목록 URL (seed URL)을 입력한다
방문할 글 목록 URL 리스트를 저장할 빈 리스트를 만든다
방문한 글 목록 URL 리스트를 저장할 빈 셋을 만든다
현재 목록 페이지에 대해 순회한다
만약 목록 페이지가 이전에 방문한 적이 있다면
이번 순회는 건너뛴다
글 목록을 추출한다
각 글에 대해서 순회한다
글 URL에 방문해서 글 내용을 가져온다
글 내용을 파일에 기록한다
다음 목록 페이지 URL들을 추출한다
방문한 글 목록 URL 셋에 추가한다
이걸 코드로 한번 옮겨보겠습니다.
def scrape(seed_url):
# 방문할 글 목록 URL 리스트를 저장할 빈 리스트를 만든다
waiting_article_list = [seed_url]
# 방문한 글 목록 URL 리스트를 저장할 빈 셋을 만든다
visited_article_list = set()
# 현재 목록 페이지에 대해 순회한다
while len(waiting_article_list):
# 현재 목록 페이지에 대해 순회한다
current_list_url = waiting_article_list.pop(0)
print(current_list_url)
# 만약 목록 페이지가 이전에 방문한 적이 있다면
if current_list_url in visited_article_list:
# 이번 순회는 건너뛴다
continue
# 글 목록을 추출한다
article_info_list = get_article_list(current_list_url)
# 각 글에 대해서 순회한다
for article_info in article_info_list:
# 글 URL에 방문해서 글 내용을 가져온다
subject, date, count, writer, article_url = article_info
# 글 내용을 파일에 기록한다
content = get_article_content(article_url)
with open('agora.txt', 'a', encoding='utf8') as fout:
fout.write('Subject: ' + subject)
fout.write('\n')
fout.write('Date: ' + date)
fout.write('\n')
fout.write('Count: ' + count)
fout.write('\n')
fout.write('Writer: ' + writer)
fout.write('\n')
fout.write(content)
fout.write('\n')
next_page_urls = get_next_pages(current_list_url)
waiting_article_list = waiting_article_list + next_page_urls
visited_article_list.add(current_list_url)# 첫 시작 목록 URL (seed URL)을 입력한다
seed_url = 'http://bbs3.agora.media.daum.net/gaia/do/petition/list?pageIndex=1&bbsId=P001&objCate1=1'
scrape(seed_url)이렇게, 간단하게나마 전체 게시글을 순회하면서 내용을 가져올 수 있는 기초적인 코드를 완성했습니다.
여기서 조금 더 나아가자면:
- 현재는 프로그램이 중간에 문제(오류 등)가 생겨서 중단하면, 처음부터 다시 수행하게 됩니다. 현재 방문하는 URL을 파일에 기록하거나 하여, 프로그램 중단시에도 이전에 수행하던 작업을 이어서 진행할 수 있게 합니다.
- 현재는 문제를 간소화하기 위해 하나의 파일에 모든 결과를 이어붙이고 있습니다. 실제의 경우에는 하나의 파일에는 하나의 URL 내용만 기록하는 것이 활용성이 더 좋습니다.
더 궁금하신 분들은 제가 작성했던 스크래핑 코드 1, 2를 참고하세요.
요즘 만들어지는 웹사이트들 중에는, HTML로 모두 미리 작성되는 대신, 서버로부터는 데이터만을 받고 웹브라우저에서 동적으로 HTML 문서 구조를 생성하는 경우가 많습니다. 이렇게 서버로부터 데이터를 받을 때 사용하는 데이터의 형식으로 최근 많이 사용되는 것이 JSON(Javascript Simple Object Notation)입니다. JSON은 아래와 같은 모양을 가집니다.
{
'people': [
{'name': 'Tom', 'age': 23},
{'name': 'John', 'age': 30}
]
}가만히 보면 Python에서 list 나 dict 을 표현하는 방식과 비슷하게 보이지 않나요? 실제로 requests 라이브러리에서는 JSON 형식을 python의 dict 와 list 형태로 변환해서 반환합니다.
Tistory의 예를 한번 살펴볼까요?
아래 URL은 IT/인터넷 카테고리에 새로 올라온 글을 보여주는 페이지의 주소입니다.
http://tistory.com/category/it/internet
크롬 웹브라우저에서 페이지를 방문해서 개발자 도구로 Network 탭을 살펴보면, 아래 URL이 실제 글 목록 내용을 담고 있는 문서라는 것을 알 수 있습니다.
http://tistory.com/category/getMoreCategoryPost.json
실제 내용을 살펴볼까요?
{
"error":false,
"data":{
"lastPublished":1514558042000,
"list":[
{"daumLikeUid":"2856430_14","title":"LEC. 01 : 파이썬 시작","summary":"프로그래밍 언어를 가장 빨리 익히는 방법은 역시 Learn by doing, 직접 타이핑하고 실행해보면서 익히는 것이다. 그렇다고해서 아무런 사전지식 없이 바로 코딩을 시작 하는 것 보다 전체적인 내용을 빠르게 훑고 관심있는 예제 코드를 작성하고 실행해보면서 모르는 부분을 찾아보는 것이 훨씬 효율적일 것이다. 지금부터 파이썬을 머릿속에 정리해보자. 1. 파이..","userName":"대봉씨","categoryName":"IT 인터넷","thumbnail":"","url":"http://daebongssi.tistory.com/14","best":false,"likeCount":0,"published":"2017.12.29 23:48","encodedTitle":"LEC.%2001%20%3A%20%ED%8C%8C%EC%9D%B4%EC%8D%AC%20%EC%8B%9C%EC%9E%91"},
{"daumLikeUid":"2745913_15","title":"[알고리즘] 백준 8958번 OX퀴즈 재도전","summary":"수요일에 풀어본 백준 8958번 OX문제를 다시 풀어보았으나 도저히 풀리지 않아서 결국 다른 블로그 https://fatc.club/2017/03/01/991 에서 코드를 긁어오게되었습니다.ㅠㅠㅠㅠ 이런식으로 코드를 짤 수 있다고 하는데요 내일 플이에 대한 설명을 덧붙이도록 하겠습니다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34..","userName":"bae_wang","categoryName":"IT 인터넷","thumbnail":"","url":"http://blue-wnag.tistory.com/15","best":false,"likeCount":0,"published":"2017.12.29 23:48","encodedTitle":"%5B%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%5D%20%EB%B0%B1%EC%A4%80%208958%EB%B2%88%20OX%ED%80%B4%EC%A6%88%20%EC%9E%AC%EB%8F%84%EC%A0%84"},
{"daumLikeUid":"2771216_174","title":"컴퓨터 공인인증서 위치 및 삭제 방법","summary":"인터넷 뱅킹을 하거나 공공기관 홈페이지에 접속하는 경우에는 공인인증서가 반드시 필요합니다. 그런데 내 컴퓨터에 저장되어 있는 공인인증서를 복사하거나 삭제해야 하는 경우가 생깁니다. 그럼 이제부터 컴퓨터 공인인증서 위치 및 삭제 방법에 대하여 알아보겠습니다. 컴퓨터 공인인증서 위치 및 삭제 방법 컴퓨터를 교체하거나 윈도우를 재설치할 때 공인인..","userName":"미네르바 올..","categoryName":"IT 인터넷","thumbnail":"http://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile21.uf.tistory.com%2Fimage%2F9924F14B5A465470141FB5","url":"http://smart365.tistory.com/174","best":false,"likeCount":0,"published":"2017.12.29 23:46","encodedTitle":"%EC%BB%B4%ED%93%A8%ED%84%B0%20%EA%B3%B5%EC%9D%B8%EC%9D%B8%EC%A6%9D%EC%84%9C%20%EC%9C%84%EC%B9%98%20%EB%B0%8F%20%EC%82%AD%EC%A0%9C%20%EB%B0%A9%EB%B2%95"}
],
"category":"it/internet"
}
}Python의 자료구조 표현형과 대부분 유사하지만, false 라고 표현되어 있는 부분은 약간 다릅니다. Python에서는 False 라고 표현해야 합니다. 이 외에도 Python에서의 None 을 javascript에서는 null 이라고 표현하는 등, 약간의 차이점은 있지만, 전반적으로는 이해하는데 큰 무리가 없습니다.
웹페이지에서 서버로부터 정보를 받아오는 과정을 관찰하고, 그 요청 질의를 모방해서 아래와 같이 정보를 Python에서 직접 받아올 수 있습니다.
import requests
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0',
'T-Ajax': '151455907333',
'X-Requested-With': 'XMLHttpRequest'}
cookies = {'TISTORY_THEME_ORDER': 'recent'}
data = {'category': 'it',
'first': True,
'lastPublished': 0}
response = requests.post('https://tistory.com/category/getMoreCategoryPost.json', data=data, headers=headers, cookies=cookies)
print(response.json()){'data': {'lastPublished': 1530714238000, 'list': [{'url': 'http://slic.tistory.com/1464', 'likeCount': 58, 'userName': 'Total Fix!', 'categoryName': 'IT 인터넷', 'encodedTitle': '%5B%20%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%20%5D%20Wi-Fi%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC%20%EB%82%B4%20%EC%9C%84%EC%B9%98%EB%A5%BC%20%EC%B6%94%EC%A0%81%ED%95%98%EB%8A%94%20..', 'best': True, 'daumLikeUid': '976931_1464', 'summary': '와이파이 기반 위치 획득 체계 ( Wi-Fi positioning system , WPS)는 스카이훅 와이어리스(Skyhook Wireless)가 Wi-Fi 기반 위치 인식 시스템을 위해 만든 용어입니다. 오늘날에는 Google, Apple 및 Microsoft와 같은 다른 회사에서도 Wi-Fi만을 기반으로 사용자의 위치를 찾는 데 사용되고 있습니다. 때로는 GPS 관련 앱이 Wi-Fi를 켜고 더 정확한 위치를 요청하..', 'published': '2018.07.03 01:05', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile29.uf.tistory.com%2Fimage%2F997A573F5B3A4B35295383', 'encodedUserName': 'Total%20Fix%21', 'title': '[ 네트워크 ] Wi-Fi를 사용하여 내 위치를 추적하는 ..'}, {'url': 'http://slic.tistory.com/1465', 'likeCount': 36, 'userName': 'Total Fix!', 'categoryName': 'IT 인터넷', 'encodedTitle': '%5B%20Windows%2010%20%5D%20%ED%8C%8C%EC%9D%BC%20%EB%B0%8F%20%ED%94%84%EB%A6%B0%ED%84%B0%20%EA%B3%B5%EC%9C%A0%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EB%8F%84%EB%A1%9D%20..', 'best': True, 'daumLikeUid': '976931_1465', 'summary': 'Windows 10은 기본적으로 개인 네트워크에서만 파일 및 프린터 공유를 허용합니다. 만약 공용 네트워크로 설정되어 있다면 먼저 개인 네트워크로 변경하셔야 합니다. 참고 : [ Windows 10 ] 개인 또는 공용 네트워크로 설정 Windows 10에 파일 또는 프린터 공유를 사용하려면 Win + i 를 눌러 설정 창을 열고 네트워크 및 인터넷 을 클릭 이동합니다. 왼쪽 창에서..', 'published': '2018.07.03 22:18', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile5.uf.tistory.com%2Fimage%2F993CEA475B3B769611C355', 'encodedUserName': 'Total%20Fix%21', 'title': '[ Windows 10 ] 파일 및 프린터 공유를 사용하도록 ..'}, {'url': 'http://slic.tistory.com/1466', 'likeCount': 30, 'userName': 'Total Fix!', 'categoryName': 'IT 인터넷', 'encodedTitle': '%5B%20Windows%2010%20%5D%20SMB1%20%EA%B3%B5%EC%9C%A0%20%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%20%EC%82%AC%EC%9A%A9', 'best': True, 'daumLikeUid': '976931_1466', 'summary': 'SMB(Server Message Block) 프로토콜은 Microsoft Windows의 네트워크 파일 공유 프로토콜입니다. Windows 10과 Windows Vista 이전 시스템 또는 Android 또는 Linux 응용 프로그램을 실행하는 컴퓨터가 있는 경우 공유를 위하여 SMB1 프로토콜이 필요 할 수 있습니다. SMB1은 오래되었고 안전하지 않습니다. Microsoft에서도 SMB1을 더는 사용하지 않..', 'published': '2018.07.04 01:38', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile21.uf.tistory.com%2Fimage%2F99CC77485B3B9DA82A877D', 'encodedUserName': 'Total%20Fix%21', 'title': '[ Windows 10 ] SMB1 공유 프로토콜 사용'}, {'url': 'http://viewingcat.tistory.com/825', 'likeCount': 5, 'userName': '야옹이파', 'categoryName': '모바일', 'encodedTitle': '%5B%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%5D%20%EC%98%A4%EB%8A%98%EC%9D%98%20%EB%AC%B4%EB%A3%8C%EC%95%B1%2018.07.05.', 'best': False, 'daumLikeUid': '2832070_825', 'summary': '오늘의 무료앱을 소개합니다. 매일 아침 7시에 올릴려고 노력중이니, 한번씩 놀러오셔서 무료앱 챙겨가세요. [게임] Galaxy Defense 타워 디펜스 - 전략 게임 https://play.google.com/store/apps/details?id=com.zonmob.TowerDefense.TD 갤럭시 국방 최선의 전략 / 아케이드 게임! [게임] Hide N Seek : Mini Game https://play.google.com/store/apps/det..', 'published': '2018.07.05 07:00', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile24.uf.tistory.com%2Fimage%2F996A69395B3D089408A013', 'encodedUserName': '%EC%95%BC%EC%98%B9%EC%9D%B4%ED%8C%8C', 'title': '[안드로이드] 오늘의 무료앱 18.07.05.'}, {'url': 'http://koreabackpacking.com/894', 'likeCount': 8, 'userName': '코리아배낭..', 'categoryName': 'IT 제품리뷰', 'encodedTitle': '%EB%86%92%EC%9D%B4%EC%A1%B0%EC%A0%88%20%EB%AA%A8%EB%8B%88%ED%84%B0%20%EB%B0%9B%EC%B9%A8%EB%8C%80%20%EC%A0%9C%EB%8B%89%EC%8A%A4%20%EB%AA%A8%EB%8B%88%ED%84%B0%20%EB%B0%9B%EC%B9%A8%EB%8C%80%20ZIGN..', 'best': False, 'daumLikeUid': '1871898_894', 'summary': '집에서 듀얼모니터를 사용하고 있는데 모니터 2대의 크기가 27인치와 24인치로 달라서 높이도 달라 불편해서 모니터 높이라도 같게하기 위해서 높이조절 모티터 받침대를 구입하였습니다. 높이조절 모니터 받침대 제닉스 모니터 받침대 ZIGNUM M-DESK F1 모니터의 높이는 사람의 눈높이일때가 가장 적합한 높이로 모니터 자체에서 높이를 조절 할 수 있다면 ..', 'published': '2018.07.05 07:00', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile23.uf.tistory.com%2Fimage%2F990AC4475B3D15DB1E2E20', 'encodedUserName': '%EC%BD%94%EB%A6%AC%EC%95%84%EB%B0%B0%EB%82%AD..', 'title': '높이조절 모니터 받침대 제닉스 모니터 받침대 ZIGN..'}, {'url': 'http://intunknown.tistory.com/266', 'likeCount': 5, 'userName': '추향', 'categoryName': 'IT 인터넷', 'encodedTitle': '%EC%9C%88%EB%8F%84%EC%9A%B010%20windows%20%EC%B6%94%EC%B2%9C%20%EB%B0%B0%EA%B2%BD%ED%99%94%EB%A9%B4%20%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0', 'best': False, 'daumLikeUid': '2743368_266', 'summary': 'windows 추천 배경화면 저장하기 윈도우를 설치하고 따로 설정하지 않았다면 잠금화면의 배경이 windows10에서 추천하는 배경으로 되어있을것입니다. 기본설정이 windows 추천이니까요. windows 추천? 뭐 할말은 없지만 간단하게 설명을 해겠습니다. windows 추천 배경화면은 시간이 지나면 바뀝니다. 가끔 이쁜화면이 나옵니다.(물론 내 기준.) 이 글을 ..', 'published': '2018.07.05 06:48', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile22.uf.tistory.com%2Fimage%2F99C9DF455B3D3D70109EA3', 'encodedUserName': '%EC%B6%94%ED%96%A5', 'title': '윈도우10 windows 추천 배경화면 저장하기'}, {'url': 'http://jongamk.tistory.com/4872', 'likeCount': 5, 'userName': '핑구야 날자', 'categoryName': 'IT 제품리뷰', 'encodedTitle': '%EC%97%98%EC%A7%80%20%EC%97%91%EC%8A%A4%EB%B6%90%EA%B3%A0%20PK7%EB%A1%9C%20%EC%9D%8C%EC%84%B1%EC%9D%B8%EC%8B%9D%EA%B8%B0%EB%8A%A5%EA%B3%BC%20%EB%B3%B4%EB%8A%94%20%EC%A6%90%EA%B1%B0%EC%9B%80%20..', 'best': False, 'daumLikeUid': '290949_4872', 'summary': '엘지 엑스붐고 PK7을 직접 사용해 보니 보는 즐거움도 추가해 색다른 거 있죠. 최근에 출시한 LG 블루투스 스피커는 3가지 모델로 PK7, PK5, PK3입니다. 엑스붐고 라인업의 성능과 사이즈는 PK7 > PK5 > PK3 순이며 PK3은 좀 더 기다려야 만나볼 수 있을 것 같아요. 엘지 엑스붐고 라인업의 가장 큰 특징 중에 하나는 영국의 대표 하이앤드 오디오 전문기업 메리..', 'published': '2018.07.05 06:30', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile25.uf.tistory.com%2Fimage%2F99AAE64B5B3AB41D200077', 'encodedUserName': '%ED%95%91%EA%B5%AC%EC%95%BC%20%EB%82%A0%EC%9E%90', 'title': '엘지 엑스붐고 PK7로 음성인식기능과 보는 즐거움 ..'}, {'url': 'http://androidsfactory.com/251', 'likeCount': 6, 'userName': '팩토리사장', 'categoryName': '모바일', 'encodedTitle': '%EC%B5%9C%EC%8B%A0%20%EA%B5%AC%EA%B8%80%20%ED%94%8C%EB%A0%88%EC%9D%B4%20%EC%8A%A4%ED%86%A0%EC%96%B4%20apk%20%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C', 'best': False, 'daumLikeUid': '1789888_251', 'summary': '안드로이드 스마트폰에 커스텀 롬을 설치하면 구글 플레이 스토어가 없이 설치되는 경우가 있습니다. 그럴 경우에는 구글 플레이 스토어 apk 파일이 필요한데요. 최신 구글 플레이 스토어 apk파일 다운로드를 지속적으로 받을 수 있는 페이지를 오늘 소개합니다. 구글 플레이 스토어 다운로드 https://www.apkmirror.com/apk/google-inc/google-play-store/ 구..', 'published': '2018.07.05 01:56', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile3.uf.tistory.com%2Fimage%2F9980EA415B3CF92117533E', 'encodedUserName': '%ED%8C%A9%ED%86%A0%EB%A6%AC%EC%82%AC%EC%9E%A5', 'title': '최신 구글 플레이 스토어 apk 다운로드'}, {'url': 'http://minuhome.tistory.com/3634', 'likeCount': 12, 'userName': 'MINU HOM..', 'categoryName': 'IT 제품리뷰', 'encodedTitle': '%EC%82%BC%EC%84%B1%20%ED%85%8C%EB%A7%88%EC%8A%A4%ED%86%A0%EC%96%B4%20MINU%20%ED%8C%94%EB%A1%9C%EC%9A%B0%2017%EB%A7%8C%20%EB%8F%8C%ED%8C%8C%EA%B8%B0%EB%85%90%20%EC%9D%B4%EB%B2%A4..', 'best': False, 'daumLikeUid': '1639173_3634', 'summary': '● 이벤트 내용 ◈삼성 테마스토어 MINU 팔로우 17만 돌파기념 이벤트◈ MINU 테마 팔로우 18만명 기념 케이스와 리딤코드를 고객님들께 나눠드립니다. ● 이벤트 정보 이벤트기간 : 2018년 7월 5일 ~ 7월 12일 이벤트상품 : 갤럭시S9플러스 케이스(커스텀 하드케이스), MINU테마 유료 리딤코드(10매) 이벤트참여방법 : ▶해당 링크 를 통해 질문 및 받고싶은 제..', 'published': '2018.07.05 00:48', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile23.uf.tistory.com%2Fimage%2F998F10395B3CEB590DF79E', 'encodedUserName': 'MINU%20HOM..', 'title': '삼성 테마스토어 MINU 팔로우 17만 돌파기념 이벤..'}, {'url': 'http://intunknown.tistory.com/265', 'likeCount': 6, 'userName': '추향', 'categoryName': 'IT 인터넷', 'encodedTitle': '%EA%B5%AC%EA%B8%80%20%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B8%8C%20%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%98%EC%A7%80%20%EC%95%8A%EC%9D%8C%20%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95', 'best': False, 'daumLikeUid': '2743368_265', 'summary': "구글 드라이브 오류. 구글 드라이브를 사용하기 위해 구글드라이브에 접속했다가 오류를 발견했습니다. 로그인하지 않음. 로그아웃되었습니다. 다시 로그인한 뒤 '다시 시도'를 클릭하세요. 이런 팝업창이 나오더군요. 일단 이것을 해결하는 해결책에는 여러가지가 있겠지만 몇 가지만 설명드리겠습니다. 첫 번째 해결방법 브라우저를 바꾸는 방법입니다. 저는 인터..", 'published': '2018.07.04 23:44', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile22.uf.tistory.com%2Fimage%2F998F4F475B3D41DE239FCF', 'encodedUserName': '%EC%B6%94%ED%96%A5', 'title': '구글 드라이브 로그인하지 않음 해결방법'}, {'url': 'http://it-talktalk.tistory.com/123', 'likeCount': 6, 'userName': '도동쓰', 'categoryName': '모바일', 'encodedTitle': '%EC%82%BC%EC%84%B1%20%EC%97%A3%EC%A7%80%20%EB%9D%BC%EC%9D%B4%ED%8C%85%20%ED%94%8C%EB%9F%AC%EC%8A%A4%2C%20%EC%97%A3%EC%A7%80%20%ED%84%B0%EC%B9%98%20%ED%99%9C%EC%9A%A9%20%EB%B0%A9%EB%B2%95', 'best': False, 'daumLikeUid': '2847499_123', 'summary': '안녕하세요. 도동쓰입니다. 오늘은 여러분에게 엣지 라이팅, 엣지 터치 앱에 대해 소개해드리려 합니다. 지난 번에 소개해드린 삼성 굿락 2018과 같이 갤릭시 앱스에서 다운로드 받아서, 사용자가 원하는 대로 설정을 변경할 수 있는 앱입니다. 엣지 라이팅 플러스(Edge Lighting +)는 기존에 제공하지 않았던 새로운 엣지 라이팅 효과를 사용할 수 있으며, 엣지 터치..', 'published': '2018.07.04 23:38', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile10.uf.tistory.com%2Fimage%2F997FDF4C5B3CCD061CD036', 'encodedUserName': '%EB%8F%84%EB%8F%99%EC%93%B0', 'title': '삼성 엣지 라이팅 플러스, 엣지 터치 활용 방법'}, {'url': 'http://donghun.kr/3466', 'likeCount': 9, 'userName': '멀티라이프', 'categoryName': 'IT 제품리뷰', 'encodedTitle': '%EC%82%BC%EC%84%B1%20%EA%B0%A4%EB%9F%AD%EC%8B%9C%20A8%EC%8A%A4%ED%83%80%2C%20%EB%84%88%EB%AC%B4%20%EC%95%A0%EB%A7%A4%ED%95%9C%20%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%B0%EC%9D%B4%20%EB%82%98%EC%99%94..', 'best': False, 'daumLikeUid': '355648_3466', 'summary': '삼성전자는 요즘 무섭게 국내 스마트폰 시장에 물량공세를 펼치고 있다. 출시된 스마트폰들의 스펙이 헷갈릴 정도로 다양한 제품을 출시하고 있는데, 그 중 하나가 갤럭시 A8스타다. 삼성은 중급형 라인업으로 A시리즈를 매년 선보였는데, A8스타역시 중급형으로 나온 제품이다. 그런데 이번에 7월 6일 출시되는 갤럭시 A8스타를 보면 참 애매하다는 생각이 든다. ..', 'published': '2018.07.04 23:23', 'thumbnail': 'https://img1.daumcdn.net/thumb/C295x191.fjpg/?scode=mtistory&fname=http%3A%2F%2Fcfile26.uf.tistory.com%2Fimage%2F99D223465B3CCF9723C595', 'encodedUserName': '%EB%A9%80%ED%8B%B0%EB%9D%BC%EC%9D%B4%ED%94%84', 'title': '삼성 갤럭시 A8스타, 너무 애매한 스마트폰이 나왔..'}], 'category': 'it'}, 'error': False}
하지만 실제 모던 웹 방식으로 만들어진 웹사이트, 또는 로그인이 필요한 웹사이트 등에서 정보를 가져오는 것은 쉽지 않은 경우도 많습니다. 최근 웹 개발 기술에 대한 상당히 복잡한 지식을 필요로 하는 경우도 많이 있습니다.



