HttpClient+jsoup实现java爬虫
我们爬取的是一个电影网站http://www.coupling.pw/fl/dy/,下载图片、获取名字、百度云链接、密码
为了简单,就直接用maven项目,当然你也可以直接导入jar包
httpclient、jsoup、commons-io、commons-lang3、junit,只用到了这几个包。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
然后是pojo
package com.xiaoyu.pojo;
import java.util.Date;
/**
* Created by hasee on 2019/4/16.
*/
public class Movie {
private String name;
private String picname;
private String mlink;
private String pass;
private Date updtime;
public Movie() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPicname() {
return picname;
}
public void setPicname(String picname) {
this.picname = picname;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public Date getUpdtime() {
return updtime;
}
public void setUpdtime(Date updtime) {
this.updtime = updtime;
}
@Override
public String toString() {
return "Movie{" +
"name='" + name + '\'' +
", picname='" + picname + '\'' +
", mlink='" + mlink + '\'' +
", pass='" + pass + '\'' +
", updtime=" + updtime +
'}';
}
public String getMlink() {
return mlink;
}
public void setMlink(String mlink) {
this.mlink = mlink;
}
}
然后是工具类
public class HttpUtils {
//创建连接池管理器
private PoolingHttpClientConnectionManager cm;
public HttpUtils() {
cm=new PoolingHttpClientConnectionManager();
//设置最大连接数
cm.setMaxTotal(100);
//设置每个主机的最大连接数
cm.setDefaultMaxPerRoute(10);
//使用连接池管理器发送请求;
}
public String doGetHtml(String url){
//从连接池里取HttpClient对象
CloseableHttpClient httpClient= HttpClients.custom().setConnectionManager(cm).build();
HttpGet httpGet=new HttpGet(url);
//配置请求信息
httpGet.setConfig(getConfig());
//3.按回车,发送请求,返回响应,使用HttpClient对象发送请求
CloseableHttpResponse response= null;
try {
response = httpClient.execute(httpGet);
//4.分析响应获取数据
//判断状态码是否是200
if(response.getStatusLine().getStatusCode()==200){
if(response.getEntity()!=null) {
String content = EntityUtils.toString(response.getEntity() ,"utf8");
return content;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public String doGetImage(String url){
//从连接池里取HttpClient对象
CloseableHttpClient httpClient= HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build(); //设置共享连接池
HttpGet httpGet=new HttpGet(url);
//配置请求信息
httpGet.setConfig(getConfig());
//3.按回车,发送请求,返回响应,使用HttpClient对象发送请求
CloseableHttpResponse response= null;
try {
response = httpClient.execute(httpGet);
//4.分析响应获取数据
//判断状态码是否是200
if(response.getStatusLine().getStatusCode()==200){
if(response.getEntity()!=null) {
//获取图片后缀
String extName=url.substring(url.lastIndexOf("."));
//创建图片名
String picName= UUID.randomUUID().toString()+extName;
//下载图片名
OutputStream os= new FileOutputStream(new File("C:\\Users\\hasee\\Desktop\\http\\"+picName));
response.getEntity().writeTo(os);
return picName;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(response!=null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public List<Movie> parse(String html) throws Exception{
List<Movie> list=new ArrayList<Movie>();
Document doc= Jsoup.parse(html);
Elements elements=doc.select("article");
for(Element e:elements){
//三秒爬一次
// Thread.sleep(3000);
Movie movie=new Movie();
Element link=e.getElementsByTag("a").first();
Element image=link.getElementsByTag("img").first();
//获取图片链接
String src=image.attr("src");
String picname= this.doGetImage(src);
//存图片
movie.setPicname(picname);
Element a=e.select("div header h2 a").first();
//爬取标题
String name=a.text();
movie.setName(name);
//获取进入的链接
String url=a.attr("href");
String infoHtml=this.doGetHtml(url);
Document doc2= Jsoup.parse(infoHtml);
Element p=doc2.select(".entry-content p:has(a)").first();
//链接
if(p!=null) {
String mlink = p.getElementsByTag("a").first().attr("href");
movie.setMlink(mlink);
//提取码
String pass = p.text().substring(url.lastIndexOf(":")).replace("\"", "");
movie.setPass(pass);
}
//爬起时间
movie.setUpdtime(new Date());
list.add(movie);
}
return list;
}
private RequestConfig getConfig(){
RequestConfig config= RequestConfig.custom().setConnectTimeout(1000)//设置最长连接时间,单位毫秒
.setConnectionRequestTimeout(500)//获取连接的最长时间
.setSocketTimeout(10*1000)//设置数据传递最长时间
.build();
return config;
}
}
然后是测试代码,这里也就直接输出,没有存入数据库
@Test
public void TestMain() throws Exception {
HttpUtils httpUtils=new HttpUtils();
//获取html
String html= httpUtils.doGetHtml("http://www.coupling.pw/fl/dy");
//jsoup解析html
List<Movie> list=httpUtils.parse(html);
if(list!=null&&(list.size()!=0)) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).toString());
}
}
}
这样就介绍了,用httpclient爬取html,用jsoup解析HTML,jsoup的的selector选择器参考https://www.open-open.com/jsoup/
Selector选择器概述
tagname
: 通过标签查找元素,比如:a
ns|tag
: 通过标签在命名空间查找元素,比如:可以用fb|name
语法来查找<fb:name>
元素#id
: 通过ID查找元素,比如:#logo
.class
: 通过class名称查找元素,比如:.masthead
[attribute]
: 利用属性查找元素,比如:[href]
[^attr]
: 利用属性名前缀来查找元素,比如:可以用[^data-]
来查找带有HTML5 Dataset属性的元素[attr=value]
: 利用属性值来查找元素,比如:[width=500]
[attr^=value]
,[attr$=value]
,[attr*=value]
: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:[href*=/path/]
[attr~=regex]
: 利用属性值匹配正则表达式来查找元素,比如:img[src~=(?i)\.(png|jpe?g)]
*
: 这个符号将匹配所有元素Selector选择器组合使用
el#id
: 元素+ID,比如:div#logo
el.class
: 元素+class,比如:div.masthead
el[attr]
: 元素+class,比如:a[href]
- 任意组合,比如:
a[href].highlight
ancestor child
: 查找某个元素下子元素,比如:可以用.body p
查找在"body"元素下的所有p
元素parent > child
: 查找某个父元素下的直接子元素,比如:可以用div.content > p
查找p
元素,也可以用body > *
查找body标签下所有直接子元素siblingA + siblingB
: 查找在A元素之前第一个同级元素B,比如:div.head + div
siblingA ~ siblingX
: 查找A元素之前的同级X元素,比如:h1 ~ p
el, el, el
:多个选择器组合,查找匹配任一选择器的唯一元素,例如:div.masthead, div.logo
伪选择器selectors
:lt(n)
: 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:td:lt(3)
表示小于三列的元素:gt(n)
:查找哪些元素的同级索引值大于n
,比如
:div p:gt(2)
表示哪些div中有包含2个以上的p元素:eq(n)
: 查找哪些元素的同级索引值与n
相等,比如:form input:eq(1)
表示包含一个input标签的Form元素:has(seletor)
: 查找匹配选择器包含元素的元素,比如:div:has(p)
表示哪些div包含了p元素:not(selector)
: 查找与选择器不匹配的元素,比如:div:not(.logo)
表示不包含 class=logo 元素的所有 div 列表:contains(text)
: 查找包含给定文本的元素,搜索不区分大不写,比如:p:contains(jsoup)
:containsOwn(text)
: 查找直接包含给定文本的元素:matches(regex)
: 查找哪些元素的文本匹配指定的正则表达式,比如:div:matches((?i)login)
:matchesOwn(regex)
: 查找自身包含文本匹配指定正则表达式的元素- 注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等