Python爬虫如何入门?

Python爬虫如何入门?

源自公众号:DeveloperPython

扯淡

其实,“入门”最好的方式是以项目开始,这样实践起来你会被目标驱动。从而,不用一步一步慢慢的学习模块化的东西。

其实,知识体系里面的每一个知识点类似于图里的点。边就是知识体系的依赖关系,那么整个图也就是一个有向无环图。因为学习A的经验可以帮助到你学习B。因此,入门的东西根本不用学习,因为入门点根本不存在。

同时,你需要学习的是如何去做一个大的项目,来亲身体会爬虫, 并一步步学习爬虫的知识点。

那么,我总结下在其他平台上的相关知识点和自己的想法:

  1. 爬虫的工作原理
  2. 基本的Https爬虫工具:Scrapy
  3. 分布式爬虫系统。也就是维护一个集群机器来高效的完成分布式队列。Github上也有一个现成的例子: nvie/rq
  4. rq和Scrapy的结合: rolando/scrapy-redis
  5. 后续处理: 网页处理grangier/python-goose 存储(Mongodb)

以下,我将基于xiyouMc/WebHubBot的经验来讲:

一、 爬虫的怎么工作的

爬虫的另一个意思其实就是🕷(Spider) ,互联“网”就是它的环境。简单的来说,也就是你需要用这个蜘蛛来把相关网站的所有角落(网页)都爬一边。

那么,你可以选择一个自己感兴趣的平台,如知乎、淘宝等等的。

我这里通过PornHub来讲解。

比如,我们访问PornHub的首页,里面会出现很多链接。最初我们的目标是拿到该站中所有的视频标题、视频简介、视频链接或者其他有用信息。然后我们兴高采烈的将整个首页都爬下来,这里可以理解为你就是将整个页面完完整整的Copy了下来。

然后,我们随便点击一个链接进入视频详情页,进入第二个页面之后,就会看到具体的视频标题、简介等等的。那么这只是一个视频信息,我们又是如何做到爬取整站的数据呢。这里可以动下脑子,一种是返回到首页去拿另一个链接,另一种则是基于第一步爬下来的首页来找下一个链接。这里,当然是第二种方案。同时我们要做去重,爬过的链接,就不要再去爬第二遍。

所以,理论上如果后续的所有视频详情页都是从首页可达的话,那么我们就一定可以将所有网页都爬下来。

以下是Python的伪代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Queue import Queue
home_page = 'https://www.pornhub.com/'
link_queue = Queue()
seen = set()
seen.insert(home_page)
link_queue.put(home_page)
while(True):
if link_queue.size() > 0:
current_url = url_queue.get() #拿到队列中第一个url
links = save(current_url) #保存这个页面的html,并返回当前页面的所有link
for link in links:
if link not in seen:
seen.put(link)
link_queue.put(link)
else:
break

以上就是一个简单的伪代码来爬取pornhub中视频资源的例子。当然, 这只是一个非常简单的例子,其实爬虫是一个非常复杂的项目,类似的就是搜索引擎需要爬取整站的数据,更是需要一整个团队来开发和维护。

二、 效率

如果,使用上述的代码来爬取PornHub的话,那么你是绝对无法在一天时间内完成500万的海量数据,更别说在短时间内爬取PornHub的所有数据。

那么,问题出在哪?爬的网页太多太多了,而且上面的代码太慢太慢了。

PornHub的全网有N个页面,那么分析下判重的时间复杂度将是 N * log(N),因为每个网页都要遍历一遍,而使用Set来做判重,需要 log(N) 的复杂度。

所以,我们需要一个成熟的判重方案。Bloom Filter。 它是一个空间效率很高的随机数据结构。官方简介

Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。

有兴趣可以详细学习下这个算法。

简单的来说,它是一种利用Hash的方法来判重的。并且在使用固定的内存,不随url的数量增加而增长,以 O(1)的效率判断url是否已经在set中。但是仍然有极小的可能性会误判,所以对于“零错误”的系统不适用。但是对于爬虫,小概率的重复爬取也是可以接受的。

那么,以上就是判重的最快方式了。

当然,另一个瓶颈又会出现,如果你只有一台机器,那么不管你的带宽有多大你的机器下载网页的速度还是会有瓶颈。那么,只有加快这个速度,我们使用多台机器来跑。

三、 集群化爬取

集群化爬取,其实不难理解。也就是将你的爬虫任务分发到n台机器来处理,当然每台机器处理的任务不同,且不重复。

那么,假设你有100台机器,怎么用Python实现一个分布式的爬取算法呢?

Server -> Client 。这种C/S 的模式,我相信大家也不陌生。那么分布式的系统,其实就是一台Server -> n个Client 来处理。这里我们将Server定义为 Master机,多个Client定义为多个 Slave。所以基于开始的伪代码,我们可以将link_queue 放到Master上,其他的Slave都可以通过网络跟Master联通,每当一个Slave下载完成一个页面之后,就会将这个页面的结果告知Master,同时获取一个新的link 来爬取。同时 BloomFliter 也是放在Master的,用来针对Links进行去重。 其中Slave和Server联通的方式,就是通过Redis,这是一个可以远程操作的缓存数据库,提供了完善的队列管理。其次,Redis的队列中已经包含的去重的,当Push一个url 到Redis之后,某一个Slave Pop拿到数据,那么这个Url将只会在这个Slave进行处理。

因此,我们可以用Python来实现。Slave的机器上安装Scrapy,Master上安装Redis和rq用作分布式队列。

伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"""
Slave
"""
url = link_from_master() # 从Master机Get到最新的链接
content = save(url) # 请求并保存这个链接下的视频信息
send_to_master(url) # 将当前处理过的url Post给主机。
"""
Master
"""
queue = Queue()
bf = BloomFilter()
home_pages = "https://www.pornhub.com/"
while(True):
if request == 'GET':
if distributed_queue.size()>0:
send(queue.get()) # 将当前url push到Redis,从而让Slave获取到。
else:
break
elif request == 'POST':
bf.put(request.url) #将处理过的Url 保存到bf队列

轮子: rolando/scrapy-redis

四、 后处理

上面的路子,其实是很简单很简单的一部分。同样的,后续你还要进行其他的处理,比如:

  • 有效的存储
  • 有效的判重
  • 有效的信息提取
  • 及时更新

所以,不要在意如何“入门”,只管上路就好了。

五、 更重要的一点

长摁‘识别二维码’,一起进步

生活不止眼前的苟且,还有手下的代码、

和嘴上的扯淡
——
个人博客: http://xiyoumc.0x2048.com/

Github:https://www.github.com/xiyouMc


六、 如何找到一群学习Python的朋友圈

点击 Join,加入Python技术成长圈子,我们在这里等着你。