目标:利用python做爬虫爬取百度百科python词条页面的数据
爬虫结构运行的流程:
url管理器:
管理带抓取url集合和已抓取url集合
防止重复抓取和循环抓取
支持的功能:
添加新url到待爬取的集合中
判断待添加url是否在集合中
从集合中获取待爬取url
判断集合中是否还有待爬取的url
将url从待爬取移动到已爬取集合中
网页下载器:
将互联网上url对应的网页下载到本地的工具
网页解析器:
在后面的实例中详细讲解
实例讲解:
分成四个部分:
url_manager,url_downloader, html_parser, html_outputer:
url_manager: #url管理器
class UrlManager(object):
#初始化url列表:存取爬取过得url列表,存取未爬取的url列表
def __init__(self):
self.old_urls = set() #已爬取过得url列表集合
self.new_urls = set() #未爬取过得url列表集合
#向url管理器添加一个新的待爬取的url
def add_new_url(self,url): #向url管理器添加一个新的url
if url is None:
return
#若这个url既不在爬取过得url列表集合也不在未爬取的url列表集合
if url not in self.old_urls and url not inself.new_urls:
self.new_urls.add(url) #将这个url添加到未爬取的url列表集合
def add_new_urls(self,urls): #向url管理器添加批量url
if urls is None or len(urls) ==0:
return
for url in urls:
self.add_new_url(url)
def has_new_url(self): #判断url管理器是否有新的url
return len(self.new_urls) !=0
def get_new_url(self): #从待爬取的url列表中获取一个url
new_url= self.new_urls.pop()
#pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
self.old_urls.add(new_url)
return new_url
url_downloader: #url下载器
import urllib2
class HtmlDownloader(object):
def download(self,url): #返回下载好的url的内容
if url is None:
return None
response = urllib2.urlopen(url)
if response.getcode()!= 200:
return None
return response.read()
#参考http://www.cnblogs.com/huangcong/archive/2011/08/31/2160633.html
‘’’
urllib2.urlopen(url):若要通过urllib模块中的urlopen(url [,data])函数打开一个HTML文档,必须提供该文档的URL地址,包括文件名。函数urlopen不仅可以打开位于远程web服务器上的文件,而且可以打开一个本地文件,并返回一个类似文件的对象,我们可以通过该对象从HTML文档中读出数据。一旦打开了HTML文档,我们就可以像使用常规文件一样使用read([nbytes])、readline()和readlines()函数来对文件进行读操作。若要读取整个HTML文档的内容的话,您可以使用read()函数,该函数将文件内容作为字符串返回。
getcode():返回Http状态码。如果是http请求,200请求成功完成;404网址未找到
read():文件对象提供了三个“读”方法: .read()、.readline()和 .readlines()。每种方法可以接受一个变量以限制每次读取的数据量,但它们通常不使用变量。 .read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。然而 .read()生成文件内容最直接的字符串表示,但对于连续的面向行的处理,它却是不必要的,并且如果文件大于可用内存,则不可能实现这种处理。
‘’’
html_parser: #html解析器
from bs4 import BeautifulSoup
import re
import urlparse
class HtmlParser(object):
def _get_new_urls(self, page_url, soup): #在soup里获取页面新的urls
new_urls = set() #获取到新的url列表的集合
links = soup.find_all('a',href=re.compile(r'/view/d+.htm'))
for link in links:
new_url = link['href'] #href属性对应的值就是页面中的url链接
new_full_url =urlparse.urljoin(page_url,new_url)
#拼接成完整的url
new_urls.add(new_full_url)
return new_urls #返回DOM树里面所有的新的url集合
#正则匹配:在soup里查询标签为a,正则匹配<a target="_blank"href="/view/592974.htm">#解释器</a>
#re.compile:可以把正则表达式编译成一个正则表达式对象。可以把那些经常使用的正则表达式编译成正则表达式对象,这样可以提高一定的效率。
#urljoin:函数urljoin(base, url [, allow_fragments])的作用是拼接URL,它以第一个参数作为其基地址,然后与第二个参数中的相对地址相结合组成一个绝对URL地址。函数urljoin在通过为URL基地址附加新的文件名的方式来处理同一位置处的若干文件的时候格外有用。需要注意的是,如果基地址并非以字符/结尾的话,那么URL基地址最右边部分就会被这个相对路径所替换。比如,URL的基地址为Http://www.testpage.com/pub,URL的相对地址为test.html,那么两者将合并成http://www.testpage.com/test.html,而非http://www.testpage.com/pub /test.html。如果希望在该路径中保留末端目录,应确保URL基地址以字符/结尾。
#http://www.cnblogs.com/huangcong/archive/2011/08/31/2160633.html
上面的链接有urljoin函数的具体实例,可以自己运行代码来增强认识。
def _get_new_data(self, page_url, soup): #解析title和summary
res_data = {} #data字典中存放title,summary,url的值
res_data['url'] = page_url
title_node = soup.find('dd',class_='lemmaWgt-lemmaTitle-title').find('h1') #
res_data['title'] = title_node.get_text()
summary_node = soup.find('div',class_="lemma-summary")
res_data['summary'] = summary_node.get_text()
return res_data
#python中title的源码
#<ddclass="lemmaWgt-lemmaTitle-title">
<h1>Python</h1>
<ahref="javascript:;" class="edit-lemma cmn-btn-hover-bluecmn-btn-28 j-edit-link" style="display: inline-block;"><emclass="cmn-icon wiki-lemma-iconswiki-lemma-icons_edit-lemma"></em>编辑</a>
<aclass="lock-lemma" target="_blank"href="/view/10812319.htm" title="锁定"><em class="cmn-icon wiki-lemma-iconswiki-lemma-icons_lock-lemma"></em>锁定</a>
</dd>
#python中summary的源码
#<divclass="para" label-module="para">Python(英国发音:/ˈpaɪθən/美国发音:/ˈpaɪθɑːn/),是一种<a target="_blank"href="/view/125370.htm">面向对象</a>、解释型<a target="_blank" href="/view/2561555.htm">计算机程序设计语言</a>,由<a target="_blank"href="/view/2975166.htm">Guido van Rossum</a>于1989年发明,第一个公开发行版发行于1991年。</div>
get_text():是返回文本,这个对每一个BeautifulSoup处理后的对象得到的标签都是生效的
def parse(self,page_url,html_cont): #返回content中的链接和数据
#传入的两个参数:当前正爬取的url,和url内容
if page_url is None or html_contis None:
return
soup = BeautifulSoup(html_cont,'html.parser',from_encoding ='utf-8') #创建一个beautifulsoup对象
#进行两个解析:从内容解析出新的urls和数据
new_urls = self._get_new_urls(page_url,soup)
new_data = self._get_new_data(page_url,soup)
return new_urls,new_data
#beautifulsoup官网:
html_doc ="""
<html><head><title>TheDormouse's story</title></head>
<body>
<pclass="title"><b>The Dormouse's story</b></p>
<p class="story">Once upona time there were three little sisters; and their names were
<ahref="http://example.com/elsie" class="sister"id="link1">Elsie</a>,
<a href="http://example.com/lacie"class="sister" id="link2">Lacie</a> and
<ahref="http://example.com/tillie" class="sister"id="link3">Tillie</a>;
and they lived at the bottom of awell.</p>
<pclass="story">…</p>
"""
frombs4import BeautifulSoup soup= BeautifulSoup(html_doc, 'html.parser')
所有的网页都有一个自己的编码。UTF-8是目前网站的标准编码。所以,当爬取这些网页时,爬虫程序必须要能理解这些网页的编码。否则,很有可能你在网页上看到的是正确的字符,而爬取获得的结果却是乱码。而BeautifulSoup则能熟练的处理这些编码。BeautifulSoup中的编码一般在一个网页中,可以从charset这个属性中看到网页的编码。
BeautifulSoup使用UnicodeDammit库来自动地检测文档的编码。BeautifulSoup创建soup对象时会自动地将内容转换为Unicode编码。了解HTML文档的原始编码,soup.original_encoding会告诉我们文档的原始编码是什么。指定HTML文档的编码,UnicodeDammit库会搜索整个文档然后来检测文档采取何种编码,这样一来浪费时间而且UnicodeDammit也有可能检测错误。如果知道文档编码是什么,那么可以在最初创建BeautifulSoup对象的时候就用from_encoding来指定文档的编码。soup = BeautifulSoup(html_markup,"lxml",from_encoding="utf-8")
当创建一个beautifulsoup的对象后,会将整个下载下来的网页内容生成一个DOM树(Document Object Model),这样就可以以树的形式进行上下级元素的遍历和访问。
html_outputer:
class HtmlOutputer(object):
def __init__(self): #初始化存放数据的列表
self.datas = []
def collect_data(self,data):#收集数据
if data is None:
return
self.datas.append(data)
def output_html(self): #将收集好的数据以一个html文件输出
fout = open('output.html','w') #写模式
fout.write("<html>") #html标签
fout.write("<body>")
fout.write("<table>") #数据以一个表格形式输出
for data in self.datas:
fout.write("<tr>") #行的开始标签
#每个单元格的内容
fout.write("<td>%s</td>" % data['url'])
fout.write("<td>%s</td>" % data['title'].encode('utf-8'))
fout.write("<td>%s</td>" % data['summary'].encode('utf-8'))
fout.write("</tr>")
fout.write("</table>") #闭合table
fout.write("</body>")
fout.write("</html>") #html闭合标签
fout.close()
爬虫总调度程序:
import url_manager
import html_downloader
import html_parser
import html_outputer
class SpiderMain(object):
def __init__(self): ##初始化各个对象
self.urls =url_manager.UrlManager()
self.downloader =html_downloader.HtmlDownloader()
self.parser =html_parser.HtmlParser()
self.outputer =html_outputer.HtmlOutputer()
#return
def craw(self, root_url): #爬虫的调度程序
count =1 #爬取一千个url
self.urls.add_new_url(root_url) #将入口地址添加到url管理器
while self.urls.has_new_url():
#管理器中已经有一个url了,可以启动爬虫的循环
try:#防止有些页面已经没有内容,或链接删除
new_url = self.urls.get_new_url() #获取一个待爬取的url
print 'craw%d : %s' % (count,new_url)
html_cont = self.downloader.download(new_url)
#下载url对应网页的内容
new_urls,new_data =self.parser.parse(new_url,html_cont)
self.urls.add_new_urls(new_urls)
self.outputer.collect_data(new_data) #数据的收集
if count == 10:
break
count=count+1
except:
print 'crawfailed'
self.outputer.output_html()
至此,我们的爬虫程序就写好了,点击总调度程序文件,run as python run,就可以在输出中看到我们爬取的到的内容。
神龙|纯净稳定代理IP免费测试>>>>>>>>天启|企业级代理IP免费测试>>>>>>>>IPIPGO|全球住宅代理IP免费测试