用python写一个爬虫(一)


这篇文章是我根据Udacity上的CS101公开课写的,代码也是直接粘贴的。

爬虫是从一个页面开始,不断提取页面里所有的链接,然后再分别进入这些链接页面,并进行同样的操作。网页的源代码就是一大串字符串,python中,使用find()可以找出某个字符串的位置。

"string".find("ing")
4   #找不到返回-1

怎么找出源代码中的链接?
链接通常这样表示:keyword
利用find method,很容易就可以找到链接
代码如下:

start_link = page.find('<a href=')            #page是网页源代码
start_quote = page.find('"', start_link)    #从start_link开始寻找,注意单引号之间是双引号' " '
end_quote = page.find('"', start_link + 1)
url = page[start_quote+1 : end_quote]  #提取url

一个页面里当然不止一个链接,所以要把所有的链接都提取出来

def get_next_target(page):
    start_link = page.find('<a href=')
    if start_link == -1:
        return None, 0
    start_quote = page.find('"', start_link)
    end_quote = page.find('"', start_quote + 1)
    url = page[start_quote + 1:end_quote]
    return url, end_quote #python支持返回多个值

def get_all_links(page):
    links = [] #储存找到的Links
    while True:
        url, endpos = get_next_target(page) #python支持返回多个值
        if url:
            links.append(url) #找到url,添加到index
            page = page[endpos+1:]
        else: # url == None, 说明没有链接了,退出循环
            break
    return links

该要设计数据结构了。好的数据结构能够是操作更加简便,代码自然也变得简洁。现在我们需要一种数据结构能够把keywords与url对应起来,注意,一个keyword可能对应多个url。

在教程里,储存keywords和url的数据结构是这样的:

[ [<keyword1>,[<url1>,<url2>] ]
[<keyword2>,[<url1>,<url2>] ]
……                                           ]

列表是允许嵌套使用的。当时我看到作者给出的数据结构,想了一会,然后顿生敬佩之情。当然,这中数据结构并不是最好的,只是它非常直观简洁,思路很清晰。

以下是添加关键字的代码

index = [] #存放keywords和url

def add_to_index(index, keyword, url): #添加关键字
    for entry in index:
        if entry[0] == keyword:
        entry[1].append(url)
        return #结束并返回
    index.append([keyword,[url]])

def lookup(index, keyword):
    for entry in index:
        if entry[0] == keyword:
            return entry[1]
    return []

def add_page_to_index(index, url, content):
    words = content.split()
    for word in words:
        add_to_index(index, word, url)

有了这些,我们就可以做一个简单的爬虫了,以下是完整代码

import urllib2

def get_page(url):
    try:
        return urllib2.urlopen(url).read()
    except:
        return ""
def get_next_target(page):
    start_link = page.find('href=')
    if start_link == -1:
        return None, 0
    start_quote = page.find('"', start_link)
    end_quote = page.find('"', start_quote + 1)
    url = page[start_quote + 1:end_quote]
    return url, end_quote
 
def get_all_links(page):
    links = [] #储存找到的Links
    while True:
        url, endpos = get_next_target(page)
        if url == None: #url == None, 说明没有链接了,退出循环
            break
        elif url.startswith("http"):
            links.append(url) #找到url,添加到index
            page = page[endpos+1:]
        else:
            page = page[endpos+1:]
    return links

index = [] #存放关键字和链接

def add_to_index(index, keyword, url):
    for entry in index:
        if entry[0] == keyword:
            entry[1].append(url)
            return
        index.append([keyword,[url]])

def lookup(index, keyword):
    for entry in index:
        if entry[0] == keyword:
            return entry[1]
        return []

def add_page_to_index(index, url, content):
    words = content.split()
    for word in words:
        add_to_index(index, word, url)

def crawl_web(seed):
    tocrawl = [seed] #没有爬过的
    crawled = []     #已经爬过的
    while tocrawl:
        page = tocrawl.pop()
        if page not in crawled:
            content = get_page(page)
            add_page_to_index(index, page, content)
            tocrawl = tocrawl + get_all_links(content)
            crawled.append(page)
    return crawled

if __name__ == '__main__':
    crawl_web('http://www.python.cn')

经过实践,发现效率很低,原因我想有以下几点:

  1. 提取链接的方法中,需要多次处理源代码。
  2. index存放了大量无关的数据,而且每次找到keyword都要在index中查找是否存在。
  3. 很多链接不是以<a href 开头,而是<a XXX href= 这样的形式的,导致漏掉很多链接。还有,有些链接是无效的。

以上问题,我会在后续的文章中给出解决方法。我想做出一个真正的搜索引擎,虽然有了可用的爬虫,但是还有很多问题需要解决。