该文章学习自慕课网:http://www.imooc.com/learn/563
转载请注明出处
爬取百度百科的指定页面及其页面关联的n个页面的地址,标题,简介
百度百科的Python页面:http://baike.baidu.com/view/21087.htm
采用自顶向下的设计方式,完成爬虫代码
审查页面信息,发现该页面中所需信息为:
a)url 如:
自由软件
取href 为r'/view/\d+.html' 的a标签,取其href,前面拼接host地址
b)title 如
Python
取class为lemmaWgt-lemmaTitle-title的dd标签,取其子标签h1的文本
c)content 如
Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种...
取class为lemma-summary 的div中的文本
主调度模块SpiderMain,调用四个子模块:
1.UrlManager 负责新url的添加,新urls的批量添加,判断是否有未爬取的url,爬取过的url的剔除,提供未爬取的url地址供下载器使用
2.HtmlDownloader 负责下载指定url的html文本
3.HtmlParser 负责解析html文本,取得页面的title,content,页面中的urls
4.Writer 负责将爬取的数据写出到文件中
生成SpiderMain,其构造函数中初始化所需四个模块;
调用Spider.craw()开始爬取:
调用UrlManager,将初始地址添加到未爬取的url的set中;
进入主循环(如果UrlManager中有未爬取的url):
UrlManager从未爬取的url中取出一条url,
将url交由HtmlDownloader ,下载html文本,
将下载的文本交由HtmlParser,解析出其中的title,content,urls,
将当前的url,解析出的title,content,交由Writer保存,
将解析出的urls交由UrlManager,将其中未爬取的地址添加进未爬取的url的set中
结束循环,由Writer将结果写出到文件中
spider->(__init__.py,spider_main.py,url_manager.py,html_downloader.py,html_parser.py,wirter.py)
细节处理:由于所有地址都由base_url和path组成,也就是主机地址和路径,因此所有的url都由两部分拼接组成;另外由于每个子页面下都会有链接,会使得爬取不断的进行,因此设置一个count来限制爬取的网页数量
# -×- coding:utf-8 -*-
from url_manager import UrlsManager
from html_downloader import HtmlDownloader
from html_parser import HtmlParser
from writer import Writer
class SpiderMain(object):
def __init__(self, base_url, start_url, count=30):
self.base_url = base_url
self.start_url = start_url
self.count = count
self.urls_manager = UrlsManager(base_url)
self.downloader = HtmlDownloader()
self.parser = HtmlParser()
self.writer = Writer()
def craw(self):
if self.base_url is None or self.start_url is None:
return
count = 0;
self.urls_manager.add_new_url(self.start_url)
while self.urls_manager.has_new_urls():
print '爬取第%d条数据' % count
url = self.urls_manager.pop()
if not url or count >= self.count:
break
count += 1
html = self.downloader.load(url)
title, content, urls = self.parser.parse(html)
if urls and len(urls) > 0:
self.urls_manager.add_new_urls(urls)
if title or content:
self.writer.append(url, title, content)
self.writer.write()
if __name__ == '__main__':
start_url = 'view/21087.htm'
base_url = 'http://baike.baidu.com/'
spider = SpiderMain(base_url, start_url, 10)
spider.craw()
import urlparse
class UrlsManager(object):
def __init__(self, base_url):
self.base_url = base_url
self.new_urls_set = set()
self.old_urls_set = set()
def add_new_url(self, url):
url = self._abs_url(url)
if self._exist(url):
return
else:
self.new_urls_set.add(url)
def add_new_urls(self, urls):
if urls and len(urls) > 0:
for url in urls:
self.add_new_url(self._abs_url(url))
def has_new_urls(self):
return len(self.new_urls_set) > 0
def pop(self):
url = self.new_urls_set.pop()
self.old_urls_set.add(url)
return url
def _exist(self, url):
return url in self.new_urls_set or url in self.old_urls_set
def _abs_url(self, url):
return urlparse.urljoin(self.base_url, url)
import urllib2
class HtmlDownloader(object):
def load(self, url):
response = urllib2.urlopen(url)
html = response.read()
response.close()
return html
解析出title,content,和urls,以tuple的形式返回
# coding:utf-8
from bs4 import BeautifulSoup
import re
class HtmlParser(object):
def parse(self, html):
if html is None:
return (None, None, None)
soup = BeautifulSoup(html, 'html.parser', from_encoding='utf-8')
title = soup.find('dd', class_='lemmaWgt-lemmaTitle-title').find('h1').get_text()
try:
content = soup.find('div', class_='lemma-summary').get_text()
except:
content = 'error'
a_tags = soup.findAll('a', href=re.compile('view/\d+.htm'))
urls = [url['href'] for url in a_tags]
return title, content, urls
最终将结果保存在html文档中,需要注意的一点是文本输出的时候需要encode('utf-8')
# coding:utf-8
class Writer(object):
def __init__(self):
self.data = []
self.prefix = ''
self.suffix = ''
def append(self, url, title, content):
self.data.append((url, title, content))
def write(self):
with open('data.html', 'w') as f:
# f.encoding('utf-8')
f.write(self.prefix)
for item in self.data:
f.write(('%s
%s
' % item).encode('utf-8'))
f.write(self.suffix)
爬取完成后,打开data.html查看效果
该爬虫基本实现了爬取网页的需求,结构明确,分工合理。
不足:1.单线程:如果数据量大的时候,需要采用多线程处理
2.静态文本爬取:如果网页由Ajax动态生成,则获取不到数据
3.无状态爬取:如果网页需要登陆,禁止爬虫爬取,或者记录cookie,就需要额外的增加功能
新手入门,请多指教!