IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 简单爬虫设计(五)——重构控制流程 -> 正文阅读

[Python知识库]简单爬虫设计(五)——重构控制流程

前言

关于本文的一些背景知识,请移步该系列的前序文章。

简单爬虫设计(一)——基本模型

简单爬虫设计(二)——爬取范围

简单爬虫设计(三)——需要处理的网页范围

简单爬虫设计(四)——管理爬虫内部状态

待重构的代码和坏味道

在本系列第一篇文章中,展示了爬虫的控制逻辑代码,现在重新把它放在下面。这部分代码虽然利用了领域模型基本模块,但是职责不清,层次不够清晰,很多语句并不在相同的抽象层次上。

包含的代码坏味道有:

  • 同一个函数有多个职责:fetchAndProcess方法
  • 同一个函数中的语句不在一个抽象层次:crawl方法
  • 没有一个地方可以清楚地展现完整处理流程
//示意代码,忽略了部分实现细节
public class CrawlerImpl implements Crawler {
     
    public void crawl() {
        Link target = null;
        while (null != (target = targetLinks.next())) {
            try {
                fetchAndProcess(target);
                if (this.fetchedLinks.total() >= crawlingScope.maxToCrawl()) {
                    return;
                }
                TimeUnit.MILLISECONDS.sleep(this.crawlDelay);
            } catch (Exception e) {
                //记录错误信息
            }
        }
    }
}

private void fetchAndProcess(Link target) {
        //不在爬取范围内,略过
        if (!this.crawlingScope.contains(target)) {
            return;
        }
        //已经爬取过,略过
        if (target.getDepth() > 0 && this.fetchedLinks.contains(target)) {
            return;
        }
        
        Webpage webpage = fetch(target);

        if (processingScope.contains(webpage)) {
            webpageRepository.add(webpage);
        }

        Links allLinks = webpage.links();
        for (Link link : allLinks) {
            
            if (this.crawlingScope.contains(link) && 
                !this.fetchedLinks.contains(link)) {
                targetLinks.add(link);
            }
        }
        //当前链接为放入已采集集合
        this.fetchedLinks.add(target);
    }

重构过程

第一步,拆分fetchAndProcess方法。

这个方法中包含了太多职责,有判断是否需要爬取的逻辑,有下载网页的逻辑,还有处理网页的逻辑和抽取链接的逻辑,需要依次提取成独立的方法。

  1. 提取fetch方法,单纯的下载网页,并返回Webpage对象。
//单纯的下载网页,返回Webpage对象
private Webpage fetch(Link target) {
    Webpage webpage = downloader.download(target);
    return webpage;
}
  1. 提取shouldFetch方法,判断一个目标是否需要采集,返回布尔值。
//判断一个目标是否需要采集
private boolean shouldFetch(Link target) {
      //不在爬取范围内,略过
      if (!this.crawlingScope.contains(target)) {
          return false;
      }
      //已经爬取过,略过
      if (target.getDepth() > 0 && this.fetchedLinks.contains(target)) {
          return false;
      }
      return true;
  }
  1. 提取process方法,仅处理网页,保存或者抽取详细信息。
//处理网页,保存或者抽取详细信息
private void process(Webpage webpage) {

    if (processingScope.contains(webpage)) {
        webpageRepository.add(webpage);
        logger.info("Saved html {}", webpage.getUrl());
    }

    if (this.webExtractor != null) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        System.out.println(gson.toJson(extract(webpage)));
    }
}
  1. 提取harvestLinks方法,从网页中收集需要继续爬取的链接。
//从网页中收集链接
private Collection<Link> harvestLinks(Links allLinks) {
    List<Link> result = new LinkedList<>();
    for (Link link : allLinks) {
        if (this.crawlingScope.contains(link) && 
           !this.fetchedLinks.contains(link)) {
            result.add(link);
        }
    }
    return result;
}
  1. 提取shouldStop方法,判断是否应该停止采集。
private boolean shouldStop() {
    if (this.fetchedLinks.total() >= crawlingScope.maxToCrawl()) {
        return true;
     }
    return false;
}

第二步,重新整理crawl方法。

可以更加清晰展现爬虫的控制逻辑,尽量让每个语句在同一个抽象层次。

public void crawl() {     
    Link target;
    while (null != (target = this.targetLinks.next())) {
        try {
            if (!shouldFetch(target)) {  //是否需要采集
                continue;
            }
            //爬取网页
            Webpage webpage = fetch(target);   
            //处理网页
            process(webpage);                  
            //收集链接
            this.targetLinks.addAll(this.harvestLinks(webpage.links())); 
            //设置已采集
            this.fetchedLinks.add(target);

            if (shouldStop()) {  //是否停止
                break;
            }
            TimeUnit.MILLISECONDS.sleep(this.crawlDelay);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

小结

通过重构,学到了两点:

一是要通过深入理解领域逻辑,抽象好的领域模型;

二是用好领域模型,需要重构加深对应用逻辑的理解。

有了这两点,才能让应用逻辑更加清晰,方便后续扩展功能。

如果喜欢这系列文章,欢迎点赞+关注+收藏!

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-01-08 13:57:17  更:2022-01-08 13:59:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:37:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码