最近在看《Python爬虫开发与项目实战》,看到第六章基础爬虫时,发现把实现爬虫各个功能分别封装为一个类能增强爬虫的可读性,不过理解代码时碰到了许多坑,因此分享一下读书笔记。
代码中的注释给的已经足够了,但是只是针对单个文件,对于整体爬虫项目,理解起来难免还会有点模糊
这一章爬取的是百度百科,给出一个url通过该页面的链接爬取其它的url,看起来就像这样:
给出一个Root_Url,看看它都经历了什么
Root_url作为整个程序的第一个Url,即为一个新的Url,要加入到Url管理器中
self.manager.add_new_url(root_url)
Root_Url对应的页面肯定含有其它知识的百科链接,因此要提取,在提取之前要从Url管理器中获得Root_Url,然后获取Root_Url对应的页面的页面信息:
new_url = self.manager.get_new_url()
html = self.downloader.download(new_url)
如何从Url管理器中获取Url?
通过pop()
方法每次从Url集合中取一个出来,并把取出的Url标记为已爬取的Url
URLManager.py
class UrlManager(object):
...
def get_new_url(self):
new_url = self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
...
如何获取页面信息?
通过requests
库
HtmlDownloader.py
class HtmlDownloader(object):
def download(self,url):
...
return r.text
...
有了页面信息,就可以把其它知识的百科链接提取出来,以及保存当前页面知识的信息:
new_urls,data = self.parser.parser(new_url,html)
怎么获取?
通过BeautifulSoup
解析HTML文档:
HtmlParser.py
class HtmlParser(object):
def parser(self,page_url,html_cont):
...
soup = BeautifulSoup(html_cont,'html.parser',from_encoding='utf-8')
new_urls = self._get_new_urls(page_url,soup)
new_data = self._get_new_data(page_url,soup)
...
def _get_new_urls(self,page_url,soup):
links = soup.find_all('a', href=re.compile(r'/item/.*'))
for link in links:
new_url = link['href']
new_full_url = urlparse.urljoin(page_url,new_url)
new_urls.add(new_full_url)
def _get_new_data(self,page_url,soup):
...
return data
如果把Root-Url看作父节点,那么第一次获取的Url集合为:
获取到Url集合之后把它保存到Url管理器中
self.manager.add_new_urls(new_urls)
对于Url管理器来说实际上就是把得到Url集合添加new_urls
集合的后面
URLManager.py
class UrlManager(object):
def __init__(self):
self.new_urls = set()
...
def add_new_url(self,url):
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
def add_new_urls(self,urls):
if urls is None or len(urls)==0:
return
for url in urls:
self.add_new_url(url)
...
通过BeautifulSoup
库解析后的参数有两个,new_urls
已经处理过了,即加入到Url管理器中了,data
还没处理,需要把它存到文件中:
self.output.store_data(data)
还要打印当前处理了多少Url,即获取处理过的Url:
print "已经抓取%s个链接"%self.manager.old_url_size()
相应地,Url管理器是这么做的:
URLManager.py
class UrlManager(object):
def __init__(self):
self.old_urls = set()
...
def old_url_size(self):
return len(self.old_urls)
这样一次循环就完事了,第二次循环呢?
会从第一次由Root_Url产生的子Url中选择一个进行处理,如此循环,有时可能在处理子Url时,还没处理当前子Url产生的子子Url,就去处理另一个子Url,等等情况。
如果Url所构造的树为下面这样该怎么做呢?
很简单,因为Url-X不会生成子Url,因此在处理Url-x时不获取该页面所包含的其它Url即可。
可以参考一下我基于一个下小说的简易爬虫改造成多文件的代码,而不在是一个文件完成所有的功能。
自己写的代码在这