cs/크롤링

[크롤링] 크롤링 시작하기 (페이지에 있는 링크 목록 가져오기/attrs/웹사이트를 무작위로 이동/딥 웹, 다크 웹, 히든 웹/웹스크레이핑/외부Url,내부 Url)

신_이나 2023. 3. 6. 16:07

 

[파이썬으로 웹 크롤러 만들기 - 라이언 미첼, 한빛미디어] 의 내용을 바탕으로 공부한 내용입니다.

작성한 글의 모든 저작권은 한빛미디어에 있습니다.

 

 

part1. 크롤링 시작하기 - chapter 3

 

- 페이지에 들어있는 링크 목록을 가져오기

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen('https://en.wikipedia.org/wiki/Kevin_Bacon')
bs = BeautifulSoup(html, 'html.parser')
for link in bs.findAll('a'):
    if 'href' in link.attrs:
        print(link.attrs['href'])

 

bs = BeautifulSoup(html, 'html.parser')
 
페이지의 주소를 저장하였고 구문 분석기를 호출하였다.
 
for link in bs.findAll('a'):
 
a태그를 찾고 그를 link에 저장한다
 
if 'href' in link.attrs:
print(link.attrs['href'])
 

attrs 는 속성값을 모두 출력하는 것을 말한다. a.attrs 는 a의 모든 속성값을 출력한다. 만약 a.attrs['herf'] 가 쓰여있다면 속성값 중 herf 에 해당하는 속성값만을 출력한다. 

!! 근데 여기서 궁금증이 생겼다. 그럼 그냥 print(link.attrs['href'] 만 쓰면 되지 굳이 if 를 왜 써줬을까? ' link의 herf 속성값을 출력하세요 ' 랑 'link 속성에 herf 가 있다면 herf 속성값을 출력하세요' 랑 똑같은 말 아닐까? 돌려보자 !!

결과는 print 만 쓴 코드는 돌아가지 않았다. 중간 과정을 거쳐서 'link.attrs'에서 href 를 먼저 걸러주는 것이 속도 측면에서 빨라서 그러는 것 아닐까?

 

 

- 링크에서 링크로 움직이며 웹사이트를 무작위로 이동하기

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{}'.format(articleUrl))
    bs = BeautifulSoup(html, 'html.parser')
    return bs.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)

 

이 코드를 실행하면 케빈 베이컨의 위키백과 항목에서 <article_name> 형태인 위키백과 항목 url을 받고 목록 전체를 반환하며, 프로그램을 끝내거나 새 페이지에 항목 링크가 없을 때까지 getLInks 에서 반환된 리스트에서 무작위로 항목 링클르 선택하여 getLinks를 다시 호출하는 작업을 반복하게 된다.

하지만 내 컴퓨터에서는 오류가 났다. 아마 여러 에러처리를 해주지 않아서겠지,,, 책보고 치다가 눈알 빠질 것 같아서 이건 여기까지만 하겠다. 

 

 

- 딥 웹, 다크 웹, 히든 웹

딥 웹은 간단히 표면 웹을 제외한 나머지 부분을 말한다. 다크 웹은 완전히 다른 개념이며, Tor 같은 클라이언트를 사용하거나 HTTP 위에서 동작하며 보안을 담당한다. 히든 웹은 이렇든 보이지 않는 숨겨진 웹을 의미한다.

 

웹사이트 전체 크롤링은 사이트맵 생성, 데이터 수집에 이용된다.

 

 

- 웹스크레이핑을 실행하는 코드 (출처. 저자의 깃허브)

from urllib.request import urlopen
from urllib.parse import urlparse
from bs4 import BeautifulSoup
import re
import datetime
import random

pages = set()
random.seed(datetime.datetime.now())

#페이지에서 발견된 내부 링크를 모두 목록으로 만든다.
#여기서 def 는 void, int 처럼 파이썬에서 함수 만들어주는 키워드
def getInternalLinks(bs, includeUrl):
    includeUrl = '{}://{}'.format(urlparse(includeUrl).scheme, urlparse(includeUrl).netloc)
    internalLinks = []
    #Finds all links that begin with a "/"
    for link in bs.find_all('a', href=re.compile('^(/|.*'+includeUrl+')')):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                if(link.attrs['href'].startswith('/')):
                    internalLinks.append(includeUrl+link.attrs['href'])
                else:
                    internalLinks.append(link.attrs['href'])
    return internalLinks
            
#페이지에서 발견된 외부 링크를 모두 목록으로 만든다.
def getExternalLinks(bs, excludeUrl):
    externalLinks = []
    #Finds all links that start with "http" that do
    #not contain the current URL
    for link in bs.find_all('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 getRandomExternalLink(startingPage):
    html = urlopen(startingPage)
    bs = BeautifulSoup(html, 'html.parser')
    externalLinks = getExternalLinks(bs, urlparse(startingPage).netloc)
    if len(externalLinks) == 0:
        print('No external links, looking around the site for one')
        domain = '{}://{}'.format(urlparse(startingPage).scheme, urlparse(startingPage).netloc)
        internalLinks = getInternalLinks(bs, domain)
        return getRandomExternalLink(internalLinks[random.randint(0,
                                    len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]
    
def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink(startingSite)
    print('Random external link is: {}'.format(externalLink))
    followExternalOnly(externalLink)
            
followExternalOnly('http://oreilly.com')

 

아래는 위 코드의 결과다. 진짜로 이동하는 게 너무너무 신기하다!

 

 

-외부Url 과 내부 Url 을 수집하기 (위 코드에 아래 코드 추가)

# 외부 Url을 모두 리스트로 수집
allExtLinks = set()
allIntLinks = set()


def getAllExternalLinks(siteUrl):
    html = urlopen(siteUrl)
    domain = '{}://{}'.format(urlparse(siteUrl).scheme,
                              urlparse(siteUrl).netloc)
    bs = BeautifulSoup(html, 'html.parser')
    internalLinks = getInternalLinks(bs, domain)
    externalLinks = getExternalLinks(bs, domain)

	#allExtLinks에 외부 링크가 없으면 추가
    for link in externalLinks:
        if link not in allExtLinks:
            allExtLinks.add(link)
            print(link)
    
    #allExtLinks에 내부 링크가 없으면 추가, 그리고 그 링크로 이동해서 처음부터 시작
    for link in internalLinks:
        if link not in allIntLinks:
            allIntLinks.add(link)
            getAllExternalLinks(link)


allIntLinks.add('http://oreilly.com')
getAllExternalLinks('http://oreilly.com')