网络爬虫(HTTPClient + Jsoup)相关

44 阅读4分钟

HTTPClient

maven配置

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpcore</artifactId>
  <version>4.4.10</version>
</dependency>
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.6</version>
</dependency>

log4j配置

  • 在本地创建一个 log.log文件
  • java项目中添加maven依赖
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>
  • 在source文件夹同级创建resource文件夹,并添加log4j.properties文件,在文件中添加:
log4j.rootLogger=DEBUG, Console ,File  

#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.Console.layout=org.apache.log4j.PatternLayout  
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

#File
log4j.appender.File = org.apache.log4j.FileAppender
log4j.appender.File.File = D://Code//JavaProject//pachong//log.log
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =%d [%t] %-5p [%c] - %m%n

GTE请求

//创建HTTPClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();

//创建HTTPGET对象,访问URL地址
HttpGet httpGet = new HttpGet("http://www.itcast.cn");

CloseableHttpResponse response = null;
try {
    //使用HTTPClient发起请求,获取response
    response = httpClient.execute(httpGet);

    //解析响应
    if (response.getStatusLine().getStatusCode() == 200){
        String content = EntityUtils.toString(response.getEntity(), "utf8");
        System.out.println(content.length());
    }
} catch (IOException e) {
    throw new RuntimeException(e);
} finally {
    //关闭response
    response.close();
}

带参数的Get请求

//设置请求地址是:http://yun.itheima.com/search?keys=Java
//创建URIBuilder(初始化HttpGet对象的参数)
URIBuilder uriBuilder = new URIBuilder("http://yun.itheima.com/search");

//设置参数
uriBuilder.setParameter("keys", "java");

//创建HTTPGET对象,访问URL地址
HttpGet httpGet = new HttpGet(uriBuilder.build());
System.out.println("请求信息:" + httpGet);

post请求

//创建post请求
HttpPost httpPost = new HttpPost("http://www.itcast.cn");
//其余步骤与GET一致

post请求(带参)

//创建post请求
HttpPost httpPost = new HttpPost("http://yun.itheima.com/search");

//声明List集合,封装表单中的参数
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("keys", "Java"));

//创建表单的Entity对象
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, "utf8");

//设置表单的Entity对象到Post请求中
httpPost.setEntity(formEntity);

System.out.println("请求信息:" + httpPost);

连接池

如果每次请求都要创建HttpClient,会有频繁创建和销毁的问题,可以使用连接池来说解决这个问题

public static void main(String[] args) throws IOException {
    //创建连接池管理器
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

    //设置最大连接数
    cm.setMaxTotal(100);

    //设置每个主机(目标服务器)的最大连接数
    cm.setDefaultMaxPerRoute(10);
    
    //使用连接池管理器发起请求
    doGet(cm);
    doGet(cm);
}

private static void doGet(PoolingHttpClientConnectionManager cm) throws IOException {
    // 不是每次创建新的HttpClient,而是从连接池中获取HttpClient对象
    CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
    HttpGet httpGet = new HttpGet("http://www.itcast.cn");

    CloseableHttpResponse response = null;
    try {
        response = httpClient.execute(httpGet);
        if (response.getStatusLine().getStatusCode() == 200){
            String Content = EntityUtils.toString(response.getEntity(), "utf8");
            System.out.println(Content.length());
        }
    } catch (ClientProtocolException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    finally {
        if (response != null) {
            try {
                response.close();
            } catch (IOException e){
                e.printStackTrace();
            }
            // 不能关闭HttpClient,而由连接池管理HttpClient
            //httpClient.close();
        }
    }
}

请求参数

有时候因为网络或者目标服务器的原因,请求需要很长的时间才能完成,我们需要自定义相关时间。

CloseableHttpClient httpClient = HttpClients.createDefault();

HttpGet httpGet = new HttpGet("http://www.itcast.cn");

//配置请求信息
RequestConfig config = RequestConfig.custom()
        .setConnectTimeout(1000) //创建连接的最长时间,单位时毫秒数
        .setConnectionRequestTimeout(500) //设置获取连接的最长时间,单位是毫秒
        .setSocketTimeout(10 * 1000) //设置数据传输的最长时间,单位是毫秒
        .build();

//给请求设置请求信息
httpGet.setConfig(config);

Jsoup

jsoup是Javade HTML解析器,主要功能如下:

  • 从一个URL,文件或字符串解析HTML
  • 使用DOM或CSS选择器来查找、取出数据
  • 可操作HTML元素、属性、文本

maven配置

<dependency> //单元测试
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.15.4</version>
</dependency>
<dependency> // 文件操作工具类
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.11.0</version>
</dependency>
<dependency> //工具类,字符串操作
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.12.0</version>
</dependency>
//解析url,第一个参数是访问的url,第二个参数是访问时候的超时时间
Document doc = Jsoup.parse(new URL("http://www.itcast.cn"), 10000);

//使用标签选择器,获取title标签中的内容
String title = doc.getElementsByTag("title").first().text();

System.out.println(title);

虽然使用Jsoup可以替代HttpClient直接发起请求解析数据,但是往往不会这样使用,因为实际开发中,需要使用多线程,连接池、代理等等方式,而Jsoup对这些的支持并不是很好,所以我们一般把Jsoup仅仅作为Html解析攻击使用。

//解析文件
Document doc = Jsoup.parse(new File("C:\Users\ljx\Desktop\传智教育【官网】-好口碑IT职业教育,好口碑IT培训机构,一样的教育,不一样的品质.html"), "utf8");

String title = doc.getElementsByTag("title").first().text();

System.out.println(title);

使用Dom方式遍历文档

  • 根据id:getElementById
  • 根据标签:getElementsByTag
  • 根据class获取元素:getElementByClass
  • 根据属性获取元素:getElementsByAttribute
  • 根据属性名与属性值:getElementsByAttributeValue

获取元素内容

  • element.id()
  • element.className()
  • element.ClassNames()
  • element.attr("属性名")
  • element.attributes() 获取所有属性
  • element.text() 获取文本

Selector选择器

  • Elements elements = doc.select("span"); //通过标签查找
  • Element element = doc.select("#city_bj").first(); //通过ID查找第一个元素
  • doc.select(".class_a").first(); //通过class获取
  • doc.select("[abc]").first(); //通过属性获取
  • Elements e = doc.select("[calss=s_name)]"); //通过属性值获取多个元素

Selector 选择器的组合使用

  • 元素+ID h3#city_bj: doc.select("h3#city_bj").first()
  • 元素+class li.class_a:doc.select("li.class_a").first()
  • 元素+属性名:span[abc]:doc.select("span[abc]").first()
  • 任意组合
  • 父子关系:
    1. ancestor child //查找某个元素下的子元素
    2. parent > child //查找某个父元素下的直接子元素
    3. parent > * //查找某个父元素下的所有子元素