什么是爬虫?按照我目前的理解:爬虫是一段脚本程序,能够自动从网站上下载网页,解析网页内容,并分析提取出所需要的数据。它最大的特点是能够自动循环运行,一个个遍历相关网页,可以抓取相当大的数据量。
就如同互联“网”上的”蜘蛛,巡视着这网上的每一个节点,择机捕获猎物。
第一次接触爬虫,我是看的慕课网上的Python开发简单爬虫 。这个教程讲解了爬虫的基本知识,并介绍了一种逻辑非常清晰的爬虫架构,实现了从百度百科上爬取1000个词条和及其简介数据,输出到一个HTML文件里。非常的实用,但是对于Python新手的我来讲还是有点复杂。代码量还不够,对于Python中的类理解还不够深。所以我就自己鼓捣,从最简单的开始写。
本篇就记录我从零开始,自己摸索,直至能够如写出上述教程中结构清晰的爬虫的一整个过程。
一、拟定需求 开始写一段程序之前,要明白自己的需求是什么。单纯一个“爬取百度百科词条”一句话,可以是客户给的需求,但是作为实际编程的人而言,要把需求细化到每一个步骤,然后再根据这些步骤一一去实现,最终达到最后的目标。下面是我初步拟出的需求:
1 2 3 4 5 6 7 # 制作一个百度百科爬虫 # 基本功能: # 1.手动给定初始页面URL # 2.自动下载初始页面 # 3.分析初始页面中包含的其他链接 # 4.依次自动下载并分析各链接的网页 # 5.依次打印出各链接对应的词条名
二、爬虫 Version 1.0 单次下载 由于很多模块的使用还不熟悉,直接实现上述需求对我来说还是有点困难,故而先实现一个无循环的简单功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import urllib2import bs4def download (url ): Page = urllib2.urlopen(url) soup = bs4.BeautifulSoup(Page.read(),'html.parser' ) print soup.h1.string root_url = r'https://baike.baidu.com/item/Python' download(root_url)
这段简单的代码中导入了两个模块,urllib2
是内置模块,用来实现下载指定链接的网页,bs4
是第三方模块,用来对网页内容进行解析。这样就实现了爬虫最最基本的功能:下载指定网页——解析网页内容——提取出所需要的信息。
三、爬虫 Version 2.0 实现循环 上面只是一个简单的单次下载的过程。下面要实现爬虫的自动循环的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import sysreload(sys) sys.setdefaultencoding( "utf-8" ) import urllib2import bs4import reurltogo = set() urlhasgone = set() n = 0 def download (url ): Page = urllib2.urlopen(url) soup = bs4.BeautifulSoup(Page.read(),'html.parser' ) for item in soup.find_all('a' ,href=re.compile(r"/item/" )): url = "https://baike.baidu.com" +item['href' ] if not url in urlhasgone: urltogo.add(url) global n print soup.h1.string,n,'has crawled' ,len(urltogo),'left to crawl' urlhasgone.add(url) def crawler (url ): urltogo.add(url) while True : next = urltogo.pop() global n n = n+1 download(next) keyword = raw_input("Please input a English word you want to research: " ) root_url = r'https://baike.baidu.com/item/' +keyword crawler(root_url)
导入了内置的sys
模块解决编码问题(但是还不知道原理,已经遇到过好几次,下次单独研究一下),导入内置的re
实现模糊匹配功能。如上所示,已经基本实现了一个爬虫的雏形。但是实际运行过程中,大约爬取两千个词条左右就会报错,应该是百度的反爬虫功能导致的。下面应该添加异常处理。然后再添加输出功能。
四、爬虫 Version 3.0 初具雏形 基本实现了爬虫功能后,我尝试将上述代码进行重构,按照不同的功能分为Url管理器,网页下载器,网页解析器,输出器,总调度程度五个部分分开编写,即Python开发简单爬虫 中的架构。这样做的好处是将不同的功能模块分开管理,方便之后维护和代码的重用。最终,运行下面的SpiderMain.py
程序,能够成功的下载1000个相关百度百科页面,并提取出其中的词条条目、词条简介、相关词条链接,最终输出到datas.xlsx
文件中。UrlManager.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class URL (object ): def __init__ (self ): self.oldurls = set() self.newurls = set() def add_new_url (self,url ): if url not in self.oldurls: self.newurls.add(url) def add_new_urls (self,urls ): for url in urls: if url not in self.oldurls: self.newurls.add(url) def get_new_url (self ): url = self.newurls.pop() self.oldurls.add(url) return url def has_new_url (self ): return len(self.newurls) != 0
HtmlDownloader.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import urllib2class DOWNLOAD (object ): def download (self,url ): response = urllib2.urlopen(url) if response.getcode() != 200 : return None return response.read()
HtmlParser.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import reimport bs4class PARSER (object ): def get_new_urls (self,soup ): newurls = set() for item in soup.find_all('a' ,href=re.compile(r"/item/" )): url = "https://baike.baidu.com" +item['href' ] newurls.add(url) return newurls def get_new_data (self,url,soup ): newdata = {} newdata['url' ] = url newdata['item' ] = soup.h1.string newdata['urls' ] = len(self.get_new_urls(soup)) summary_node = soup.find('div' , class_='lemma-summary' ) newdata['summary' ] = summary_node.get_text() return newdata def parser (self,url,page ): soup = bs4.BeautifulSoup(page,'html.parser' ,from_encoding='utf-8' ) newurls = self.get_new_urls(soup) newdata = self.get_new_data(url,soup) return newurls,newdata
HtmlOutputer.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import openpyxlclass OUTPUT (object ): def __init__ (self ): self.datas = [] def collect_data (self,data ): self.datas.append(data) def output (self ): wb = openpyxl.Workbook() ws = wb.active ws['A1' ] = 'num' ws['B1' ] = 'url' ws['C1' ] = 'item' ws['D1' ] = 'urls' ws['E1' ] = 'summary' for n,data in enumerate(self.datas): ws.cell(row = n+2 , column = 1 ).value = n+1 ws.cell(row = n+2 , column = 2 ).value = data['url' ] ws.cell(row = n+2 , column = 3 ).value = data['item' ] ws.cell(row = n+2 , column = 4 ).value = data['urls' ] ws.cell(row = n+2 , column = 5 ).value = data['summary' ] wb.save('datas.xlsx' )
SpiderMain.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import UrlManager,HtmlDownloader,HtmlParser,HtmlOutputerclass SPIDER (object ): def __init__ (self ): self.URL = UrlManager.URL() self.DOWNLOAD = HtmlDownloader.DOWNLOAD() self.PARSER = HtmlParser.PARSER() self.OUTPUT = HtmlOutputer.OUTPUT() def crawler (self,root_url ): self.URL.add_new_url(root_url) count = 1 while self.URL.has_new_url(): try : url = self.URL.get_new_url() print 'Crawl %d %s' %(count,url) page = self.DOWNLOAD.download(url) newurls,newdata = self.PARSER.parser(url,page) self.URL.add_new_urls(newurls) self.OUTPUT.collect_data(newdata) if count == 1000 : break count = count + 1 except : print 'Crawl failed!' self.OUTPUT.output() keyword = raw_input("Please input a English word you want to research: " ) root_url = r'https://baike.baidu.com/item/' +keyword mycrawler = SPIDER() mycrawler.crawler(root_url)
五、总结
练习了Python的基本语法,另外学习了几个写爬虫需要用到的库:urllib2
——下载网页、bs4
,re
——解析网页、openpyxl
——操作Excel文件。
学习了爬虫的一种基本架构:把一个爬虫分为Url管理器、网页下载器、网页解析器、输出器、总调度程序五个部分。
异常处理的思想:最开始是利用if response.getcode() != 200:
这个语句简单判断一下网页下载是否成功。然后在总调度程序中,将主要的循环体用一个try
语句包含起来,将一次循环的整个过程进行异常判断。其实在课程的源代码中,作者在每个模块的输入部分都加入了异常处理,判断输入为空时,应如何操作。我的代码中暂时没有加入这些。但是显然的,这些异常处理的语句让整个爬虫的容错率得到了提高。