티스토리 뷰
단일 도메인 내의 이동
1 2 3 4 5 6 7 8 | from urllib.request import urlopen from bs4 import BeautifulSoup html=urlopen("http://en.wikipedia.org/wiki/kevin_Bacon") bsObj=BeautifulSoup(html,"html.parser") for link in bsObj.findAll("a"): if 'href' in link.attrs: print(link.attrs['href']) | cs |
링크 목록을 살펴보면 에상대로(예상..??) 'Apollo 13','Philadelphia','Primetime Emmy Award' 등이 모두 있을 것입니다. 하지만 원하지 않는 것들도 포함되어 있을 것입니다.
그 이유는 위키백과의 모든 페이지에는 사이드바, 푸더, 헤더 링크가 있고 카테고리 페이지, 토론 페이지 등 그 외에도 우리가 관심 있어 하는 항목이 아닌 페이지를 가리키는 링크가 많이 있습니다. 하지만 항목 페이지를 가리키는 링크에는 다른 내부 페이지를 가리키는 링크와 비교되는 세 가지 공통점을 찾을 수 있습니다.
● 이 링크들은 id가 bodyContent인 div 안에 있습니다.
● URL에는 세미콜론이 포함되어 있지 않습니다.
● URL은 /wiki/로 시작합니다.
이 규칙들을 활용하면 항목 페이지를 가리키는 링크만 가져오도록 할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 | from urllib.request import urlopen from bs4 import BeautifulSoup import re html=urlopen("http://en.wikipedia.org/wiki/Kevin_Bacon") bsObj=BeautifulSoup(html,"html.parser") for link in bsObj.find("div",{"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$")): if 'href' in link.attrs: print(link.attrs['href']) | cs |
이 코드를 실행해 보면 위키백과 항목에서 다른 항목을 가리키는 모든 링크 목록을 볼 수 있을 것입니다.
이번에는 위 코드를 아래 두 조건들을 추가하여 좀 더 향상된 코드를 만들 수 있습니다.
● /wiki/<article_name> 형태인 위키백과 항목 URL을 받고, 링크된 항목 URL 목록 전체를 반환하는 getLinks 함수
● 시작 항목에서 getLinks를 호출하고 반환된 리스트에서 무작위로 항목 링크를 선택하여 getLinks를 다시 호출하는 작업을, 프로그램을 끝내거나 새 페이지에 항목 링크가 없을 때까지 반복하는 메인 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from urllib.request import urlopen from bs4 import BeautifulSoup import datetime import random import re random.seed(datetime.datetime.now()) def getLinks(articleUrl): html=urlopen("http://en.wikipedia.org"+articleUrl) bsObj=BeautifulSoup(html,"html.parser") return bsObj.find("div",{"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$")) links=getLinks("/wiki/Kevin_Bacon") while len(links)>0: newArticle=links[random.randint(0, len(links)-1)].attrs['href'] print(newArticle) links=getLinks(newArticle) | cs |
이 프로그램이 필요한 라이브러리를 임포트한 후 처음 한 일은 현재 시스템 시간을 가지고 난수 발생기를 실행하는 겁니다. 이렇게 하면 프로그램을 실행할 때마다 위키백과 항목들 속에서 새롭고 흥미로운 무작위 경로를 찾을 수 있습니다.
그 다음 getLinks 함수를 정의합니다. 이 함수는 /wiki/....형태로 된 URL을 받고 그 앞에 위키백과 도메인 이름인http://en.wikipedia.org를 붙여, 그 위치의 HTML에서 BeautifulSoup 객체를 가져옵니다. 그리고 앞에서 설명한 매개변수에 따라 항목 링크 태그 목록을 추출해서 반환합니다.
이 프로그램은 초기 페이지인 https://en.wikipedia.org/wiki/Kevin_Bacon의 링크 목록을 links 변수로 정의하며 시작합니다. 그리고 루프를 실행해서 항목 링크를 무작위로 선택하고, 선택한 링크에서 href 속성을 추출하고, 페이지를 출력하고, 추출한 URL에서 새 링크 목록을 가져오는 작업을 반복합니다.
사실 단순히 페이지로 이동한다고 해서 케빈 베이컨의 여섯다리 문제가 풀리는 것이 아닙니다. 왜냐면 반드시 결과 데이터를 저장하고 분석할 수 있어야 하기 때문입니다.
다크 웹과 딥 웹
전체 사이트 크롤링
사이트맵 생성
데이터 수집
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from urllib.request import urlopen from bs4 import BeautifulSoup import re pages=set() def getLinks(pageURL): global pages html=urlopen("http://en.wikipedia.org"+pageURL) bsObj=BeautifulSoup(html,"html.parser") for link in bsObj.findAll("a",href=re.compile("^(/wiki/)")): if 'href' in link.attrs: if link.attrs['href'] not in pages: #새 페이지를 발견 newPage=link.attrs['href'] print(newPage) pages.add(newPage) getLinks(newPage) getLinks("") | cs |
전체 사이트에서 데이터 수집
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from urllib.request import urlopen from bs4 import BeautifulSoup import re pages=set() def getLinks(pageURL): global pages html=urlopen("http://en.wikipedia.org"+pageURL) bsObj=BeautifulSoup(html,"html.parser") try: print(bsObj.h1.get_text()) print(bsObj.find(id=="mw-content-text").findAll("p")[0]) print(bsObj.find(id=="ca-edit").find("span").find("a").attrs['href']) except AttributeError: print("This page is missing something! No worries though!") for link in bsObj.findAll("a",href=re.compile("^(/wiki/)")): if 'href' in link.attrs: if link.attrs['href'] not in pages: newPage=link.attrs['href'] print("-----------------\n"+newPage) pages.add(newPage) getLinks(newPage) getLinks("") | cs |
인터넷 크롤링
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | from urllib.request import urlopen from bs4 import BeautifulSoup from urllib.parse import urlparse import re import datetime import random pages=set() random.seed(datetime.datetime.now()) #페이지에서 발견된 내부 링크를 모두 목록으로 만듭니다. def getInternetLinks(bsObj, includeUrl): includeUrl=urlparse(includeUrl).scheme+"://"+urlparse(includeUrl).netloc internetLinks=[] # /로 시작하는 링크를 모두 찾습니다. for link in bsObj.findAll("a",href=re.compile("^(/|.*"+includeUrl+")")): if link.attrs['href'] is not None: if link.attrs['href'] not in internetLinks: if(link.attrs['href'].startswith("/")): internetLinks.append(includeUrl+link.attrs['href']) else: internetLinks.append(link.attrs['href']) return internetLinks # 페이지에서 발견된 외부 링크를 모두 목록으로 만듭니다. def getExternalLinks(bsObj, excludeUrl): externalLinks=[] # 현재 URL을 포함하지 않으면서 http나 www로 시작하는 링크를 모두 찾습니다. for link in bsObj.findAll("a",href=re.compile("^(http|www)((?!"+excludeUrl+").)*$")): if link.attrs['href'] is not None: if link.attrs['href'] not in externalLinks: externalLinks.append(link.attrs['href']) return externalLinks def splitAddress(address): addressParts=address.replace("http://", "").split("/") return addressParts def getRandomExternalLink(startingPage): html=urlopen(startingPage) bsObj=BeautifulSoup(html,"html.parser") externalLinks=getExternalLinks(bsObj, urlparse(startingPage).netloc) if len(externalLinks)==0: domain=urlparse(startingPage).scheme+"://"+urlparse(startingPage).netloc internetLinks=getExternalLinks(bsObj,domain) return getRandomExternalLink(internetLinks[random.randint(0,len(internetLinks)-1)]) else: return externalLinks[random.randint(0,len(externalLinks)-1)] def followExternalOnly(startingSite): externalLink=getRandomExternalLink(startingPage) print("Random external link is: "+externalLink) followExternalOnly(externalLink) followExternalOnly("http://oreilly.com") | cs |
작업을 '이 페이지에 있는 모든 외부 링크를 찾는다' 같은 단순한 함수로 나누면, 나중에 코드를 다른 크롤링 작업에 쓸 수 있도록 리팩토링하기 쉽습니다. 예를 들어 사이트 전체에서 외부 링크를 검색하고 각 링크마다 메모를 남기고 싶다면 다음과 같은 함수를 추가하면 됩니다.
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 35 36 | # 사이트에서 찾은 외부 URL을 모두 리스트로 수집 allExtLinks=set() allIntLinks=set() def getAllExteralLinks(siteUrl): try: html=urlopen(siteUrl) except HTTPError as e: return None try: bsObj=BeautifulSoup(html,"html.parser") internalLinks=getInternalLinks(bsObj,splitAddress(domain)[0]) externalLinks=getExteranlLinks(bsObj,splitAddress(domain)[0]) except AttributerError as e: return None for link in externalLinks: if link not in allExtLinks: allExtLinks.add(link) print(link) for link in internalLinks: if link=="/": link=domain elif link[0:2]=="//": link="http:"+link elif link[0:1]=="/": link=domain+link if link not in allIntLinks: print("About to get link:"+link) allIntLinks.add(link) getAllExteralLinks(link) domain="http://oreilly.com" getAllExteralLinks(domain) | cs |
리다리렉트 처리
'python > 웹 크롤링(python)' 카테고리의 다른 글
API에서 얻은 데이터와 웹 스크레이퍼 결합하기!!! (0) | 2018.05.05 |
---|---|
고급 HTML 분석 (0) | 2018.05.01 |
BeautifulSoup 라이브러리 (0) | 2018.05.01 |
urllib 라이브러리 (0) | 2018.04.29 |