本文共 8333 字,大约阅读时间需要 27 分钟。
什么是网络爬虫
从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分。爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。
网络爬虫常用的技术
底层实现 HttpClient + Jsoup
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。更多信息请关注http://hc.apache.org/。
Jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。 HttpClient 是用来下载网页源代码的,Jsoup 用来从网页源代码中解析需要的内容。Webmagic框架
webmagic是一个开源的Java爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发。webmagic 的核心非常简单,但是覆盖爬虫的整个流程,也是很好的学习爬虫开发的材料。
爬虫框架 Webmagic 介绍
架构的四大组件
WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。
简单解释一下:Downloader 负责将网页的源代码爬取下来,PageProcesser 指定爬取的规则(即指定爬取什么内容),然后交由 Pipeline 进行将爬取的内容存储,同时将 网址 交给 Scheduler 管理,防止 Downloader 再次爬取到这个页面,防止爬取重复内容。-
Downloader
Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了 ApacheHttpClient 作为下载工具。 -
PageProcessor
PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用 Jsoup 作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。 -
Scheduler
Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic 默认提供了 JDK 的内存队列来管理 URL,并用集合来进行去重。也支持使用 Redis 进行分布式管理。 -
Pipeline
Pipeline 负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic 默认提供了“输出到控制台”和“保存到文件”两种结果处理方案,也可以自定义 Pipeline。
PageProcessor
爬取页面全部内容
需求:编写爬虫程序,爬取csdn中博客的内容 https://blog.csdn.net/
1)引入依赖us.codecraft webmagic-core 0.7.3 us.codecraft webmagic-extension 0.7.3
2)编写类实现网页内容的爬取
public class MyProcessor implements PageProcessor { public void process(Page page) { // 将爬取的网页源代码输出到控制台 System.out.println(page.getHtml().toString()); } public Site getSite() { return Site.me().setSleepTime(100).setRetryTimes(3); } public static void main(String[] args) { Spider.create(new MyProcessor()) .addUrl("https://blog.csdn.net/") .run(); }}
Spider是爬虫启动的入口。在启动爬虫之前,我们需要使用一个 PageProcessor 创建一个 Spider 对象,然后使用 run() 进行启动。
Spider 的一些方法: 同时 Spider 的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。 Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page 是 WebMagic 抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。 Site 用于定义站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。 Site 的一些方法:爬取指定内容(Xpath)
如果我们想爬取网页中部分的内容,需要指定 xpath。 XPath,即为XML路径语言(XMLPathLanguage),它是一种用来确定XML文档中某部分位置的语言。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
我们通过指定xpath来抓取网页的部分内容:System.out.println(page.getHtml().xpath("//*[@id=\"mainBox\"]/main/div[1]/div/div/div[1]/h1").toString());
获取 xpath 的简单方法:打开网页,按 F12
添加目标地址
我们可以通过添加目标地址,从种子页面爬取到更多的页面:
目标地址正则匹配
有时候我们需要将当前页面里 只有符合要求的链接 才添加到目标页中,这时候我们就可以利用正则表达式对 链接 进行筛选:
// 添加目标地址,从一个页面爬到另一个页面page.addTargetRequests(page.getHtml().links().regex("https://blog.csdn.net/[a-z 0-9 _]+/article/details/[0-9]{8}").all());System.out.println(page.getHtml().xpath("//*[@id=\"mainBox\"]/main/div[1]/div/div/div[1]/h1").toString());
PipeLine
ConsolePipeline 控制台输出
将处理结果输出到控制台FilePipeline 文件保存
以 json 方式保存
自定义 Pipeline
一般我们需要将爬取的数据放到数据库中,这时候我们就可以自定义 Pipeline
创建一个类,实现 Pipeline 接口: 修改 main 方法:Scheduler
我们刚才完成的功能,每次运行可能会爬取重复的页面,这样做是没有任何意义的。Scheduler(URL管理) 最基本的功能是实现对已经爬取的 URL 进行标示。可以实现 URL 的增量去重。
目前scheduler主要有三种实现方式:- 内存队列 QueueScheduler:地址存于内存中,适合于单次爬取
- 文件队列 FileCacheQueueScheduler:地址存于本机文件中,适合于单机爬取
- Redis队列 RedisScheduler:地址存于 redis 中,适合于分布式爬取
内存队列
使用 setScheduler来设置 Scheduler:
文件队列
使用文件保存抓取 URL,可以在关闭程序并下次启动时,从之前抓取到的 URL 继续抓取
不过使用这种方式首先要保证 文件目录 存在,所以先创建 文件的保存目录: 运行后文件夹 E:\scheduler 会产生两个文件 blog.csdn.net.urls.txt 和 blog.csdn.net.cursor.txtRedis 队列
使用Redis保存抓取队列,可进行多台机器同时合作抓取
首先运行 redis 服务端实例(爬取 csdn 博客的用户昵称 和 头像)
1)引入依赖
us.codecraft webmagic-core 0.7.3 org.slf4j slf4j-log4j12 us.codecraft webmagic-extension 0.7.3 org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java com.tensquare tensquare_common 1.0-SNAPSHOT
2)创建配置文件
server: port: 9014spring: application: name: tensquare-article-crawler #指定服务名 datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.227.129:3306/tensquare_article?characterEncoding=UTF8 username: root password: 123456 jpa: database: MySQL show-sql: true redis: host: 192.168.227.129
3)创建启动类
@SpringBootApplicationpublic class ArticleCrawlerApplication { public static void main(String[] args) { SpringApplication.run(ArticleCrawlerApplication.class); } @Value("${spring.redis.host}") private String redis_host; @Bean public IdWorker idWorker(){ return new IdWorker(1,1); } @Bean public RedisScheduler redisScheduler(){ return new RedisScheduler(redis_host); }}
4)创建爬取类
打开网页,按 F12,找到头像和昵称 如上图:头像是标签里的一个属性,如何获取呢?@Componentpublic class UserProcessor implements PageProcessor { @Override public void process(Page page) { // 添加目标地址,从一个页面爬到另一个页面 page.addTargetRequests(page.getHtml().links().regex("https://blog.csdn.net/[a-z 0-9 _]+/article/details/[0-9]{8}").all()); // 添加字段,代码结构化,可多次使用 String nickName = page.getHtml().xpath("//*[@id=\"uid\"]/text()").get(); String image = page.getHtml().xpath("//*[@id=\"asideProfile\"]/div[1]/div[1]/a/img[1]/@src").get(); if (nickName != null && image != null){ page.putField("nickName",nickName); page.putField("image",image); } else { // 不执行后面的步骤 page.setSkip(true); } } @Override public Site getSite() { return Site.me().setSleepTime(100).setRetryTimes(3); }}
只需要在相应的 xpath 路径后加 /@属性名称
package util;import java.io.*;import java.net.URL;import java.net.URLConnection;/** * 下载工具类 */public class DownloadUtil { public static void download(String urlStr,String filename,String savePath) throws IOException { URL url = new URL(urlStr); //打开url连接 URLConnection connection = url.openConnection(); //请求超时时间 connection.setConnectTimeout(5000); //输入流 InputStream in = connection.getInputStream(); //缓冲数据 byte [] bytes = new byte[1024]; //数据长度 int len; //文件 File file = new File(savePath); if(!file.exists()) file.mkdirs(); OutputStream out = new FileOutputStream(file.getPath()+"\\"+filename); //先读到bytes中 while ((len=in.read(bytes))!=-1){ //再从bytes中写入文件 out.write(bytes,0,len); } //关闭IO out.close(); in.close(); }}
5)创建入库类
@Componentpublic class UserPipeline implements Pipeline { @Autowired private IdWorker idWorker; @Autowired private UserDao userDao; @Override public void process(ResultItems resultItems, Task task) { String nickName = resultItems.get("nickName"); String image = resultItems.get("image"); String imageName = image.substring(image.lastIndexOf("/") + 1) + ".jpg"; User user = new User(); user.setId(idWorker.nextId()+""); user.setAvatar(imageName); user.setNickname(nickName); // 将用户存入数据库 userDao.save(user); // 将图片下载下载 try { DownloadUtil.download(image,imageName,"E:/tensquare/userimage"); } catch (IOException e) { e.printStackTrace(); } }}
6)创建任务类
@Componentpublic class UserTask { @Autowired private UserProcessor userProcessor; @Autowired private UserPipeline userPipeline; @Autowired private RedisScheduler redisScheduler; @Scheduled(cron = "0 57 22 * * ?") public void UserTask(){ Spider spider = Spider.create(userProcessor); spider.addUrl("https://blog.csdn.net/"); spider.addPipeline(userPipeline); spider.setScheduler(redisScheduler); spider.start(); }}
7)启动类添加注解
8)运行该模块,到指定的时间就会自动爬取数据了转载地址:https://blog.csdn.net/zwj_jyzl/article/details/104469011 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!