这里需要用到 队列 和 宽度优先搜索(BFS)
什么是队列?
(详细说明请翻阅 维基百科 )
你去食堂打饭,人多,要排队是吧?队伍头部的人正在被服务(打饭),新来的人只能跑到队伍最后面,眼巴巴地看着。队头的人打完饭了,就离开队伍,座到旁边吃去了。此时第二名变成了对头,开始打饭……
也就是说,新来的只能加在队伍最后面,而队头才是我们永远的候选。选中它,操作它,扔掉它。
Python的列表很魔性啊!数组、队列、栈三合一!很神奇!怪不得列表是Python的“苦力”。
那么宽度优先搜索呢?
(详细说明请翻阅 维基百科 )
队头的人打完饭,拿出手机,给室友们打电话:“喂,想吃饭的话自己来打饭,老子不给你们带饭了”。室友们接到电话,立刻闪现过来,然而无奈发现只能去队伍后面默默排队,顺带看着刚才Call自己的那位猪队友优雅地离开队伍,坐旁边吃去了。
第二个人也是如此。队伍好像变长了。
第三个人木有室友……
第四个人打了电话,然而室友叫了外卖,不来打饭了……
总之呢,队伍总会排完的。
ok,爬虫也是这样,拿到一个页面之后,在抓取下自己感兴趣的东西之后,顺带着将这个页面中的所有超链接都找了出来,记录下来,加入到一个队列里面。然后,只要队列不空,就取出队头,访问这个URL,继续抓取、找出链接、放入队列……
在这里我们先考虑一种简单的情况:事先就能把全部的网址都构造出来,放入队列中,不考虑队列会在抓取过程中被动态地添加进去新的URL。这样便于测试,并且在大多数时候是很有效的。
为什么?仔细看URL,对于一部分网站来说,URL都是有规律的:
http://www.lagou.com/gongsi/129.html
把后面的129换成其他数字试试?发现了什么?
另外还有一个好处:队伍长度一直在减少。这样就不会出现“一个网页被访问多次”的尴尬局面了。至于“将网址动态添加到队列中”的情形,我们以后再讨论。
排排坐
这个不难,一个for循环就全都出来了:
queue=[]
for index in range(100,200):
url='http://www.lagou.com/gongsi/'+str(index)+'.html'
queue.append(url)
吃果果
当队列不为空的时候,取出队头,进行抓取:
while queue:
url = queue.pop(0)
response = requests.get(url, timeout=5, allow_redirects=False)
data = BeautifulSoup(response.text)
... ...
当然,我们构造出来的URL有可能会导致404。因为我们是尝试打开了几个网页后就假设它的id号是连续增加的。如果404了怎么办?
跳过啊!这还用教!
也就是说,如果网页返回404,或者从返回的网页中得不到我们想要的信息,跳过就好啦~天涯何处无芳草,少你一个不算少!吼吼!
import requests
from bs4 import BeautifulSoup
queue = []
for index in range(100, 200):
url = 'http://www.lagou.com/gongsi/' + str(index) + '.html'
queue.append(url)
while queue:
url = queue.pop(0)
try:
response = requests.get(url, timeout=5, allow_redirects=False) #有可能在这里,因为网络原因,导致超时或者其他什么
data = BeautifulSoup(response.text)
name = data.h1.a.text.strip() #有可能在这里,因为没有这个公司,导致404,获取不到信息
location = data.find('ul', 'info_list_with_icon').find(attrs={'class': 'location'}).span.text
print("%s %s" % (name, location))
except:
pass
写到这里,对于一个洁癖来说,我已经快受不了了。随着代码越来越多,不把各个功能都分成函数的话,我会疯掉的。
OK,下面是代码重写后的结果。
import requests
from bs4 import BeautifulSoup
class Spider:
def __init__(self):
self.queue = []
def addUrlBlock(self, start=100, end=200):
for index in range(start, end):
url = 'http://www.lagou.com/gongsi/' + str(index) + '.html'
self.addUrl(url)
def addUrl(self, url):
self.queue.append(url)
def run(self):
while self.queue:
url = self.queue.pop(0)
try:
response = requests.get(url, timeout=5, allow_redirects=False)
data = BeautifulSoup(response.text)
name = data.h1.a.text.strip()
location = data.find('ul', 'info_list_with_icon').find(attrs={'class': 'location'}).span.text
print("%s %s" % (name, location))
except:
pass
if __name__ == '__main__':
spider = Spider()
spider.addUrlBlock(100, 200)
spider.run()
看起来舒(zhuang)服(bi)多了吧?
发表回复