`

爬虫基础类

阅读更多
自己封装的爬虫基础类。


public interface TaskBaseInfo {
	
	/**
	 * 返回任务的名称.
	 * <br/>
	 * 一般用作日志输出
	 * @return
	 */
	String taskName();

	/**
	 * 返回任务的唯一code
	 * @return 在整个爬虫项目中不重复的Code值
	 */
	String taskCode();
}



import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.JedisCluster;

public interface TaskStringCache {

	Logger logger = LoggerFactory.getLogger(TaskStringCache.class);
	
	String BASE_FILE_PATH = "/mfs/ShareFile/static/cms/crawler/cache/";
	
	JedisCluster obtainJedisCluster();
	
	String getCacheStr(String taskCode, String cacheKey);

	void setCacheStr(String taskCode, String cacheKey, int cacheSeconds, String cacheStr);
	
	
	
	default String obtainTargetFilePath(String taskCode, String cacheKey) {
		
		return BASE_FILE_PATH + taskCode + File.pathSeparator + cacheKey + ".properties";
	}
	
	/**
	 * 设置缓存的默认方法
	 * @param taskName 任务中文名,日志使用
	 * @param taskCode 任务code需保持唯一性
	 * @param cacheKey 缓存的key
	 * @param cacheStr 缓存的值
	 */
    default void defaultSetCacheStr(String taskName, String taskCode, String cacheKey, int cacheSeconds, String cacheStr) {

        JedisCluster jedisCluster = obtainJedisCluster();
        jedisCluster.setex(cacheKey, cacheSeconds, cacheStr);
        String targetFilePath = obtainTargetFilePath(taskCode, cacheKey);
        save2FileAtomic(taskName, targetFilePath, cacheStr);
    }

    /**
     * 获取通过【设置缓存的默认方法】{@link #defaultSetCacheStr(String, String, String, String)}设置的缓存
     * @param taskName 任务中文名,日志使用
     * @param taskCode 任务code需保持唯一性
     * @param cacheKey 缓存的key
     * @return
     */
    default String defaultGetCacheStr(String taskName, String taskCode, String cacheKey) {

        JedisCluster jedisCluster = obtainJedisCluster();
        String cacheStr = jedisCluster.get(cacheKey);
        if (StringUtils.isNotBlank(cacheStr)) {

            return cacheStr;
        }

        String targetFilePath = obtainTargetFilePath(taskCode, cacheKey);
        try {
        	// 没利用到多少异步的优势,执行异步操作后马上获取结果还是会阻塞
            cacheStr = readFile(targetFilePath).get();
        } catch (InterruptedException | ExecutionException e) {
			
        	logger.error("【" + taskName + "】 执行异步获取文件缓存内容时失败. taskCode=>" + "【" + taskCode + "】" + " cacheKey=>" + "【" + cacheKey + "】");
        	logger.error(e.getMessage());
		}

        return cacheStr;
    }

    /**
     * 通过文件持久化爬取的游标Id,避免在数据增加字段
     * 文件写入操作较慢,异步执行
     * 原子操作,避免写入和读取的并发问题
     *
     * @param filePath
     * @return
     */
    default void save2FileAtomic(String taskName, String filePath, String content) {

        CompletableFuture.runAsync(() -> {

			File tmpFile = new File(filePath + ".tmp");
			try {

				if (tmpFile.exists() == false) {

					tmpFile.getParentFile().mkdirs();
					tmpFile.createNewFile();
				}
				try (FileWriter fw = new FileWriter(tmpFile)) {

					fw.write(content);
					fw.flush();
				}

			} catch (IOException e) {

				logger.error("【" + taskName + "】 => 写入缓存字符串到文件 => 【" + tmpFile + "】 时异常	\n" + e.getMessage());
				logger.error("【" + taskName + "】 文件写入操作退出");
				if (tmpFile.exists()) {
					tmpFile.delete();
				}
				return;
			}

			if (tmpFile.exists() == false) {

				return;
			}
            
            // 此段注释针对windows系统在同一个文件系统内且是同一个盘符下已经有一个目标文件;
            // 下面的renameTo操作会失败,造成无限递归调用进而 【栈溢出】 异常
            // 在Linux运行的情况下,可暂时先注释掉,测试没问题后上线
            // 注释开始段
//			File destFile = new File(filePath);
//			if (destFile.exists()) {
//				destFile.delete();
//			}
            // 注释结束段

            if (tmpFile.renameTo(new File(filePath))) {

                tmpFile.delete();
            } else {

            	logger.error("move fails filePath:" + filePath);
                tmpFile.delete();
                this.save2FileAtomic(taskName, filePath, content);
                // 当在Linux某个发行版下测试时,renameTo操作出错的话,可不硬性要求原子操作,
                // 可将上面的原子操作注释掉,改为下面的操作
//				save2File(filePath, content);
            }
        });
    }

//	default void save2File(String filePath, String content) throws IOException {
//		
//		try (FileWriter fw = new FileWriter(new File(filePath))) {
//
//			fw.write(content);
//			fw.flush();
//		}
//	}

    /**
     * 异步读取文件内容
     *
     * @param filePath
     * @return
     * @throws IOException
     * @throws FileNotFoundException
     */
	default CompletableFuture<String> readFile(String filePath) {

		return CompletableFuture.supplyAsync(() -> {

			StringBuilder strb = new StringBuilder();
			try (FileInputStream fis = new FileInputStream(filePath); 
					BufferedReader inReader = new BufferedReader(new InputStreamReader(fis));) {

				String line = StringUtils.EMPTY;
				while ((line = inReader.readLine()) != null) {
					strb.append(line);
				}
			} catch (IOException e) {

				logger.error(e.getMessage());
				return StringUtils.EMPTY;
			}

			return strb.toString();
		});
	}
}



public interface BasicTask {

	void run();
}



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.xxx.zx.crawler.basic.BasicTask;

public abstract class BaseCrawlerTask implements TaskBaseInfo, TaskStringCache, BasicTask, ApplicationContextAware {

	protected final Logger logger = LoggerFactory.getLogger(getClass());
	
	protected static ApplicationContext ac;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		
		ac = applicationContext;
	}
	
	public synchronized static <T> T getBean(Class<T> beanClass) {
		
		return ac.getBean(beanClass);
	}
	
	public synchronized static Object getBean(String beanName) {
		
        return ac.getBean(beanName);
    }  
	
	@Override
	public String getCacheStr(String taskCode, String cacheKey) {
		
		return defaultGetCacheStr(taskName(), taskCode, cacheKey);
	}

	@Override
	public void setCacheStr(String taskCode, String cacheKey, int cacheSeconds, String cacheStr) {
		
		defaultSetCacheStr(taskName(), taskCode, cacheKey, cacheSeconds, cacheStr);
	}
}
1
0
分享到:
评论

相关推荐

    Python爬虫基础类库源码示例.zip

    Python爬虫教学基础类库源码示例,实战教学,仅供学习参考。

    2017黑马Python就业班

    │ 第1节 Python爬虫基础类库 │ 第2节 scrapy框架 │ 第3节 scrapy-redis分布式策略 │ 资料 │ ├─08 tornado │ 第1节 Tornado │ 第2节 爱家租房项目 │ 第3节 微信公众号 │ 资料 │ ├─09shell和自动化运维 ...

    零基础Python爬虫从入门到精通-视频教程网盘链接提取码下载 .txt

    本套视频教程适合想掌握爬虫技术的学习者,以企业主流版本Python 3.7来讲解,内容包括:Python基础、Urllib、解析(xpath、jsonpath、beautiful)、requests、selenium、Scrapy框架等。针对零基础的同学可以从头学起...

    python爬虫基础知识、爬虫实例、反爬机制包含技术栈、对象和爬虫使用及环境依赖构建

    MovieSpider类: 包含爬虫的逻辑,发送HTTP请求并解析响应。 Movie类: 用于存储电影信息的数据结构。 爬虫使用: 实例化MovieSpider对象。 调用MovieSpider对象的爬取方法开始爬取热门电影信息。 将爬取到的数据保存...

    python零基础爬虫技术详解

    比如你想获得淘宝某类商品的价格,你可以编写爬虫自动搜 索某类商品,然后获取信息,得到自己想要的结果,每天定时爬一下自己就可以决 定在什么时候低价的时候买下心仪的商品了。或者说自己想收集某类信息集合成自 ...

    python爬虫视频教程案例百度网盘链接.docx

    最新版的python爬虫知识,其中还介绍了Android开发的基础知识。 目录: 网络协议&爬虫简介;爬虫请求模块;正则表达式;xpath;Beautiful Soup库;selenium;多线程;Scrapy框架;CrawSpider使用和settings文件讲解...

    爬虫开发初学者入门简单讲解的教程.docx

    二、爬虫基础概念 1. 爬虫定义:爬虫,又称网络爬虫或网络蜘蛛,是一种按照一定的规则,自动地抓取互联网信息的程序或者脚本。它能够模拟人类的浏览行为,自动访问网页并抓取其中的内容。 2. 爬虫分类:根据应用场景...

    Python基础网络爬虫示例

    还可以将爬虫代码封装成函数或类,方便重复使用和扩展。 总之,该代码是一个简单而实用的Python网络爬虫示例,可以用于采集目标网站上的数据,为进一步的数据分析和利用打下基础,也可以为相关领域的研究和应用提供...

    网络爬虫一种搜索引擎

     抓取目标的描述和定义是决定网页分析算法与URL搜索策略如何制订的基础。而网页分析算法和候选URL排序算法是决定搜索引擎所提供的服务形式和爬虫网页抓取行为的关键所在。这两个部分的算法又是紧密相关的。  2 ...

    C++网络爬虫项目

    WEBCRAWLER 网络爬虫实训项目 1 WEBCRAWLER 网 络 爬 虫 实 训 项 目 文档版本: 1.0.0.1 ... student目录中的源代码是不完整的,部分类或者函数的实现只给出了基 本框架,但代码中的注释和teacher目录下对应的部分...

    史上最详细Python爬虫基础教学(一)

    目录一、前言二、爬虫简介2.1 什么是爬虫2.2 基本的爬虫流程2.3 爬虫的分类2.4 robots协议三、网站基础3.1 HTTP和HTTPS3.2 URL3.3 请求和响应3.4 网页基础 一、前言 首先,我也是个爬虫小白,也在努力的学习中,当然...

    C#WPF爬虫示例程序

    爬虫基础知识 如何绕开反爬虫机制 网页抓取原理 使用套接字来获取网页源码 法律与道德约束 爬虫协议 爬虫协议介绍,以及它的语法规则; C#如何获取网站的爬虫协议; C#中如何解析爬虫协议; 法律相关 抓取网页 ...

    52讲轻松搞定网络爬虫.txt

    专栏从基础知识点讲起,通过样例代码,帮你搞懂基本用法和原理。每个知识点会结合实际案例,手把手带你进行爬虫实操,加深你对爬虫技术的理解。 学了这个专栏之后,你能轻松过掌握当下优秀爬虫所用到的必备技术,...

    爬虫原爬虫原理简介.pptx

    一、爬虫网络基础 1.IP地址(身份证) 2.域名(名字) 3.DNS域名解析系统 4.HTTP协议 5.URL 二、爬虫示例 1.爬虫产生背景与爬虫分类 2.爬虫在浏览器搜索栏输入URL后的过程 3.request库介绍 4.代码举例 5.网课及书籍...

    论文研究-基于爬虫和网站分类的主题信息源发现方法.pdf

    以该模型为基础,通过类中心向量法与SVM相结合对网站主题进行分类。提出一种能尽量少爬取网页的网络搜索策略,在发现站外链接的同时爬取最能代表网站主题的页面。将该主题信息源发现方法应用于林业商务信息源,通过...

    python爬虫的基本教程.txt

    二、学习Python基础知识 变量和数据类型:了解Python中的基本数据类型,如字符串、整数、列表等。 控制流语句:掌握条件语句(if/else)、循环语句(for/while)等。 函数和模块:学习如何定义和使用函数,以及...

    python爬虫基础入门 — python爬虫requests库使用操作全解

    文章目录一.HTTP协议1....方法的解析2.方法的使用a. get方法使用b. head方法的使用c. post方法的使用3. requests库的异常处理四.爬取网页的通用代码框架五.requests库爬虫实例1....【python爬虫基础入门】系列是对p

    零基础写python爬虫之爬虫的定义及URL构成

    一、网络爬虫的定义 网络爬虫,即Web Spider,是一个很形象的名字。 把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛。 网络蜘蛛是通过网页的链接地址来寻找网页的。 从网站某一个页面(通常是首页...

    爬虫理论剖析到实战演练视频课程上.rar

    3-1HTTP以及Https协议基础详解ev4.mp4 3-2HTTP的request请求详解ev4mp4 3-3HTTP请求python库实战ev4mp4 34HTTP的response响应以及python库ev4.mp4 4-1 wireshark安装与使用教程ev4mp4 4-2 Fiddler安装与使用教程ev4....

    Python程序设计:多线程爬虫.pptx

    多线程爬虫有道翻译 知识点:多线程爬虫的编写 进程与线程概念 进程 操作系统像是一个奇怪的工厂,因为工人人数有限,每次只能支持一个车间开工。开工运转的车间就是进程,它...爬虫技术基础 进程与线程 编写多线程爬虫

Global site tag (gtag.js) - Google Analytics