运维学python之爬虫中级篇(六)基础爬虫

通过这么多天对爬虫的介绍, 我们对爬虫也有所了解,今天我们将引入一个简单爬虫的技术架构,解释爬虫技术架构中的几个模块,后面的爬虫也多是今天架构的延伸,只不过这个架构是简单的实现,对优化、爬取方式等不是很完善,主要为了方便大家对爬虫的理解和后面的编程。

1 基础架构和流程

简单的爬虫架构由以下几部分构成:
爬虫调度器:总体协调其它几个模块的工作
URL管理器:负责管理URL,维护已经爬取的URL集合和未爬取的URL集合
网页下载器:对未爬取的URL下载
网页解析器:解析已下载的html,并从中提取新的URL交给URL管理器,数据交给存储器处理
数据存储器:将html解析出来的数据进行存取
架构图如下:

爬虫流程图如下:

下面我们就分别按每个部分来拆分。
我们本次就拿百科搜索词条来演示,爬取百科内容标题和摘要信息,同时如果摘要中有链接,还会把连接的标题摘要下载下来。如下图:

2 URL管理器

基本功能:

  • 判断是否有待爬取的url
  • 添加新的url到待爬取url集合中
  • 获取未爬取的url
  • 获取待爬取集合大小
  • 获取已爬取集合大小
  • 将爬取完成的url从待爬取url集合移动到已爬取url集合。

这里URL管理器还要用到set去重功能,防止程序进入死循环。大型互联网公司,由于缓存数据库的高性能,一般把url存储在缓存数据库中。小型公司,一般把url存储在内存中,如果想要永久存储,则存储到关系数据库中。
URL管理器代码

# -*- coding: utf-8 -*-

class UrlManager:
    """
    URL管理器类
    """

    def __init__(self):
        """
        初始化未爬取集合new_urls和已爬取集合old_urls
        """
        # python的set和其他语言类似, 是一个无序不重复元素集
        self.new_urls = set()
        self.old_urls = set()

    def has_new_url(self):
        """
        判断是否有未爬取的url
        :return:
        """
        return self.new_url_size() != 0

    def get_new_url(self):
        """
        获取未爬取的url
        :return:
        """
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url

    def add_new_url(self, url):
        """
        将新的url添加到未爬取的url集合中
        :return:
        """
        if url is None:
            return
        # 判断url是否在new_urls或old_urls中
        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):
        """
        添加新的url到未爬取集合
        :param urls: url集合
        :return:
        """
        if urls is None or len(urls) == 0:
            return
        # 循环将获取的url存入new_urls中
        for url in urls:
           self.add_new_url(url)

    def new_url_size(self):
        """
        获取未爬取集合大小
        :return:
        """
        return len(self.new_urls)

    def old_url_size(self):
        """
        获取已爬取集合大小
        :return:
        """
        return len(self.old_urls)

HTML下载器
HTML下载器就比较简单了,只是通过requests获取html内容即可(注意:要用到session,不然会报 TooManyRedirects 异常),具体看代码:

# -*- coding: utf-8 -*-
import requests

class HtmlDownload:
    """
    HTML下载器类
    """

    def download(self, url):
        """
        下载html
        :param url:根据url下载html内容
        :return: 返回html
        """
        # 创建session对象,这里一定要用session,不然会报TooManyRedirects异常
        s = requests.session()
        # 添加头部信息
        s.headers[‘User-Agent‘] = ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0‘
        # 获取内容
        resp = s.get(url)
        # 要以content字节形式返回
        return resp.content.decode()

HTML解析器
先来分析要爬取的网页:

通过上面分析可以看到,我们需要的标题在<dd class="lemmaWgt-lemmaTitle-title">标签中,摘要信息在<div class="para" label-module="para">标签中,其中<div class="para" label-module="para">标签中的a标签的herf是我们需要继续爬取的相对url通过上面分析可以看到,我们需要的标题在<dd class="lemmaWgt-lemmaTitle-title">标签中,摘要信息在<div class="para" label-module="para">标签中,其中<div class="para" label-module="para">标签中的a标签的herf是我们需要继续爬取的相对url。

# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup

class HtmlParse:
    """
    HTML解析器类
    """

    def __init__(self, baseurl=‘https://baike.baidu.com‘):
        """
        初始化基础url
        :param baseurl:基础url
        """
        self.baseurl = baseurl

    def parse_data(self, page):
        """
        获取网页数据
        :param page:获取的html
        :return:解析后要存储的数据
        """
        # 创建实例
        soup = BeautifulSoup(page, ‘lxml‘)
        # 获取标题
        title = soup.find(‘dd‘, class_=‘lemmaWgt-lemmaTitle-title‘).find(‘h1‘).string
        # 获取摘要
        summary = soup.find(‘div‘, class_=‘para‘).text
        # 完整的数据信息
        data = title + summary
        return data

    def parse_url(self, page):
        """
        获取网页url
        :param page:获取的html
        :return:解析后获取的新url
        """
        # 创建实例
        soup = BeautifulSoup(page, ‘lxml‘)
        # 所有的<div class="para" label-module="para">中a标签
        anodes = soup.find(‘div‘, class_=‘para‘).find_all(‘a‘)
        # new_url set集
        new_urls = set()
        # 循环获取相对路径(href),与baseurl拼成全url
        for anode in anodes:
            link = anode.get(‘href‘)
            # 完整路径
            fullurl = self.baseurl + link
            # 添加到未爬取集合new_urls中
            new_urls.add(fullurl)
        return new_urls

数据存储器
解析出来的数据就直接追加保存到文件就可以了。

# -*- coding: utf-8 -*-

class DataStore:
    """
    数据存储器类
    """

    def store_data(self, data, name=‘baike.txt‘):
        """
        将获取的数据存储到文件中
        :param data: 解析的数据
        :param name: 本地文件名
        :return:
        """
        with open(name, ‘a‘, encoding=‘utf-8‘) as fp:
            fp.write(‘\r\n‘)
            fp.write(data)

爬虫调度器
爬虫调度器主要负责以上几个模块的调度

# -*- coding: utf-8 -*-
# 导入各个模块
from spider.Datastore import DataStore
from spider.Htmldownload import HtmlDownload
from spider.Htmlparse import HtmlParse
from spider.Urlmanager import UrlManager

class SpiderMain:
    """
    爬虫调度器类
    """

    def __init__(self):
        """
        初始化各模块
        """
        self.manager = UrlManager()
        self.download = HtmlDownload()
        self.parse = HtmlParse()
        self.output = DataStore()

    def spider(self, url):
        """
        爬虫主程序
        :param url:初始url
        :return:
        """
        # 添加初始url到未爬取的new_urls中
        self.manager.add_new_url(url)
        # 通过while循环获取是否还有新的url要爬取,爬取了5条就结束,防止死循环下去
        while self.manager.has_new_url() and self.manager.old_url_size() < 5:
            try:
                # 获取url
                new_url = self.manager.get_new_url()
                # 获取html
                html = self.download.download(new_url)
                # 解析后的新url
                new_urls = self.parse.parse_url(html)
                # 解析后要存储的数据
                data = self.parse.parse_data(html)
                # 添加解析后的新url到new_urls集中
                self.manager.add_new_urls(new_urls)
                # 保存数据
                self.output.store_data(data)
                print(‘已经抓取%s 个链接‘ % self.manager.old_url_size())
            except Exception as e:
                print(‘failed‘)
                print(e)

if __name__ == ‘__main__‘:
    # 实例化爬虫调度器类
    spidermain = SpiderMain()
    # 输入初始url进行爬取
    spidermain.spider(‘https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB‘)

运行结果(只爬取了5条):

已经抓取1 个链接
已经抓取2 个链接
已经抓取3 个链接
已经抓取4 个链接
已经抓取5 个链接

好,今天的爬虫基础框架,就到这里了。
本文部分参考:范传辉 python爬虫开发与项目实战

原文地址:http://blog.51cto.com/linuxliu/2059671

时间: 2024-10-07 20:32:27

运维学python之爬虫中级篇(六)基础爬虫的相关文章

运维学python之爬虫高级篇(六)scrapy模拟登陆

上一篇介绍了如何爬取豆瓣TOP250的相关内容,今天我们来模拟登陆GitHub. 1 环境配置 语言:Python 3.6.1 IDE: Pycharm 浏览器:firefox 抓包工具:fiddler 爬虫框架:Scrapy 1.5.0 操作系统:Windows 10 家庭中文版 2 爬取前分析 分析登陆提交信息分析登陆信息我使用的是fiddler,fiddler的使用方法就不作介绍了,大家可以自行搜索,首先我们打开github的登陆页面,输入用户名密码,提交查看fiddler获取的信息,我这

运维学python之爬虫中级篇(五)数据存储(无数据库版)

本篇主要介绍,爬取html数据后,将html的正文内容存储为json或csv格式. 1 json格式存储 选定要爬取的网站后,我们利用之前学过的内容,如:Beautiful Soup.xpath等方式解析,来获取我们希望得到的内容. 1.1 获取数据 首先使用urllib访问页面https://www.lagou.com/zhaopin/Python/?labelWords=label获取html内容,代码如下: from urllib import request try: url = 'ht

运维学python之爬虫中级篇(八)MongoDB

1 MongoDB MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.具有高性能.高可用性和自动扩展性.MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成.MongoDB 文档类似于 JSON 对象的BSON.字段值可以包含其他文档,数组及文档数组.MongoDB最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引. 1.1 安装Mongo

运维学python之爬虫高级篇(七)scrapy爬取知乎关注用户存入mongodb

首先,祝大家开工大吉!本篇将要介绍的是从一个用户开始,通过抓关注列表和粉丝列表,实现用户的详细信息抓取并将抓取到的结果存储到 MongoDB. 1 环境需求 基础环境沿用之前的环境,只是增加了MongoDB(非关系型数据库)和PyMongo(Python 的 MongoDB 连接库),默认我认为大家都已经安装好并启动 了MongoDB 服务. 项目创建.爬虫创建.禁用ROBOTSTXT_OBEY设置略(可以参考上一篇) 2 测试爬虫效果 我这里先写一个简单的爬虫,爬取用户的关注人数和粉丝数,代码

运维学python之爬虫高级篇(五)scrapy爬取豆瓣电影TOP250

对于scrapy我们前面已经介绍了简单的应用,今天我们用一个完整的例子,爬取豆瓣电影TOP250来做一个小的练习,把scrapy阶段做一个总结. 1 环境配置 语言:Python 3.6.1 IDE: Pycharm 浏览器:firefox 爬虫框架:Scrapy 1.5.0 操作系统:Windows 10 家庭中文版 2 爬取前分析 2.1 需要保存的数据 首先确定我们要获取的内容,在items中定义字段,来将非结构化数据生成结构化数据,获取的内容主要包括:排名.电影名称.得分.评论人数.如下

linux运维及Python运维免费公开课

适用人群:想从事linux运维及python运维开发的人员 企业网管.技术支持.linux运维人员.大中专学生 听课时间:2014年11月30日(周日)下午1:30 听课地点:北京市昌平区沙河青年创业大厦B座1519室(地铁昌平线沙河站B1口200米处) 听课内容: LINUX运维:(1.5小时) 1.软件开源的大发展趋势及如何把握这个趋势? 2.linux运维职位到底都做什么? 3.linux运维前景到底咋样? 4.到底是选择运维还是选择开发发展? 5.运维人员如何超越年薪30万,50万? 6

Linux运维 第四阶段 (六)MySQL备份&&还原(mysqldump、LV’s snapshot、xtrabackup)

Linux运维 第四阶段 (六)MySQL备份&&还原(mysqldump.LV's snapshot.xtrabackup) 一.相关概念 备份:副本,mysql-database备份不同于RAID(RAID是保证硬件损坏而不会业务终止) 备份内容:数据.配置文件.二进制日志.事务日志 1.备份类型: >热备份.温备份.冷备份 热备份:读写不受影响,复杂度高,InnoDB(xtrabackup,mysqldump),lvm快照功能可实现几乎热备: 温备份:仅可执行读操作,MyISA

小白学 Python(23):Excel 基础操作(上)

人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变量基础操作 小白学 Python(5):基础运算符(上) 小白学 Python(6):基础运算符(下) 小白学 Python(7):基础流程控制(上) 小白学 Python(8):基础流程控制(下) 小白学 Python(9):基础数据结构(列表)(上) 小白学 Python(10):基础数据结构(

运维小白的成长日记第四天-基础网络构建OSI七层模型-物理层基础知识

运维小白的成长日记第四天- 基础网络构建OSI七层模型-物理层基础知识 网络运维的小白和想要加入网络运维的小伙伴们值得一看哦~ 今天是初识网络运维的第四天.希望能有志同道合的小伙伴一起讨论和学习,也希望有网络运维的大神能够帮忙在网络运维这条路上帮忙指点,能够多提意见使我进步. 今天和大家分享一下OSI七层模型中的物理层基础知识. 一.物理介质 1.传输数据的物理介质有:双绞线.光纤.同轴电缆(有线电视).无线.电力线.红外.蓝牙.微波 2.信号 (1)模拟信号:比如声波(水波纹) (2)数字信号