入门python爬虫(1)

原文源自知乎回答:

作者:谢科-搜索引擎
链接:https://www.zhihu.com/question/20899988/answer/24923424
来源:知乎

什么是爬虫?

要想入门爬虫,首先要知道,什么是爬虫。网络爬虫(又称网页蜘蛛,网络机器人,更常称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。

入门爬虫首先需要学习

1. 基本的爬虫工作原理

2.基本的http抓取工具,scrapy

3.Bloom Filter:Bloom Filters by Example

4.如果需要大规模网页抓取,你需要学习分布式爬虫的概念,主要学会怎样维护一个所有集群机器能够有效分享的分布式队列。最简单的实现是python-rq:

5.rq和Scrapy的结合:darkrho/scrapy-redis · GitHub

6.后续处理,网页析取(grangier/python-goose · GitHub),存储(Mongodb)

说说当初写的一个集群爬下整个豆瓣的经验:

(1)首先要明白爬虫怎样工作。

想象你是一只蜘蛛,现在你被放到了互联网上,你需要把所有的网页都看一遍。怎么办呢?就随便从某个地方开始,比如说,人民日报的首页,叫initial pages,用$表示。

在人民日报的首页,看到那个页面引向的各种链接。于是你很开心地爬到了“国内新闻”那个页面。这样就爬完了两个页面(首页和国内新闻)。暂且不用管爬下来的页面怎么样处理,你就想象你把这个页面完整抄成了个html放到了你身上。

突然你发现,在国内新闻这个页面上,有一个链接链回“首页”。作为一只聪明的蜘蛛,肯定知道不用爬回去,所以需要用脑子,存下你已经看过的页面地址,这样,每次看到一个可能需要爬的新链接,你先查查是不是已经去过这个页面地址,如果去过,就别去了。

理论上,如果所有的页面都可以从initial page到达的话,那么证明你一定可以爬完所有的网页。以下是在python中的实现。

import Queue

initial_page = "http://www.renminribao.com"

url_queue = Queue.Queue()
seen = set()

seen.insert(initial_page)
url_queue.put(initial_page)

while(True): #一直进行直到海枯石烂
    if url_queue.size()>0:
        current_url = url_queue.get()    #拿出队例中第一个的url
        store(current_url)               #把这个url代表的网页存储好
        for next_url in extract_urls(current_url): #提取把这个url里链向的url
            if next_url not in seen:      
                seen.put(next_url)
                url_queue.put(next_url)
    else:
        break

所有的爬虫的backone都在这里,下面分析一下为什么爬虫事实上是个非常复杂的东西--搜索引擎公司通常有一整个团队来维护和开发。

(2)效率

如果直接使用上边的代码运行,那么一整年才能爬下整个豆瓣的内容,更别说Google爬取全网的内容了。问题出在哪里呢?需要爬的网页实在是太多了,而上边的代码太慢太慢了。设想全网有N个网站,那么分析一下判重的复杂度就是N*log(N),因为所有的网页都要遍历一遍,而每次判重用set的话,需要log(N)的复杂度。虽然python的set实现的是hash,不过这样还是太慢了,至少内存使用效率不高。

通常的判重的做法是Bloom Filter。简单讲它仍是一种hash方法,但是它的特点是,他可以使用固定的内存(不随url的数量而增长)以O(1)的效率判定url是否已经在set中。他唯一的问题在于,如果这个url不在set中,BF可以100%确定这个url没有看过。但是如果这个url在set中,他就会告诉你,这个url应该已经出现过,不过我有2%的不确定性。注意这里的不确定性在你分配的内存足够大时,可以变得很小很小。一个简单的教程:Bloom Filters by Example

现在已经接近处理判重最快的方法了,另外一个瓶颈--你只有一台机器,不管你的带宽有多大,只要你的机器下载网页的速度是瓶颈的话,那么你只有加快这个速度。用一台机子不够的话,用很多台。当然,我们假设每台机子都已经进了最大的效率--使用多线程(python的话,使用多线程吧)。

(3)集体化抓取

假设你现在有100台机器可以用,如何用python实现一个分布式的爬取算法呢?

我们把100台中的99台运算能力较小的机器叫做slave,另外一台较大的机器叫做master,那么回顾上边的代码中的url_queue,如果我们能把这个queue放到这台master机器上,所有的slave都可以通过网络跟master联通,每当一个slave完成下载一个网页,就向master请求一个新的网页来抓取。而每次slave新抓到一个网页,就把这个网页上所有的链接送到master的queue里去。同样,bloom filter也放到master上,但是现在master只发送确定没有访问过的url给slave。bloom filter放到master的内存里,而被访问过的url放到运行在master上的Redis里,这样保证所有操作都是O(1)。(至少平摊是O(1),Redis的访问效率见:LINSERT – Redis)。

 考虑如何用python实现:

在各台slave上装好scrapy,那么各台机子就变成了一台有抓取能力的slave,在master上装好Redis和rq用作分布式队列。

代码于是写成

#slave.py

current_url = request_from_master()
to_send = []
for next_url in extract_urls(current_url):
    to_send.append(next_url)

store(current_url);
send_to_master(to_send)

#master.py
distributed_queue = DistributedQueue()
bf = BloomFilter()

initial_pages = "www.renmingribao.com"

while(True):
    if request == 'GET':
        if distributed_queue.size()>0:
            send(distributed_queue.get())
        else:
            break
    elif request == 'POST':
        bf.put(request.url)

完整代码见:darkrho/scrapy-redis · GitHub

(4)展望以及后处理

上边的代码用来爬一个整体的网站问题不大,但是如果附上后续处理,比如

1. 有效地存储(数据库应该怎样安排)

2. 有效地判重(网页判重,网页和抄袭他的网页不能都爬取)

3. 有效地信息抽取(比如怎么样抽取网页上所有的地址,搜索引擎通常不需要存储所有的信息)

4. 及时更新(预测这个网页多久会更新一次)

这里的每一个点都需要很长时间的研究!

原文地址:https://www.cnblogs.com/lbwBH/p/14147559.html