日記

検索エンジニアになりたい

クローラのコードを読む

クローラをつくるにあたってサンプルコード(https://github.com/REMitchell/python-scraping/blob/master/chapter3/3-crawlSite.py)を読んでみる。

サンプルコードの出典は最後に明記してある。
以下がサンプルコードの全体。

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

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

#Retrieves a list of all Internal links found on a page
def getInternalLinks(bsObj, includeUrl):
    internalLinks = []
    #Finds all links that begin with a "/"
    for link in bsObj.findAll("a", href=re.compile("^(/|.*"+includeUrl+")")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                internalLinks.append(link.attrs['href'])
    return internalLinks
            
#Retrieves a list of all external links found on a page
def getExternalLinks(bsObj, excludeUrl):
    externalLinks = []
    #Finds all links that start with "http" or "www" that do
    #not contain the current URL
    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)
    externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])
    if len(externalLinks) == 0:
        internalLinks = getInternalLinks(startingPage)
        return getNextExternalLink(internalLinks[random.randint(0, 
                                  len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]
    
def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink("http://oreilly.com")
    print("Random external link is: "+externalLink)
    followExternalOnly(externalLink)
            
followExternalOnly("http://oreilly.com")

以上のコードを少しずつ区切って読んでいく。まず一番下の行のfollowExternalOnly("http://oreilly.com")はオライリーの公式サイトのURLにfollowExternalOnly関数を適用する。

def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink("http://oreilly.com")
    print("Random external link is: "+externalLink)
    followExternalOnly(externalLink)

followExternalOnly(startingSite):よりオライリーの公式サイトがなにかのはじめとなるURLだということがわかる。externalLink = getRandomExternalLink("http://oreilly.com")でオライリーの公式サイトから関連するリンクをランダムで取得しそれをprintする関数だと推測する。followExternalOnly(externalLink)でこの関数自体を再帰している…と思う。

def getRandomExternalLink(startingPage):
    html = urlopen(startingPage)
    bsObj = BeautifulSoup(html)
    externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])
    if len(externalLinks) == 0:
        internalLinks = getInternalLinks(startingPage)
        return getNextExternalLink(internalLinks[random.randint(0, 
                                  len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]

getRandomExternalLink(startingPage):よりオライリーの公式サイトがなにかのはじめとなるページだということがわかる。
html = urlopen(startingPage)
bsObj = BeautifulSoup(html)
でページ全体を取り出してBeautifulSoupオブジェクトを作っている。
externalLinks = getExternalLinks(bsObj, splitAddress(startingPage)[0])はbsObjとsplitAddress関数の0番目をgetExternalLinks関数でなんらかの処理をしている。

if len(externalLinks) == 0:
        internalLinks = getInternalLinks(startingPage)
        return getNextExternalLink(internalLinks[random.randint(0, 
                                  len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0, len(externalLinks)-1)]

ここはもしexternalLinksの長さが0だった場合、getInternalLinks関数でそのスタートページ内のリンクのリストを取り出して…ランダムでその中から1つ選択する…?

def splitAddress(address):
    addressParts = address.replace("http://", "").split("/")
    return addressParts

splitAddress関数はaddressを/で分割する関数。address.replace("http://", "")でhttp://を空文字列に置き換えてるようだけどなぜ置き換えてるの…?

def getExternalLinks(bsObj, excludeUrl):
    externalLinks = []
    #Finds all links that start with "http" or "www" that do
    #not contain the current URL
    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

getExternalLinks関数はページ内でみつかったhttpまたはwwwではじまるURLを探し出す関数。

def getInternalLinks(bsObj, includeUrl):
    internalLinks = []
    #Finds all links that begin with a "/"
    for link in bsObj.findAll("a", href=re.compile("^(/|.*"+includeUrl+")")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                internalLinks.append(link.attrs['href'])
    return internalLinks

getInternalLinks関数はページ内でみつかった/ではじまるURLを探し出す関数。

冗長な説明になってしまったがコードの意味はわかるようになった。


サンプルコードの出典
『Web Scraping with Python』 Ryan Mitchell、 O'Reilly、 Copyright 2015 Ryan Mitchell、 978-1-491-91029-0、 邦題『PythonによるWebスクレイピングオライリー・ジャパン、 ISBN978-4-87311-761-4

より3.3 crawlSite.py(https://github.com/REMitchell/python-scraping/blob/master/chapter3/3-crawlSite.py)