聚合平台项目之数据抓取

首先先记录一下我自己对这个数据抓取的一些心得:

数据抓取也就是常说的爬虫。

在我没真正去做的时候,我还想爬虫好高大上。

现在学完之后也就怯魅了

其实本质就是在自己的代码中模拟浏览器给后端发请求,后端收到响应之后,返回给前端,这个时候我们进行接收即可。

当然我说的那是最简单的数据抓取,如果别人网站有反爬虫的机制,那可能就需要更多的成本了

说完了心得之后,就开始真正记录了

首先分析的是数据抓取的流程:

    数据抓取流程

  1. 分析数据源,怎么获取?
  2. 拿到数据后,怎么处理?
  3. 写入数据库等存储

数据抓取的几种方式

  1. 直接请求数据接口(最方便),可使用 HttpClient、OKHttp、RestTemplate、Hutool 等客户端发送请求
  2. 等网页渲染出明文内容后,从前端完整页面中解析出需要的内容
  3. 有一些网站可能是动态请求的,他不会一次性加载所有的数据,而是要你点某个按钮、输入某个验证码才会显示出数据。可使用无头浏览器:selenium、node.js puppeteer(这篇文章没用到第三种方式)

获取网页的文字信息:

上面说了,本质上就是向后端发送请求

所以,我们第一步就是要知道往哪发请求,并且携带什么参数?

以编程导航网站为例编程导航 - 程序员一站式编程学习交流社区,做您编程学习路上的导航员 (code-nav.cn)

 

F12就能知道

查看这个携带的参数的时候,最好变成这种源代码模式,用视图好像是会有格式上的错误。

我们复制之后:

@SpringBootTest
public class crawler {

    @Resource
    private PostService postService;

    @Test
    public void TestCrawler(){
        String json ="{\"pageSize\":12,\"sortOrder\":\"descend\",\"tags\":[\"Java\"],\"current\":1,\"reviewStatus\":1,\"category\":\"文章\",\"hiddenContent\":true,\"priorityList\":[999,9999],\"sorterList\":[{\"field\":\"priority\",\"asc\":false},{\"field\":\"createTime\",\"asc\":false}]}";
        String url  = "https://api.code-nav.cn/api/course/list/page/vo";
        String result = HttpRequest.post(url)
                .body(json)
                .execute().body();
        Map<String,Object> map = JSONUtil.toBean(result,Map.class);
        JSONObject data = (JSONObject)map.get("data");
        JSONArray records = (JSONArray) data.get("records");
        //1:title 2:content 3:tag
        List<Post> postList = new ArrayList<>();
        for (Object record : records) {
            JSONObject tempjson = (JSONObject)record;
            Post post = new Post();
            post.setTitle(tempjson.getStr("title"));
            post.setContent(tempjson.getStr("content"));
            Object tags = tempjson.get("tags");
            if(!(tags instanceof JSONNull)){
                JSONArray jsonArray = (JSONArray) tempjson.get("tags");
                List<String> tagList = jsonArray.toList(String.class);
                post.setTags(JSONUtil.toJsonStr(tagList));
            }else {
                post.setTags("");
            }
            post.setUserId(1L);
            postList.add(post);
        }
        final boolean save = postService.saveBatch(postList);
        System.out.println(save);
    }

}

上面是编写了一个测试类。

一开始发送请求的时候,我们可以先看一下拿到了那些东西

到这一步,我们就需要思考?

我们需要什么?再针对从获得的资源中去取

我们项目的背景是要往数据库里面插入文章数据

所以我们需要的就是三个元素:

标题title    内容content    标签tags

明白了需求之后,我们就可以一步一步拆解我们拿到的资源

然后我们发现拿到的东西是一个json格式的数据

同样也是用Hutool的JSON解析的工具转换。

获取网页的图片信息:

在说获取网页的图片信息之前

可以先说一下这个项目的搜索图片的原理

这个平台搜索图片并不是将所有图片都存储在自己的数据库中

是自己的前端访问我的后端

然后我的后端通过我自己代码的逻辑去访问其它人的服务器

整体流程就是这样

讲完这个,我们直接开始

在开始看代码和网页之前,需要先介绍一个工具:Jsoup解析库:支持发送请求获取HTML文档,并且从中解析出需要的字段。

第一步肯定也是导入依赖:

        <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.15.3</version>
        </dependency>

第二步访问官网然后获取实例代码

jsoup: Java HTML parser, built for HTML editing, cleaning, scraping, and XSS safety

Document doc = Jsoup.connect("https://en.wikipedia.org/").get();
log(doc.title());
Elements newsHeadlines = doc.select("#mp-itn b a");
for (Element headline : newsHeadlines) {
  log("%s\n\t%s", 
    headline.attr("title"), headline.absUrl("href"));
}
  1. 连接到 Wikipedia 并获取 HTML 文档

    Document doc = Jsoup.connect("https://en.wikipedia.org/").get();

    • Jsoup.connect("https://en.wikipedia.org/"):使用 Jsoup 连接到指定的 URL(在这里是 Wikipedia 的主页)。
    • .get():发送 HTTP GET 请求以获取该 URL 的 HTML 内容,并将其解析为 Document 对象。
  2. 记录网页标题

    log(doc.title());

    • doc.title():获取网页的标题。
    • log(...):假设这是一个自定义方法或库函数,用于打印或记录信息。这里它会输出网页的标题。
  3. 选择特定的 HTML 元素

    Elements newsHeadlines = doc.select("#mp-itn b a"); 
    • doc.select("#mp-itn b a"):使用 CSS 选择器从 HTML 文档中选择所有符合 #mp-itn b a 选择器的元素。
    • Elements 是一个 Jsoup 的集合类,包含了所有符合选择器的 Element 对象。
  4. 遍历并记录每个新闻标题的标题属性和链接

    for (Element headline : newsHeadlines) { log("%s\n\t%s", headline.attr("title"), headline.absUrl("href")); }

    • for (Element headline : newsHeadlines):遍历所有选择器匹配到的 Element 对象。
    • headline.attr("title"):获取当前 Elementtitle 属性,这通常是链接的悬浮提示文本。
    • headline.absUrl("href"):获取当前 Elementhref 属性的绝对 URL。
    • log("%s\n\t%s", ...):记录或打印出每个新闻标题的 title 属性和 href 链接的绝对 URL。

总的来说,这段代码从 Wikipedia 的主页抓取 "In the news" 区域的所有新闻链接,然后输出每条新闻的标题和链接。

这里有一个点就是这个css选择器,待会也得需要用到这个点。

讲完了步骤

我们下面开始从网页分析:

当我们找不到页面中图片的元素的时候可以点这个检查

然后我们上下找一找

就会发现

这个class叫iusc的css样式选择器,就包含这个图片

然后我们复制这个名字

然后我们鼠标往上一放,就会发现所有的图片都被选中了

ok,那我们图片的资源就找到了

下一个是对应这个图片的标题

这个什么inflnk就能跟踪到这个图片的文字

我们还是和上面一样

最后整合一下,我们直接看代码:

@SpringBootTest
public class crawler {

    @Resource
    private PostService postService;

   
    @Test
    public void JsoupTest() throws IOException {
        String current = "1";
        String url = "https://cn.bing.com/images/search?q=小黑子&form=HDRSC2&first="+current;
        Document doc = Jsoup.connect(url).get();
        Elements elements = doc.select(".iusc");
        final Elements elements1 = doc.select("a.inflnk");
        List<Picture> pictureList = new ArrayList<>();
        for (int i = 0; i < elements.size(); i++) {
            //获取图片
            final String m = elements.get(i).attr("m");
            Map<String,Object> map = JSONUtil.toBean(m,Map.class);
            final String murl = (String) map.get("murl");
            //获取标题
            final String title = elements1.get(i).attr("aria-label");
            Picture picture = new Picture();
            picture.setMurl(murl);
            picture.setTitle(title);
            pictureList.add(picture);
        }
    }

}

我们代码的整体逻辑就是:

首先先修改一下这个url

并且和上面Jsoup的select一样,我们将这个图片中我们刚刚获取的这个css样式选择器直接cv上去

接着就可以获取这个返回值了

然后我们也是一步一步进行输出,每个网页包裹的东西不太一样,我们一层一层取出来就行。

最后稍微来看一下在项目中的实现:

首先就是这个文字资源

我将这个这个放到了一个一次性任务中

这个一次性任务怎么保证一次性呢

我们都知道springboot项目启动后会加载所有的bean对象

我们只需要将这个类注册成bean即可,然后用完直接把@Component注销掉即可

下一个是图片的项目实战应用

@Service
public class PictureDataSource implements DataSource{
    @Override
    public Page doSearch(String searchText, long pageNum, long pageSize) {
        String url = String.format("https://cn.bing.com/images/search?q=%s&form=HDRSC2&first=%s",searchText,pageSize);
        Document doc = null;
        try {
            doc = Jsoup.connect(url).get();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        Elements elements = doc.select(".iusc");
        final Elements elements1 = doc.select("a.inflnk");
        List<Picture> pictureList = new ArrayList<>();
        for (int i = 0; i < elements.size(); i++) {
            //获取图片
            final String m = elements.get(i).attr("m");
            Map<String,Object> map = JSONUtil.toBean(m,Map.class);
            final String murl = (String) map.get("murl");
            //获取标题
            final String title = elements1.get(i).attr("aria-label");
            Picture picture = new Picture();
            picture.setMurl(murl);
            picture.setTitle(title);
            pictureList.add(picture);
            if(pictureList.size()>=pageSize){
                break;
            }
        }
        Page<Picture> picturePage= new Page<>(pageNum,pageSize);
        picturePage.setRecords(pictureList);
        return picturePage;
    }
}

整体的东西是差不多的

我们将这个url修改成动态了

之前在测试类中我们是写死了小黑子

这里用一个searchText就行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值