Java爬虫开发
操作要点
正则表达式
Java网络通信:URL
IO流
Map—HashMap
字符串操作
异常处理
项目已上传本人码云(gitee)传送门
如果这篇博客对你有一点点小帮助,希望您能给我来波一键三连;
前言
python优点:
1.各种爬虫框架,方便高效的下载网页;
2.多线程、进程模型成熟稳定,爬虫是一个典型的多任务处理场景,请求页面时会有较长的延迟,总体来说更多的是等待。多线程或进程会更优化程序效率,提升整个系统下载和分析能力。
3.gae 的支持,当初写爬虫的时候刚刚有 gae,而且只支持 python ,利用 gae 创建的爬虫几乎免费,最多的时候我有近千个应用实例在工作。
java 和 c++ :
相对脚本语言比较麻烦,所以放弃。总之,如果开发一个小规模的爬虫脚本语言是个各方面比较有优势的语言。如果要开发一个复杂的爬虫系统可能 java 是个增加选项, c++ 我感觉写个模块之类的更加适合。对于一个爬虫系统来说,下载和内文解析只是基本的两个功能。真正好的系统还包括完善的任务调度、监控、存储、页面数据保存和更新逻辑、排重等等。爬虫是一个耗费带宽的应用,好的设计会节约大量的带宽和服务器资源,并且好坏差距很大。
注意(爬虫爬取数据,必须是合法的数据,)
爬虫的类型
通用型的爬虫: 宽度遍历
针对网页进行无差别抓取,例如Google搜索引擎和百度搜索引擎等等;(他们不生产网页内容,其内容都是类似的爬虫爬取来的)难点:页面下载和链接管理上,如果你要高效的抓取更多界面,就必须进行更快的下载;同时随着链接数量的增多,需要考虑如果对大规模的链接进行去重和调度,就成了一个很大的问题通用型爬虫框架:Nutch, Heritrix
垂直型的爬虫
关注内容与准确还有效率,仅仅抓取到有效有用的数据,并且在爬虫抓取之前,就能够对数据进行简单的处理:如:提取标题,内容,时间等;例如:股票财经,其涨跌幅度;难点:如何高效的定制一个爬虫,可以精确的抽取网页内容,并保存成结构化的数据。框架 :webmagic
本次代码实践所用技术
模拟浏览器:HttpClient html解析:jsoup
1.在Module建立一个maven工程
(因为我提前建立了一个大的maven工程项目,本次为了节省时间,就不重新创建一个maven工程,直接利用现有的)
建立一个简单的maven,不选用现成的骨架
点击module
选择好你需要的jdk版本,点击next
设置好工程名称,和域名倒写。点击finish
点击file,点击Project structure,再点击module
将 java设为Source 将resources 设为Resource
引入你需要的maven依赖
</dependencies>
建立好包
本次爬取网站:昵图网传送门 以一个图片网站作为本次爬取对象
在网站中审查元素:确定图片的html代码结构
crtl+(a标签里面的http://www.nipic.com/topic/show_27400_1.html)
分析html代码,观察里面的结点结构特点。建立一个URL的值,把里面所有的html的保存起来。
然后来处理里面的每一个html,再来根据里面的每一个html,来取这个界面中间的图片。
需要分开处理,img图片和上面的a标签,来爬取上面的图片,将图片保存起来。
创建一个UrlPool类,用来存放html。
代码全在我的gitee:传送门
对你有用的话,可以点个收藏关注;给我的gitee点一个star;谢谢;
package com;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** Created with IntelliJ IDEA.** @Auther: 孙伟俊* @Date: 2021/03/14/22:37* @Description:*/
//用来存放html
public class UrlPool {public static void main(String[] args) {getURL("http://www.nipic.com/");}private static void getURL(String baseUrl) {
// 创建一个map集合,传入要爬取的页面的url,并定义一个Boole型值,用于判断当前传入的url是否被遍历过;Map<String,Boolean> oldMap=new LinkedHashMap<String,Boolean>();
// 如果采集下来的路径是相对路径的话,要将相对路径进行String oldLinkHost="";
// 使用正则表达式,对路径进行占位符替换Pattern p=Pattern.compile("(http?://)?[^/\\s]*");Matcher m=p.matcher(baseUrl);if(m.find()){oldLinkHost=m.group();
// 取到正则之后的值//因为baseUrl,也是需要去爬取的,所以要将baseUrl的值,放入到这个集合中,因为未采集,所以定值为falseoldMap.put(baseUrl,false);oldMap=crawlLinks(oldLinkHost,oldMap);//遍历这个url,告诉他传入的值经过正则处理过,如果取到一个相对路径则进行拼接//返回的也是一个oldMap;(是个引用类型)for (Map.Entry<String,Boolean> mapping:oldMap.entrySet()){System.out.println("链接"+ mapping.getKey());}}}/*** @Description: 实现递归 对传入的url进行处理* @Param: [oldLinkList, oldMap]* @return: void* @Author: 孙伟俊* @Date: 2021/3/14*/private static Map<String,Boolean> crawlLinks(String oldLinkHost, Map<String, Boolean> oldMap) {Map<String,Boolean> newMap=new LinkedHashMap<String,Boolean>();//创建一个新的容器
// 如果url被遍历过,就不做遍历,String oldLink="";for (Map.Entry<String,Boolean> mapping: oldMap.entrySet()){System.out.println("链接"+ mapping.getKey()+"----check"+mapping.getValue());if(!mapping.getValue()){}oldLink=mapping.getKey();try{URL url=new URL(oldLink);HttpURLConnection connection=(HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");//如果拿到的响应值为responseCode=200if(connection.getResponseCode()==200){//读取所有的a标签 使用BufferedReader 缓冲区来接收,这样读取速度快一点,BufferedReader reader=new BufferedReader(new InputStreamReader(connection.getInputStream()));//定义 a标签的正则表达式Pattern p = Pattern.compile("a.*?href=[\\\"']?((https?://)?/?[^\"']+)[\\\"']?.*?>(.+)</a>");Matcher matcher=null;String line="";//开始流操作while ((line=reader.readLine())!=null){matcher=p.matcher(line);if(matcher.find()){String newLink=matcher.group(1).trim();//判断newLink是否为合法的,如果newLink不以http开头,此时的newLink就是一个相对路径if(!newLink.startsWith("http")){if (newLink.startsWith("/")){newLink=oldLinkHost+newLink;}else {newLink=oldLinkHost+"/"+newLink;}}if(newLink.endsWith("/")){newLink=newLink.substring(0,newLink.length()-1);}//判断url有没有重复if(!oldMap.containsKey(newLink)&&!newMap.containsKey(newLink)&&!newLink.startsWith(oldLinkHost)){newMap.put(newLink,false);}}}}}catch (Exception e){}finally {}oldMap.replace(oldLink,false,true);}if(!newMap.isEmpty()){//将新的url集合和oldMap进行合并oldMap.putAll(newMap);oldMap.putAll(crawlLinks( oldLinkHost, oldMap));}return oldMap;}}
运行上面的UrlPool类,得到下面的控制台输出:
为了拿到被爬取的内容图片,对内容解析并持久化,创建ImageCrawl类
这一部分:因为考虑到待会教程太长了,放到下一篇博客中说明;
对内容解析并持久化:放在下一篇博客教程中;
后续我会对本次爬虫中涉及的技术的内容,整理出相关博客:
正则表达式
Java网络通信:URL
IO流
Map—HashMap
字符串操作
异常处理
本人技术有限,如中间出现什么错误,欢迎大家在评论区指出,希望大家多多支持;
点赞+加一键三连