网络爬虫(Web crawler)也叫做网络机器人 可以代替人 自动地在互联网中进行数据信息的采集和整理
是一种按照一定的规则 自动地抓取万维网信息的程序或者脚本
可以自动采集所有其能够访问到的页面的内容 以获取相关数据
在大数据时代 信息的采集是一项重要的工作
而互联网中的数据是海量的 如果单纯靠人力进行信息采集 不仅低效繁琐 搜集的成本也会提高
爬虫技术就是为了解决如何高效地获取互联网中重要的信息
从功能上讲 爬虫分为数据采集 处理 储存三个部分
爬虫从一个或若干个初始网页的URL开始 获取初始网页的URL
在抓取网页的过程中 不断从当前页面上抽取新的URL放入队列 直到满足系统的一定停止条件最后结束爬取
其实爬虫的话在Python中应用的会更加广泛 好歹是脚本语言
上学期上课也学到Python爬虫 但长久没使用 好多都忘了 (笑
扯远了
在Java中 若要实现爬虫 可用HttpClient和Jsoup 这两个是一对
其它Java的爬虫框架大部分都是底层以此为基础进行封装的 例如WebMagic
在浏览器中 一直以来都是使用HTTP协议访问互联网的网页
网络爬虫需要编写程序 同样使用HTTP协议访问网页
那么 可以使用Java的HTTP协议客户端HttpClient这个技术来实现抓取网页数据
引入依赖:
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.2version>
dependency>
在抓取到页面后 页面是一大段的HTML代码 因此还需要对页面进行解析
可以使用字符串处理工具解析页面 也可使用正则表达式 但是这些方法都会带来很大的开发成本
因此需要使用一款专门解析HTML页面的技术 由此 就有了Jsoup
Jsoup是一款Java的HTML解析器 可直接解析某个URL地址或HTML文本内容
提供了一套非常省力的API 可通过DOM CSS以及类似于JQuery的操作方法来取出数据和操作数据
主要功能:
引入依赖:
<dependency>
<groupId>org.jsoupgroupId>
<artifactId>jsoupartifactId>
<version>1.10.2version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.7version>
dependency>
★虽然 使用Jsoup也可以替代HttpClient来直接发起请求解析数据 但往往不会这么用
因为在实际的爬虫开发过程中 需要使用到多线程 连接池 代理等方式
Jsoup对这些的支持并不是很好 因此一般仅仅把Jsoup作为Html解析工具使用
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet=new HttpGet("http://www.baidu.com");
CloseableHttpResponse response = httpClient.execute(httpGet);
// 判断状态码是否为200
if (response.getStatusLine().getStatusCode()==200)
{
HttpEntity httpEntity = response.getEntity();
String data=EntityUtils.toString(httpEntity,"utf8");
System.out.println(data);
}
首先 当然是创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
接下来 就是发起请求了 分为Get请求和Post请求
Get
请求创建HttpGet对象:不带参数
// 创建HttpGet对象 设置url地址
HttpGet httpGet=new HttpGet("http://www.baidu.com");
带单个参数
// 创建URIBuilder
URIBuilder uriBuilder=new URIBuilder("http://zp.zjitc.net/info/notice/detail");
// 设置参数
uriBuilder.setParameter("id","c5bab1524b974f9fbc7c456a2fa79844");
带多个参数
// 创建URIBuilder
URIBuilder uriBuilder=new URIBuilder("http://www.zjitc.net/newssjgy.jsp");
// 设置参数
uriBuilder.setParameter("urltype","tree.TreeTempUrl").setParameter("wbtreeid","1209");
// 创建HttpGet对象 设置url地址
HttpGet httpGet=new HttpGet(uriBuilder.build());
Post
请求创建HttpGet对象:不带参数
创建HttpPost对象 设置url地址
HttpPost httpPost=new HttpPost("http://www.zjitc.net/");
带参数
// 创建HttpPost对象 设置url地址
HttpPost httpPost=new HttpPost("http://yun.itheima.com/search");
// 声明List集合 封装表单中的参数
List<NameValuePair> params=new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("so","Java"));
params.add(new BasicNameValuePair("type","course"));
params.add(new BasicNameValuePair("realhash","6666cd76f96956469e7be39d750cc7d9_60eddf1e1638d1929ceae963fa334549"));
// 创建表单的Entity实体对象 第一个参数为封装好的表单数据 第二个参数为编码
UrlEncodedFormEntity urlEncodedFormEntity=new UrlEncodedFormEntity(params,"utf8");
// 设置表单的Entity实体对象到httpPost对象中
httpPost.setEntity(urlEncodedFormEntity);
最后 处理返回数据
CloseableHttpResponse response=null;
try
{
// 使用HttpClient发起请求 获取response
response = httpClient.execute(httpGet);
// 解析响应
if (response.getStatusLine().getStatusCode()==200)
{
HttpEntity entity = response.getEntity();
String data = EntityUtils.toString(entity,"utf8");
System.out.println(data.length());
}
}
catch
(IOException e){
e.printStackTrace();
}
finally{
// 关闭response
try{response.close();} catch (IOException e) {e.printStackTrace();}
// 关闭httpClient
try { httpClient.close();} catch (IOException e) {e.printStackTrace();}
}
每次请求都需要创建HttpClient 会产生频繁创建和销毁的问题 造成资源浪费和影响性能
可用连接池来解决此问题
①、创建连接池管理器
PoolingHttpClientConnectionManager connectionManager=new PoolingHttpClientConnectionManager();
②、配置参数
// 设置最大连接数
connectionManager.setMaxTotal(100);
// 设置每个主机(Host)的最大连接数
connectionManager.setDefaultMaxPerRoute(10);
③、使用连接池管理器发起请求
语法:doGet(connectionManager);
private static void doGet(PoolingHttpClientConnectionManager connectionManager)
{
// ★并非每次创建新的HttpClient对象 而是从连接池中获取
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
HttpGet httpGet=new HttpGet("http://www.baidu.com");
CloseableHttpResponse response=null;
try {
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode()==200){
String data = EntityUtils.toString(response.getEntity(), "utf8");
System.out.println(data.length());
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (response!=null){
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 当使用连接池之后 不能手动关闭HttpClient 因为是由连接池管理的
}
}
①、配置请求信息
RequestConfig config=RequestConfig.custom().setConnectTimeout(1000)//创建连接的最长时间(单位毫秒)
.setConnectionRequestTimeout(500)//获取连接的最长时间(单位毫秒)
.setSocketTimeout(10*1000)//设置数据传输的最长时间(单位毫秒)
.build();
②、给请求设置请求信息
httpGet.setConfig(config);
Jsoup处理的步骤是 先解析 然后获取指定的元素
URL
中解析调用Jsoup的parse方法:parse(new URL,超时时长)
Document document = Jsoup.parse(new URL("http://www.baidu.com"), 1000);
使用标签选择器获取<title>标签中的内容
String title=document.getElementsByTag("title").first().text();
System.out.println(title);
字符串
中解析使用工具类的方法读取文件 获取字符串:readFileToString(new File,编码格式)
String content=FileUtils.readFileToString(new File("F:/IdeaProjects/crawler-first/src/JsoupTest.html"),"utf8");
Document document = Jsoup.parse(content);
String title=document.getElementsByTag("title").text();
System.out.println(title);
文件
中解析调用Jsoup的parse方法:parse(new File,编码格式)
Document document = Jsoup.parse(new File("F:/IdeaProjects/crawler-first/src/JsoupTest.html"), "utf8");
String title = document.getElementsByTag("title").text();
System.out.println(title);
dom
方式获取元素首先 解析网页数据
(这里举个栗子从本地的文件中解析
Document document = Jsoup.parse(new File("F:/IdeaProjects/crawler-first/src/JsoupTest.html"), "utf8");
然后 就是获取元素了
// 根据【id】获取元素 getElementById()
String data = document.getElementById("myspan1").text();
// 根据【标签】获取元素 getElementsByTag()
// 获取第一个
String data = document.getElementsByTag("span").first().text();
// 获取第二个
String data = document.getElementsByTag("span").eq(1).text();
// 根据【class类名】获取元素 getElementsByClass()
// 获取第一个
String data = document.getElementsByClass("ms").first().text();
// 获取第二个
String data = document.getElementsByClass("ms").eq(1).text();
// 根据【属性名】获取元素 getElementsByAttribute()
// 获取第一个
String data = document.getElementsByClass("ms").first().text();
// 获取第二个
String data = document.getElementsByClass("ms").eq(1).text();
// 根据【属性和属性值】获取元素
String data = document.getElementsByAttributeValue("test", "aaa").text();
获取完了 最后 是对元素进行处理
System.out.println(data);
如此 即为dom方式获取元素的一个基本流程
Selector选择器
方式获取元素同样的 首先 是解析网页数据
(这里举个栗子从本地的文件中解析
Document document = Jsoup.parse(new File("F:/IdeaProjects/crawler-first/src/JsoupTest.html"), "utf8");
然后获取元素
// 根据id获取元素 #name
Elements elements = document.select("#myspan1");
// 根据标签获取元素 name
Elements elements = document.select("span");
// 根据class获取元素 .name
Elements elements = document.select(".ms");
// 根据属性获取元素 [name]
Elements elements = document.select("[abc]");
// 根据属性和属性值获取元素 [name=value]
Elements elements = document.select("[test=aaa]");
获取完了 最后 是对元素进行处理
for (Element e:elements)
{
System.out.println(e.text());
}
如此 即为选择器方式获取元素的一个基本流程
元素和id:el#id
元素和class:el.class
元素和属性名:el[attr]
任意组合:例如el[attr].class
所有子元素:parent child
指定直接子元素:parent>child
所有直接子元素:parent>*
使用的时候 同样也是使用select()方法
document.select()