爬虫的基本概念:
什么是爬虫
爬虫的价值
爬虫的分类
通用的爬虫
垂直的爬虫
爬虫的基本原理
爬虫的三大模块:
获取数据
解析数据
保存数据
案例一: 爬取起点中文网的小说案例
案例二: 使用爬虫程序登录某个网站, 获取此用户下的信息
网络爬虫是一个程序, 采用一种特定的解析结构来获取互联网中数据的, 爬虫一般分为三大模块: 获取数据, 解析数据, 保存数据
网络爬虫的价值其实就是数据的价值, 一切皆为数据, 例如: 用户的信息,分析用户的维度, 商品的信息,竞价的网站
指的就是爬虫互联网中所有的信息, 例如: 百度 谷歌
指的爬取某个行业或者某个网站或者某个分类下的信息,这样的爬虫程序, 垂直爬虫 例如: 慢慢网, 笔趣阁
在开发过程中, 大部分开发的都是垂直爬虫,
爬虫基本原理:
1. 确定爬虫的url
2. 发起http请求, 获取数据
1. 原始的jdk的方式: get post
2. httpClient get post
3. 解析获取到数据
1. jsoup
4. 保存数据
1. JDBC
2. DButils
3. JDBCTemplate(Spring)
4. mybatis
获取数据的过程, 其实就是发送一个http请求, 获取其响应的内容
//演示 jdk 的get请求方式
public class JDKget {
public static void main(String[] args) throws Exception {
//1. 创建URL对象
URL url = new URL("http://www.itcast.cn");
//2. 打开一个连接
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
//3. 设置请求方式
urlConnection.setRequestMethod("GET");//此处必须使用大写, 默认是get请求
//4. 获取输入流
InputStream in = urlConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
//5. 获取数据
String len = null;
while((len = bufferedReader.readLine())!=null){
System.out.println(len);
}
//6. 关流
bufferedReader.close();
in.close();
}
}
//演示jdk发送post请求
public class JDKpost {
public static void main(String[] args) throws Exception {
//1. 创建url对象
URL url = new URL("http://www.itcast.cn");
//2. 建立连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//3. 设置请求方式
connection.setRequestMethod("POST");
//如果要有使用jdk的方式发送post请求, 需要设置doOutput为true
connection.setDoOutput(true);
//4. 设置参数
OutputStream out = connection.getOutputStream();
out.write("username=zs&password=123".getBytes());
//5. 获取响应体, 获取输入流
InputStream in = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String len = null;
while((len = bufferedReader.readLine())!=null){
System.out.println(len);
}
//6. 关流
bufferedReader.close();
in.close();
}
}
总结:
实现的步骤:
创建url对象, 指定url路径
打开一个连接, 获取连接对象(HttpURLConnection)
设置请求方式
如果是post, 需要设置两个参数:
设置输出数据, 和 doOutPut设置为true
获取输入流(获取响应体)
读取输入流中的数据
关流
httpclient是一个专为用来做http请求的工具, 是Apache开发
使用步骤:
第一步: 导包
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.4version>
dependency>
//演示 httpclient的get请求
public class HTTPClientGet {
public static void main(String[] args) throws IOException {
//1. 创建httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//2. 创建get请求对象
HttpGet httpGet = new HttpGet("http://www.itcast.cn");
//3. 发送一个请求
CloseableHttpResponse response = httpClient.execute(httpGet);
//4. 获取状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println(statusCode);
if(statusCode==200){
//获取响应体(数据)
String html = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));
System.out.println(html);
}
}
}
//演示 httpclient 发送post请求
public class HTTPClientPost {
public static void main(String[] args) throws Exception {
//1. 创建 httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//2. 创建post请求对象
HttpPost httpPost = new HttpPost("http://www.itcast.cn");
//3. 设置请求参数
List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>();
list.add(new BasicNameValuePair("usernam","zs"));
list.add(new BasicNameValuePair("password","123"));
HttpEntity entity = new UrlEncodedFormEntity(list);
httpPost.setEntity(entity);
//4. 发送数据
CloseableHttpResponse response = httpClient.execute(httpPost);
//5. 获取数据
Header[] headers = response.getHeaders("Date");
System.out.println(headers[0].getValue());
System.out.println(EntityUtils.toString(response.getEntity(),"utf-8"));
}
}
总结:
get请求:
创建httpclient对象:HttpClients.createDefault();
创建请求对象(httpget)
发送请求: httpclient.execute(httpGet);
获取数据:
1.2 获取响应头
1.3 获取响应体
post请求:
1.创建httpclient对象: HttpClients.createDefault();
2.创建请求对象(httpPost)
3.设置参数: httpPost.setEntity(entity)
List list = new ArrayList();
list.add(new BasicNameValuePair("usernam","zs"));
list.add(new BasicNameValuePair("password","123"));
HttpEntity entity = new UrlEncodedFormEntity(list);
4.发送请求:httpclient.execute(httpPost);
5.获取数据:
1.1获取状态码
1.2获取响应头
1.3 获取响应体
解析数据, 其实就是解析HTML文档,js中dom操作就是在解析HTML文档
集合 | 描述 |
---|---|
[all] | 提供对文档中所有 HTML 元素的访问。 |
[anchors] | 返回对文档中所有 Anchor 对象的引用。 |
applets | 返回对文档中所有 Applet 对象的引用。 |
[forms] | 返回对文档中所有 Form 对象引用。 |
[images] | 返回对文档中所有 Image 对象引用。 |
[links] | 返回对文档中所有 Area 和 Link 对象引用。 |
属性 | 描述 |
---|---|
body | 提供对 元素的直接访问。 对于定义了框架集的文档,该属性引用最外层的 。 |
cookie | 设置或返回与当前文档有关的所有 cookie。 |
domain | 返回当前文档的域名。 |
lastModified | 返回文档被最后修改的日期和时间。 |
referrer | 返回载入当前文档的文档的 URL。 |
title | 返回当前文档的标题。 |
URL | 返回当前文档的 URL。 |
方法 | 描述 |
---|---|
close() | 关闭用 document.open() 方法打开的输出流,并显示选定的数据。 |
getElementById() | 返回对拥有指定 id 的第一个对象的引用。 |
getElementsByName() | 返回带有指定名称的对象集合。 |
getElementsByTagName() | 返回带有指定标签名的对象集合。 |
open() | 打开一个流,以收集来自任何 document.write() 或 document.writeln() 方法的输出。 |
write() | 向文档写 HTML 表达式 或 JavaScript 代码。 |
jsoup是一个专门为HTML解析而生的工具,提供了丰富解析方案, 一种使用类似于js中原生dom操作的方案 , 一种是类似于jQuery(css选择器)中选择器的方案
org.jsoup
jsoup
1.10.3
public class JsoupToDocument {
public static void main(String[] args) throws IOException {
//1. 获取document对象:通过url获取document
//Document document = Jsoup.connect("http://www.itcast.cn").get();
//1.1. 获取网页的标题
//String title = document.title();
//System.out.println(title);
//2. 获取document: 通过HTML文档获取
// Document document = Jsoup.parse("\n" +
"\n" +
"\n" +
" \n" +
" 黑马程序员 \n" +
"\n" +
"\n" +
"\n" +
"\n" +
"");
// String title = document.title();
//System.out.println(title);
//3. 通过本地html文件获取document对象
// Document document = Jsoup.parse(new File(""), "utf-8");
//4. 通过html片段获取
// Document document = Jsoup.parseBodyFragment("传智博客");
}
}
总结: 四种
1.通过url路径的方式获取
2.通过HTML文档的形式获取(重点)
3.通过本地HTML文件的形式获取
4.通过HTML片段形式获取
第三步: 解析数据(如何解析document)
选择器 | 例子 | 例子描述 | CSS |
---|---|---|---|
.class | .intro | 选择 class=“intro” 的所有元素。 | 1 |
#id | #firstname | 选择 id=“firstname” 的所有元素。 | 1 |
* | * | 选择所有元素。 | 2 |
element | p | 选择所有 元素。 |
1 |
element,element | div,p | 选择所有
元素和所有
元素。 |
1 |
element element | div p | 选择
元素内部的所有
元素。 |
1 |
element>element | div>p | 选择父元素为
元素的所有
元素。 |
2 |
element+element | div+p | 选择紧接在
元素之后的所有
元素。 |
2 |
[attribute] | [target] | 选择带有 target 属性所有元素。 | 2 |
[attribute=value] | [target=_blank] | 选择 target="_blank" 的所有元素。 | 2 |
[attribute~=value] | [title~=flower] | 选择 title 属性包含单词 “flower” 的所有元素。 | 2 |
[attribute|=value] | [lang|=en] | 选择 lang 属性值以 “en” 开头的所有元素。 | 2 |
:link | a:link | 选择所有未被访问的链接。 | 1 |
:visited | a:visited | 选择所有已被访问的链接。 | 1 |
:active | a:active | 选择活动链接。 | 1 |
:hover | a:hover | 选择鼠标指针位于其上的链接。 | 1 |
:focus | input:focus | 选择获得焦点的 input 元素。 | 2 |
:first-letter | p:first-letter | 选择每个 元素的首字母。 |
1 |
:first-line | p:first-line | 选择每个 元素的首行。 |
1 |
:first-child | p:first-child | 选择属于父元素的第一个子元素的每个 元素。 |
2 |
:before | p:before | 在每个 元素的内容之前插入内容。 |
2 |
:after | p:after | 在每个 元素的内容之后插入内容。 |
2 |
:lang(language) | p:lang(it) | 选择带有以 “it” 开头的 lang 属性值的每个 元素。 |
2 |
element1~element2 | p~ul | 选择前面有 元素的每个
|
3 |
[attribute^=value] | a[src^=“https”] | 选择其 src 属性值以 “https” 开头的每个 元素。 | 3 |
[attribute$=value] | a[src$=".pdf"] | 选择其 src 属性以 “.pdf” 结尾的所有 元素。 | 3 |
[attribute*=value] | a[src*=“abc”] | 选择其 src 属性中包含 “abc” 子串的每个 元素。 | 3 |
:first-of-type | p:first-of-type | 选择属于其父元素的首个 元素的每个 元素。 |
3 |
:last-of-type | p:last-of-type | 选择属于其父元素的最后 元素的每个 元素。 |
3 |
:only-of-type | p:only-of-type | 选择属于其父元素唯一的 元素的每个 元素。 |
3 |
:only-child | p:only-child | 选择属于其父元素的唯一子元素的每个 元素。 |
3 |
:nth-child(n) | p:nth-child(2) | 选择属于其父元素的第二个子元素的每个 元素。 |
3 |
:nth-last-child(n) | p:nth-last-child(2) | 同上,从最后一个子元素开始计数。 | 3 |
:nth-of-type(n) | p:nth-of-type(2) | 选择属于其父元素第二个 元素的每个 元素。 |
3 |
:nth-last-of-type(n) | p:nth-last-of-type(2) | 同上,但是从最后一个子元素开始计数。 | 3 |
:last-child | p:last-child | 选择属于其父元素最后一个子元素每个 元素。 |
3 |
:root | :root | 选择文档的根元素。 | 3 |
:empty | p:empty | 选择没有子元素的每个 元素(包括文本节点)。 |
3 |
:target | #news:target | 选择当前活动的 #news 元素。 | 3 |
:enabled | input:enabled | 选择每个启用的 元素。 | 3 |
:disabled | input:disabled | 选择每个禁用的 元素 | 3 |
:checked | input:checked | 选择每个被选中的 元素。 | 3 |
:not(selector) | :not§ | 选择非 元素的每个元素。 |
3 |
::selection | ::selection | 选择被用户选取的元素部分。 | 3 |
public class JsoupToParse {
@Test
public void jsoupToDomParse() throws IOException {
//1. 获取document
Document document = Jsoup.connect("http://www.itcast.cn/subject/cloudzly/index.shtml").get();
//1.1 获取文档的标题
String title = document.title();
System.out.println(title);
//1.2
Elements elements = document.getElementsByClass("head");
Element element = elements.get(0);
elements = element.getElementsByClass("inner");
//System.out.println(elements.size());
element = elements.get(0);
Elements lis = element.getElementsByTag("li");
/*for (Element li : lis) {
System.out.println(li.);
}*/
Elements a = lis.get(0).getElementsByTag("a");
String text = a.text();
System.out.println(text);
}
}
//使用jsoup的选择器来解析网页的数据
@Test
public void jsoupToSelectParse() throws IOException {
//1.获取document对象
Document document = Jsoup.connect("http://www.itcast.cn/subject/cloudzly/index.shtml").get();
//2. 获取标题
Elements title = document.select("title");
System.out.println(title.text());
//3. 获取 云计算大数据培训 内容
//Elements elements = document.select(".head .inner li");
Elements elements = document.select("body > div.wrap > div.head > div > ul > li:nth-child(1) > a");
/*Element element = elements.get(0);
Elements a = element.select("a");*/
System.out.println(elements.text());
}
}
//获取网易新闻的内容
@Test
public void jsoupTo163Parse() throws IOException {
Document document = Jsoup.connect("http://news.163.com/18/0727/08/DNN5HCQU0001875N.html").get();
//1. 解析新闻的标题
Elements elements = document.select("#epContentLeft");
Elements h1 = elements.select("h1");
System.out.println(h1.text());
//2. 获取新闻的来源
Elements laiyuan = document.select("#ne_article_source");
System.out.println(laiyuan.text());
//3. 获取新闻的正文
Elements ps = document.select("#endText p");
for (Element p : ps) {
System.out.println(p.text());
}
}
}
目前采用的保存到mysql数据库中, 以后hadoop, hbase
四种方案:
jdbc:
dbutils:
jdbcTemplate
mybatis
//需求: 爬取起点中文网中任意一个榜单的小说
public class QiDianSprider {
public static void main(String[] args) throws IOException {
//1. 确定爬取的url
String url = "https://www.qidian.com/";
//2. 发起请求获取数据: httpClient
//2.1 创建httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.2 创建请求方式: get
HttpGet httpGet = new HttpGet(url);
//2.3 发起请求获得响应
CloseableHttpResponse response = httpClient.execute(httpGet);
String html = EntityUtils.toString(response.getEntity(), "utf-8");
//3.解析数据: jsoup
Document document = Jsoup.parse(html);
Elements elements = document.select("div[class=rank-list sort-list]");
Elements lis = elements.select(".book-list ul li");
//System.out.println(lis.size());
Elements as = lis.select("a[href^=//book.qidian.com]:not([class=link])");
for (Element a : as) {
String href = a.attr("href");
// http://book.qidian.com/info/1012284323
//System.out.println(href);
//拼接url
href = "https:" + href;
//System.out.println(href);
//重新发起请求, 获取每一个小说页面
httpClient = HttpClients.createDefault();
httpGet = new HttpGet(href);
response = httpClient.execute(httpGet);
html = EntityUtils.toString(response.getEntity(), "utf-8");
document = Jsoup.parse(html);
//解析小说详情页
elements = document.select("#readBtn");
href = elements.attr("href");
//拼接小说内容的url
href = "https:" + href;
System.out.println(href);
while (true) {
//重新发起请求, 获取每一个小说页面
httpClient = HttpClients.createDefault();
httpGet = new HttpGet(href);
response = httpClient.execute(httpGet);
html = EntityUtils.toString(response.getEntity(), "utf-8");
//获取到小说内容页数据
document = Jsoup.parse(html);
//获取小说的名称
elements = document.select(".book-cover-wrap h1");
System.out.println("小说名称:" + elements.text());
//获取章节名称
elements = document.select(".j_chapterName");
System.out.println("章节名称" + elements.text());
//获取小说的内容
elements = document.select("div[class=read-content j_readContent] p");
for (Element element : elements) {
System.out.println(element.text());
}
//获取下一章节的url
elements = document.select("#j_chapterNext");
href = elements.attr("href");
if(href==null||href==""||href==" "){
System.out.println("跳出本小说内容");
break;
}
href = "https:" + href;
}
}
}
}
//需求: 模拟登陆, 将登陆后的用户的数据获取到
public class LoginSpider {
public static void main(String[] args) throws Exception {
//1.确定url
String url = "http://www.svn.club/user/login";
//2. 发起请求, 获取数据
CloseableHttpClient httpClient = HttpClients.createDefault();
//3. 创建请求方式: post
HttpPost httpPost =new HttpPost(url);
//4. 设置参数
List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>();
list.add(new BasicNameValuePair("uid","itcast"));
list.add(new BasicNameValuePair("pwd","www.itcast.cn"));
list.add(new BasicNameValuePair("x","97"));
list.add(new BasicNameValuePair("y","29"));
HttpEntity entity = new UrlEncodedFormEntity(list);
httpPost.setEntity(entity);
//5. 设置浏览器的类型: 模拟浏览器的
httpPost.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");
//6.发起请求
CloseableHttpResponse response = httpClient.execute(httpPost);
//7. 获取响应的内容
// String html = EntityUtils.toString(response.getEntity(), "utf-8");
//7. 获取状态码
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode==302){
Header[] locations = response.getHeaders("Location");
Header[] cookies = response.getHeaders("Set-Cookie");
String reURL = locations[0].getValue();
String cookie = cookies[0].getValue();
// System.out.println(reURL);
//拼接url
reURL = "http://www.svn.club"+reURL;
//重新发送请求, 获取登陆后的数据
httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(reURL);
httpGet.setHeader("Cookie",cookie);
response = httpClient.execute(httpGet);
String html = EntityUtils.toString(response.getEntity(), "utf-8");
Document document = Jsoup.parse(html);
//System.out.println(document);
Elements elements = document.select(".tb");
Element element = elements.get(0);
Elements trs = element.select("tr");
Element element1 = trs.get(1);
String aText = element1.select("td").get(0).select("a").text();
System.out.println(aText);
}
}
}
作业:
1. 演示 jdk发送get和post请求(www.itcast.cn)
2. 演示httpClient发送post请求
3. 寻找一个网页, 进行解析(解析传智博客的科目列表)
4. 将案例一书写(200%)
5. 将案例二写一遍