使用底层技术实现爬虫操作(新手友好型)

153 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

之前遇到有人问我会不会爬虫,好家伙啊,我真不会,然后就开始叽里咕噜捣鼓一下,发现URLConnection 就可以实现 “获取互联网上的数据” 这一功能。

URLConnection类概述

抽象类 URLConnection 是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。URLConnection 基于Http协议。通常,创建一个到 URL 的连接需要几个步骤:

  1. 通过在 URL 上调用 openConnection 方法创建连接对象。
  2. 处理设置参数和一般请求属性。
  3. 使用 connect 方法建立到远程对象的实际连接。
  4. 远程对象变为可用。远程对象的头字段和内容变为可访问。

普通开发者要如何搜集海量数据呢?

在大数据时代,获得数据,最常用方法就是爬虫了,从互联网中爬取数据,典型的代表就是百度、谷歌等搜索引擎了。

在实际做爬虫开发时,通常会使用 HttpClient 、 JSoup 或 WebMagic 等框架进行实现。

但由于我目前所学的知识有限,因此这里使用API 模拟实现一个简单爬虫的功能。

注意:爬虫可以将网络上的资源作为己用,但是那些资源的拥有者是否允许开发者爬取他们的数据呢?

因此在使用爬虫时,稍有不慎,就可能面临法律纠纷,大家在使用爬虫时务必要注意法律问题。

以下列举的一些禁止爬取的场景,但由于非法律专业人员,以下列举场景仅供大家参考,爬虫的禁止场景并不限于此。

在实际工作中,如果有涉及爬虫开发的地方,建议大家务必咨询一下公司的法务人员或者相关法律人士。

  1. 不要大规模的爬取网络数据,或对对方的服务器造成较大影响
  2. 不要将爬虫用于灰色产业、敏感行业,或使用爬虫非法获利。
  3. 不要使用爬虫获取网站或用户的隐私数据
  4. 不要违背 robots 协议或经营者意志。
  5. 不要使用爬虫进行任何法律、法规或道德禁止的行为。

使用爬虫爬数据

从技术体系而言,爬虫可以分为三个部分:获取海量数据、解析数据和存储数据。

实验:爬取网站html源码

第一步:创建字符串缓冲区,存放爬取的数据 StringBuffer html = new StringBuffer();

第二步:创建 URL 对象 URL url = new URL("网址");

第三步:通过 URL 对象获取 URLConnection 对象 URLConnection urlConnection = url.openConnection();

第四步:建立连接 urlConnection.connect();

第五步:通过 IO 流提取数据信息 BufferedReader reader = null;

reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

第六步:把数据存储到缓冲区

String line = null;

while ((line = reader.readLine()) != null) { html.append(line); }

这是掘金home页的html源码:

image.png 这个是具体代码:

package 网络编程;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
/**
 * 爬虫获取海量数据
 */
public class TestCrawler {
    //获取首页的html源码
    public static String getResource() {
        BufferedReader reader = null;
        // 创建字符串缓冲区,存放爬取的数据
        StringBuffer html = new StringBuffer();
        try {
            // 创建 URL 对象
            URL url = new URL("https://juejin.cn/creator/home");
            // 通过 URL 对象获取 URLConnection 对象
            URLConnection urlConnection = url.openConnection();
            // 建立连接
            urlConnection.connect();
            // 通过 IO 流提取数据信息
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                html.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 流资源释放
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return html.toString();
    }

    public static void main(String[] args) {
        String html = getResource();
        System.out.println("首页的源码如下所示:\n"+html);
    }
}

image.png 看到上面结果是不是有点蒙

下面就是解析

作为简单的模拟爬虫,我们实现从源码中解析出网站标题,即上述 <meta> 标签中的 content 值。

🤔 那么如何解析呢?

我们知道,源码其实就是字符串,因此完全可以用字符串解析的相关方法进行解析。

假设现在要在 A 字符串中,查找是否含有 B 内容,就可以通过以下形式进行匹配查询:

Pattern pattern = Pattern.compile(A);
Matcher matcher = pattern.matcher(B);

之后再结合 matcher.find() 方法进行正则匹配,如果 matcher.find() 的返回值是 true,就说明 A 中存在符合条件的 B,否则说明不存在。

在爬虫下面添加一个方法

这里我们使用正则表达式来进行匹配

第一步:(.+?) :表示匹配一次符合条件的值,即从网站首页的源码中,寻找以下内容: <meta data-n-head="ssr" data-hid="description" name="description" content=

代码:Pattern pattern = Pattern.compile("meta data-n-head="ssr" data-hid="description" name="description" content="(.+?)"");

第二步:然后开始提取符合pattern约束的字符串

Matcher matcher = pattern.matcher(html);

第三步:提取出全部符合条件的值,即提取出标签中的content属性值并存储

String result = null;

第四步:判断是否存在 符合约束的字符串

if (matcher.find()) {

第五步:提取出全部符合条件的值,即提取出标签中的content属性值

result = matcher.group(0); result = result.substring(result.indexOf("content=") + "content=".length());

修改后代码如下:

package 网络编程;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 爬虫获取海量数据
 */
public class jiexijieguo {
    //获取掘金首页的html源码
    public static String getResource() {
        BufferedReader reader = null;
        // 创建字符串缓冲区,存放爬取的数据
        StringBuffer html = new StringBuffer();
        try {
            // 创建 URL 对象
            URL url = new URL("https://juejin.cn/creator/home");
            // 通过 URL 对象获取 URLConnection 对象
            URLConnection urlConnection = url.openConnection();
            // 建立连接
            urlConnection.connect();
            // 通过 IO 流提取数据信息
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                html.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 流资源释放
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return html.toString();
    }

    // 从首页的源码中解析特定的数据
    public static String parseResource(String html) {
        /*
            (.+?) :表示匹配一次符合条件的值,即从源码中,寻找以下内容:
            <meta data-n-head="ssr" vmid="description" name="description" content="掘金是一...">
        */
        Pattern pattern = Pattern.compile("meta data-n-head="ssr" vmid="description" name="description" content="(.+?)"");
        //提取符合pattern约束的字符串
        Matcher matcher = pattern.matcher(html);
        String result = null;
        //判断是否存在 符合约束的字符串
        if (matcher.find()) {
            //提取出全部符合条件的值,即提取出<meta>标签中的content属性值
            result = matcher.group(0);
            result = result.substring(result.indexOf("content=") + "content=".length());
        }
        return result;
    }

    public static void main(String[] args) {
        String html = getResource();
        // System.out.println("首页的源码如下所示:\n"+html);
        String result = parseResource(html);
        System.out.println(result == null ? "爬取失败" : "viewport 的 content 值是:" + result);
    }
}

image.png

如果匹配失败,注意看看

Pattern pattern = Pattern.compile(A);

Matcher matcher = pattern.matcher(B); 这里的ab是不是正确 a是在html源码里面的部分,对应xx=“xxx”名字,参数要对应

b的参数,就是我们爬取的数据集合