Java 网络爬虫
了解网络爬虫
什么是网络爬虫
- 在大数据时代,信息的采集是重要的工作,而互联网中数据是海量的,如果单纯靠人力进行信息采集,低效繁琐,搜集成本会提高。如何自动高效地获取互联网中的信息并为我们所用,爬虫技术就是解决这些问题而生的
- 网络爬虫(Web crawler),可以自动地在互联网中进行数据信息的采集整理。按照一定的规则,自动地抓取万维网信息的程序或者脚 本,可以自动采集能够访问到的页面内容,以获取或更新这些网站的内容和检索方式
- 从功能上来讲,爬虫分为数据采集,处理,储存。爬虫初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新 的URL放入队列,直到满足系统停止条件
网络爬虫作用
- 可以实现搜索引擎
- 大数据时代,可以让我们获取更多的数据源。
- 快速填充测试和运营数据
- 为人工智能提供训练数据集
网络爬虫常用的技术
底层实现 HttpClient + Jsoup
- HttpClient 提供高效的、最新的、功能 丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,Cactus 和 HTMLUnit 都使用了 HttpClient
- jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本。它提供API,通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据
开源框架 Webmagic
-
webmagic是Java爬虫框架,简化爬虫的开发流程,专注于逻辑功能的开发。webmagic的核心非常简单,但是覆盖爬虫的整个流程,也是很好的学习爬虫开发的材料
webmagic主要特色
- 模块化设计,扩展性强
- 核心简单但是涵盖爬虫全部流程,灵活强大
- 提供丰富的抽取页面API
- 无配置,可通过POJO+注解形式实现爬虫
- 支持多线程
- 支持分布式
- 支持爬取js动态渲染的页面
- 无框架依赖,可以灵活的嵌入到项目中去
爬虫框架Webmagic
架构解析
- WebMagic的目标是尽量的模块化,并体现爬虫的功能特点。提供简单、灵活的API,在基本不改变开发模式的情况下,编写一个爬虫
- WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline组件,并由Spider组织起来。四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心、
四大组件
Downloader
- Downloader负责下载页面,以便后续处理。WebMagic默认使用了ApacheHttpClient作为下载工具
PageProcessor
- PageProcessor解析页面,抽取信息,发现新的链接。WebMagic使用Jsoup 作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup
- 这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分
Scheduler
- Scheduler负责管理待抓取的URL,去重的工作。WebMagic默认提供JDK内存队列管理URL,用集合进行去重。支持使用Redis进行分布式管理
Pipeline
- Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案
PageProcessor
爬取页面全部内容
创建工程,引入依赖
-
<dependency> <groupId>us.codecraft</groupId> <artifactId>webmagic‐core</artifactId> <version>0.7.3</version> </dependency> <dependency> <groupId>us.codecraft</groupId> <artifactId>webmagic‐extension</artifactId> <version>0.7.3</version> </dependency>
编写类实现网页内容的爬取
-
import us.codecraft.webmagic.Page; import us.codecraft.webmagic.Site; import us.codecraft.webmagic.Spider; import us.codecraft.webmagic.processor.PageProcessor; /** * 爬取类 */ 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的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来 进行设置
-
Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽 取、结果保存等
-
Site用于定义站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置
爬取指定内容(XPath)
- XPath,即为XML路径语言(XMLPathLanguage),用来确定XML文档中某部分位置的语言。XPath 使用路径表达式来选取 XML 文档中的节点或节点集。路径表达式和常规的电脑文件系统中看到的表达式相似
通过指定xpath来抓取网页的部分内容
-
System.out.println(page.getHtml().xpath("//* [@id=\"nav\"]/div/div/ul/li[5]/a").toString());
-
以上代码的含义:id为nav的节点下的div节点下的div节点下的ul下的第5个li节点下的a节点
添加目标地址
-
通过添加目标地址,从种子页面爬取到更多的页面
-
public void process(Page page) { page.addTargetRequests( //将当前页面里的所有链接都添加到目标页面中 page.getHtml().links().all()); System.out.println(page.getHtml() .xpath("//* [@id=\"nav\"]/div/div/ul/li[5]/a") .toString()); }
-
运行后发现好多地址都出现在控制台
-
目标地址正则匹配
-
提取博客的文章详细页内容,并提取标题
-
import us.codecraft.webmagic.Page; import us.codecraft.webmagic.Site; import us.codecraft.webmagic.Spider; import us.codecraft.webmagic.processor.PageProcessor; /** * 爬取类 */ public class MyProcessor implements PageProcessor { public void process(Page page) { //page.addTargetRequests( page.getHtml().links().all() ); //将当前页 面里的所有链接都添加到目标页面中 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[1]/h1/text()") .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(); } }
ConsolePipeline 控制台输出
-
/** * 爬取类 */ public class MyProcessor implements PageProcessor { public void process(Page page) { //page.addTargetRequests( page.getHtml().links().all() ); //将当前页面里的所有链接都添加到目标页面中 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[1]/h1/text()") .toString()); page.putField("title",page.getHtml() .xpath("//* [@id=\"mainBox\"]/main/div[1]/div[1]/h1/text()") .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") .addPipeline(new ConsolePipeline()) .run(); } }
FilePipeline 文件保存
-
public static void main(String[] args) { Spider.create( new MyProcessor() ) .addUrl("https://blog.csdn.net") .addPipeline(new ConsolePipeline()) .addPipeline(new FilePipeline("e:/data")) //以文件方式保存 .run(); }
JsonFilePipeline方式保存
-
public static void main(String[] args) { Spider.create( new MyProcessor() ) .addUrl("https://blog.csdn.net") .addPipeline(new ConsolePipeline()) .addPipeline(new FilePipeline("e:/data")) .addPipeline(new JsonFilePipeline("e:/json")) // 以json方式保 存 .run(); }
Scheduler
- 刚才完成的功能,每次运行可能会爬取重复的页面,没有任何意义的。Scheduler(URL管理) 最基本的功能是实现对已经爬取的URL进行标示。可以实现URL的增量去重
scheduler主要有三种实现方式:
- 内存队列 QueueScheduler
- 文件队列 FileCacheQueueScheduler
- Redis队列 RedisScheduler
内存队列
-
public static void main(String[] args) { Spider.create( new MyProcessor() ) .addUrl("https://blog.csdn.net") .setScheduler(new QueueScheduler()) .run(); }
文件队列
-
使用文件保存抓取URL,可以在关闭程序并下次启动时,从之前抓取到的URL继续抓取
- 创建文件夹E:\scheduler
- 修改代码
-
public static void main(String[] args) { Spider.create( new MyProcessor() ) .addUrl("https://blog.csdn.net") //.setScheduler(new QueueScheduler()) //设置内存队列 .setScheduler(new FileCacheQueueScheduler("E:\\scheduler")) //设置文件队列 .run(); }
Redis队列
-
用Redis保存抓取队列,可进行多台机器同时合作抓取
- 运行redis服务端
- 修改代码
-
public static void main(String[] args) { Spider.create( new MyProcessor() ) .addUrl("https://blog.csdn.net") //.setScheduler(new QueueScheduler()) //设置内存队列 //.setScheduler(new FileCacheQueueScheduler("E:\\scheduler")) //设置文件队列 .setScheduler(new RedisScheduler("127.0.0.1")) //设置Redis队列 .run(); }
韩清宗