爬虫的基础
爬虫的基础工作就是利用 http 协议从网络上读取内容。内容一般是 html 网页,当然也可能会是图片、pdf 、视频 、json 字符串等。网络浏览器 chrome 、firefox 等也是通过 http 协议读取网络上的资源并且在界面上进行渲染,然后呈现给用户。所以做好爬虫的第一个步就是如何在程序里面利用 http 协议和网络上的资源进行交互。
Java Http 库的选择
Http协议的细节比较复杂,显然我们不应该完全重头开始从底层去实现一个。在 Java 里有很多类库已经帮你完成了这部分基础的工作。例如 JDK 自带的 HttpURLConnection 标准库、Apache HttpComponents HttpClient 库、 OkHttp 等。
- HttpURLConnection 是 JDK 原生的库,不需要引入其他的依赖。但是使用起来非常繁琐,也不支持一些高级的功能。
- Apache HttpComponents HttpClient 支持的特性非常多,可以满足用户的各种需求。有一个比较大的缺点是目前还不支持 Http2 协议。不过对于一般的爬虫来说。这个也不是什么大的问题。
- OkHttp 是一个更高层次封装的 Http Client 库,支持 Http2 最新协议。接口的设计上也更加友好,使用起来上手非常容易。
综合来说,OkHttp库是一个接口友好,功能丰富的 Http Client 库。首选他作为我们爬虫的 Http Client
相关类库的引入
如果我们的 Java 项目是 Maven 来进行管理的,那么在 pom.xml 文件中加入如下的依赖就可以引入 OkHttp 库
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.11.0</version>
</dependency>
目前最新版本是 3.11.0 。为了对 Http 响应的二进制流进行处理,我们还引入了 Apacke Commons IO 库。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
下载一个 HTML 页面
一个可以在浏览器上访问的 HTML 页面一般都是采用 Http Get 方法访问的。大概的步骤如下:
- 首先建立一个 OkHttpClient 对象;
- 然后构建一个 Request 对象;
- 之后通过 OkHttpClient 和 Request 对象创建一个新的 Call 对象
- 调用 Call execute 方法获得一个 Response 对象。
- 分析这个 Response 获得需要的结果
具体的代码如下所示
OkHttpClient httpClient = new OkHttpClient.Builder().build();
String url = "https://www.jianshu.com/p/675ea919230e";
Request request = new Request.Builder()
.url(url)
.build();
try {
Response response = httpClient.newCall(request).execute();
if (response.isSuccessful() && response.body() != null) {
System.out.println(response.body().string());
}
} catch (Exception e) {
e.printStackTrace();
}
执行这段代码后,就会将简书的一篇文章《是什么支撑了淘宝双十一,没错就是它java编程语言。》 的 html 代码在控制台打印出 来。
<!DOCTYPE html>
<!--[if IE 6]><html class="ie lt-ie8"><![endif]-->
<!--[if IE 7]><html class="ie lt-ie8"><![endif]-->
<!--[if IE 8]><html class="ie ie8"><![endif]-->
<!--[if IE 9]><html class="ie ie9"><![endif]-->
<!--[if !IE]><!--> <html> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<!-- Start of Baidu Transcode -->
<meta http-equiv="Cache-Control" content="no-siteapp" />
<meta http-equiv="Cache-Control" content="no-transform" />
<meta name="applicable-device" content="pc,mobile">
<meta name="MobileOptimized" content="width"/>
<meta name="HandheldFriendly" content="true"/>
<meta name="mobile-agent" content="format=html5;url=https://www.jianshu.com/p/675ea919230e">
<!-- End of Baidu Transcode -->
<meta name="description" content="先来看看淘宝双十一数据 总交易额:1207亿, 覆盖国家和地区:235个, 物流订单:6.75亿, 支付总笔数:10.5亿, 支付峰值:12W笔/秒, 保险总保单量:6亿笔, 保险总金额:224亿元, 历史成交额:第一年的5000W,去年912亿,今年单日千亿,流 0.9秒付款成功,6分51秒商品完成打包从仓库发出,13分19秒签收成功。淘宝再次刷新世界支付记录。世界上规模最大,复杂性最高电...">
...
</html>
通过 POST 方法获取 JSON 数据
现在动态网页,很多时候需要用用 AJAX 的方法动态的获取一些内容。这些内容可以是一些 html 代码片段,也可以是 JSON 数据。。
例如我们访问下面的网站,
在这一页里面我们可以分析每个文章的条目得到这一页里面所有的目标文章的链接。但是,如果我们想看更多的的时候,点击图片中红圈部分的 “Next” 按钮,通过再浏览器的开发选项里面观察网络情况,我们发现这里是通过 ajax 做了一次 post 请求,提交的数据是一个 json 字符串,返回的数据也是一个 json 字符串,而新的一页的文章列表的 url 就在返回的 json 字符串的内容中。在 chrome 浏览器中,我们用 F12 快捷键打开浏览器的开发者选项,然后点击 “Next” 按钮,得到如下图所示的结果。
翻页 ajax 请求这里是浏览器通过 post 方法想地址 https://api.swiftype.com/api/v1/public/engines/search.json 发起了一个 ajax 请求,请求的数据是一个 json 字符串
{
"engine_key": "Eqsrs2GTE_JFL6QA-25N",
"page": 2,
"per_page": 10,
"sort_direction": {},
"filters": {
"posts": {
"tags": ["!Private", "!999", "!private"],
"issue": {
"type": "and",
"values": ["Economy", "Economy"]
}
}
},
"facets": {
"posts": ["issue", "project", "person", "tags"]
}
}
其中 page 属性值代表页码,per_page 代表每页返回的数据条数。这个请求返回的额数据也是一个 json 字符串,如下图
ajax 请求结果那么,如果我们要用 OkHttp 来模拟这个过程,则需要用下面的代码来实现
String json="{\n" +
"\t\"engine_key\": \"Eqsrs2GTE_JFL6QA-25N\",\n" +
"\t\"page\": 1,\n" +
"\t\"per_page\": 10,\n" +
"\t\"sort_direction\": {\n" +
"\t\t\"posts\": \"desc\"\n" +
"\t},\n" +
"\t\"filters\": {\n" +
"\t\t\"posts\": {\n" +
"\t\t\t\"tags\": [\"!Private\", \"!999\", \"!private\"],\n" +
"\t\t\t\"issue\": {\n" +
"\t\t\t\t\"type\": \"and\",\n" +
"\t\t\t\t\"values\": [\"Economy\"]\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t},\n" +
"\t\"facets\": {\n" +
"\t\t\"posts\": [\"issue\", \"project\", \"person\", \"tags\"]\n" +
"\t},\n" +
"\t\"sort_field\": {\n" +
"\t\t\"posts\": \"published_at\"\n" +
"\t}\n" +
"}";
String url="https://api.swiftype.com/api/v1/public/engines/search.json";
OkHttpClient httpClient=new OkHttpClient.Builder()
.connectTimeout(30,TimeUnit.SECONDS)
.readTimeout(30,TimeUnit.SECONDS)
.build();
RequestBody requestBody=FormBody.create(MediaType.parse("application/json; charset=utf-8"),json);
Request request=new Request.Builder().url(url).post(requestBody).build();
try {
Response response = httpClient.newCall(request).execute();
if(response.isSuccessful() && response.body()!=null){
System.out.println(new String(IOUtils.toByteArray(response.body().byteStream())));
}
}catch (Exception e){
e.printStackTrace();
}
}
执行这一段代码,会在控制台打印出 “ajax 请求结果” 中的 json 字符串。
这样,我们就实现了利用 OkHttp 库来用 get 的 http 的方式获取一个网页以及通过 post 方式提交一个 json 数据的 http 请求的过程。这两个步骤基本上就解决了爬虫系统中最基本的问题。至于得到的 html 内容和 json 内容怎么分析,后面再将
网友评论