Python 简单爬虫实现(爬取百度百科信息)

该文章学习自慕课网: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)

详细代码:

spider_main.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()


url_manager.py

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)



html_downloader.py

import urllib2


class HtmlDownloader(object):
    def load(self, url):
        response = urllib2.urlopen(url)
        html = response.read()
        response.close()
        return html


html_parser.py

解析出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


writer.py

最终将结果保存在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,就需要额外的增加功能


新手入门,请多指教!

你可能感兴趣的:(Python,python,爬虫)