更多课程 选择中心


Python培训

400-996-5531

Python3 开发轻量级爬虫教程


爬虫又被称为网页蜘蛛,网络机器人。是一种按照一定的规则,自动地抓取互联网上信息的程序或者脚本。

一、爬虫算法

在写爬虫时候有两种常用的算法可使用,即深度优先算法、广度优先算法。

深度优先算法

对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。直到访问完成后再返回到最上层,然后重复上述步骤。

广度优先算法

从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。

负载均衡

当爬取量很大的话,需要负载到多台服务器同时运行(搜索引擎都是这么做的)。但这样会出现一个问题,当 A 服务器已经爬取完成的 URL,但 B 服务器并不知道 A 是否爬取完成,这样会造成资源的浪费,那怎么办呢?如何突破爬虫的瓶颈?

其中最简单的便是 URL 分类。举个栗子:现在有 A、B、C、D、X 五台服务器同时运行爬虫,X 为负载均衡服务器。所有的 URL 都要经过 X 服务器进行分配, X 服务器遇到域名是 .com 结尾的就分配给 A,遇到 .cn 结尾就分配给 B,遇到 .net 结尾就分配给 C,其他域名都分配给 D。这样就解决了爬虫瓶颈的问题,这个问题可是谷歌的面试题。

二、爬虫逻辑

爬虫可大致分为五个部分:

  • 调度器:引擎,是爬虫逻辑实现的模块

  • 管理器:URL 管理器,负责新增、删除、获取、存储、计数等功能,避免爬取重复的 URL。

  • 下载器:HTML 下载器,将 URL 地址中的 HTML 内容获取到

  • 解析器:HTML 解析器,将 HTML 获取到的内容进行分析

  • 输出器:将分析完成后的数据进行输出、存储、利用等

爬虫逻辑可分解为如下几个部分:

  1. 查询管理器中是否有待爬取的 URL

  2. 调度器从管理器中获取一个待爬取 URL

  3. 将获取到的 URL 交给下载器处理

  4. 将下载器获取到的网页数据交给解析器处理

  5. 将解析器处理后的 URL 集合交给管理器

  6. 将解析器处理后的数据交给输出器

  7. 重复上述步骤

三、开发轻量级爬虫

1. 调度器

spider.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

#
# 【调度器】
#
# 调度器又称为引擎,是爬虫逻辑实现的模块。
#
# 爬虫逻辑可分解为如下几个部分:
#
# 1. 查询管理器中是否有待爬取的 URL
# 2. 调度器从管理器中获取一个待爬取 URL
# 3. 将获取到的 URL 交给下载器处理
# 4. 将下载器获取到的网页数据交给解析器处理
# 5. 将解析器处理后的 URL 集合交给管理器
# 6. 将解析器处理后的数据交给输出器
# 7. 重复上述步骤
# 


import manager, download, parser, output


class Spider(object):

    def __init__(self):

        # 实例化 管理器、下载器、解析器、输出器
        self.manager  = manager.Manager()
        self.download = download.Download()
        self.parser   = parser.Parser()
        self.output   = output.Output()

    def spider(self, url):

        # 爬虫计数器
        count = 0

        # 将第一个 URL 放入管理器中
        self.manager.add_url(url)

        # 判断管理器中 URL 的数量是否为 0
        while self.manager.num_url():

            try:

                # 从管理器中获取一个 URL
                new_url = self.manager.get_url()

                # 将获取到的 URL 交给下载器处理
                html_cont = self.download.download(new_url)

                # 将 URL 和 下载器处理后的网页数据交给解析器处理
                new_manager, new_data = self.parser.parser(new_url, html_cont)

                # 将解析器处理后的数据和新的 URL 分别交给管理器和输出器
                self.manager.add_urls(new_manager)
                self.output.add_data(new_data)

                # 最多爬取 100 次
                if count == 100:
                    break

                count += 1

                print('爬虫运行状态 ==> %d : %s' % (count, new_url))

            except:

                print('爬虫爬取失败')

        # 当爬虫爬取完所有数据后,输出器将数据输出到文件
        self.output.output()

if __name__ == '__main__':

    # URL 入口(爬虫爬取的第一个 URL)
    url = '#/peida/archive/2012/12/05/2803591.html'

    # 实例化调度器并启动爬虫
    run = Spider()
    run.spider(url)

2. 管理器

manager.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

#
# 【管理器】
#
# 对外提供四个方法:
#
# 1. add_url(url)
# 添加一个 URL,接收一个参数 URL
# 
# 2. add_urls(url)
# 批量添加 URL,接收一个参数 URL
#
# 3. num_url()
# 管理器中URL 的数量
# 
# 4. get_url()
# 从管理器中获取一个 URL
#


class Manager(object):

    def __init__(self):

        # 待爬取的 URL 集合
        self.new_urls = set()
        # 已爬取的 URL 集合
        self.old_urls = set()

    # 添加一个 URL
    def add_url(self, url):

        # 判断 url 是否为空,为空则返回 None
        if url is None:
            return

        # 判断 url 是否存在于【待爬取的 URL 集合】和【已爬取的 URL 集合】,如若都不在将新 url 添加到【待爬取的 URL 集合】中
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)

    # 批量添加 URL
    def add_urls(self, urls):

        if urls is None or len(urls) == 0:
            return

        for url in urls:
            # 将 URL 集合交给 add_url() 函数处理
            self.add_url(url)

    # 返回管理器中 URL 的数量
    def num_url(self):

        return len(self.new_urls) != 0

    # 从管理器中获取一个 URL
    def get_url(self):

        url = self.new_urls.pop()
        self.old_urls.add(url)

        return url

3. 下载器

download.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

#
# 【下载器】
#
# requests 模块需要安装
# sudo pip3 install requests
#
# 对外提供一个方法:
#
# download(url)
# 下载网页中的内容,接收一个参数 URL
#


import requests


class Download(object):

    def download(self, url):

        # 判断 URL 是否为空,为空直接返回 None
        if url is None:
            return None

        # 请求 URL
        response = requests.get(url = url)

        # 判断 URL 地址是否访问成功,若失败直接返回 None
        if response.status_code != 200:
            return None

        return response.text

4. 解析器

parser.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

#
# 【解析器】
#
# bs4 模块需要安装
# sudo pip3 install bs4
#
# 对外提供一个方法:
#
# 1. parser(url)
# BeautifulSoup DOM 树处理,接收两个参数 URL 和 html 内容
#
# 对内提供两个方法:
#
# 1. _get_urls(url, soup)
# 获取网页中的 URL,接收两个参数 URL 和 DOM 树
#
# 2. _get_data(url, soup)
# 获取网页中的数据,接收两个参数 URL 和 DOM 树
#


import re
from bs4 import BeautifulSoup


class Parser(object):

    def _get_urls(self, url, soup):

        urls = set()

        links = soup.find_all('a', href=re.compile(r'/peida/archive/*'))

        for link in links:

            new_url = link['href']
            urls.add(new_url)

        return urls

    def _get_data(self, url, soup):

        res_data = {}
        res_data['url'] = url
        title_node = soup.find('div', class_='post').find('a')
        res_data['title'] = title_node.get_text()

        return res_data

    def parser(self, url, html):

        # 判断 url 是否为空,或网页数据是否为空,为空则返回 None
        if url is None or html is None:
            return

        # 格式化 html 为 BeautifulSoup DOM 树
        soup = BeautifulSoup(html, 'html.parser')

        # 获取网页中的 URL
        urls = self._get_urls(url, soup)
        # 获取网页中的数据
        data = self._get_data(url, soup)

        return urls, data

5. 输出器

output.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

#
# 【输出器】
#
# 对外提供两个方法:
#
# 1. add_data(data) 
# 存储网页数据,接收一个参数 data
# 
# 2. output()
# 将数据输出到 html 文件中
#


class Output(object):

    def __init__(self):

        self.datas = []

    def add_data(self, data):

        if data is None:
            return 

        self.datas.append(data)

    def output(self):

        head = '''

            <html>
            <meta charset="UTF-8">
            <body>
            <table>

        '''

        foot = '''

            </table>
            </body>
            </html>

        '''

        with open('CSDN.html', 'w') as f:

            f.write(head)

            for data in self.datas:

                f.write('<tr><td><a href =%s>%s</a></td>' % (data['url'], data['url']))
                f.write('<td>%s</td></tr>' % data['title'])

            f.write(foot)

运行结果:

预约申请免费试听课

填写下面表单即可预约申请免费试听! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!

上一篇:如何让你的python代码更高效灵活
下一篇:使用 Python 标准库上传文件

Python IDE推荐7个你可能会错过的Python IDE

Python面试题之Python中爬虫框架或模块的区别

2021年Python面试题及答案汇总详解

python数据分析,你需要这些工具

Copyright © 2023 Tedu.cn All Rights Reserved 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有

选择城市和中心
黑龙江省

吉林省

河北省

湖南省

贵州省

云南省

广西省

海南省