1. 爬取思路
在线编程评测系统中,根据问答题的题目,需要自动搜索CSDN博客中的相关回答。具体流程如下:
- 爬取搜索列表:使用爬虫技术从百度搜索引擎中获取与题目关键词相关的CSDN博客搜索列表。
- 解析搜索列表:从搜索结果页面中解析出列表中的第一项。
- 获取博客链接:解析第一项搜索结果,提取CSDN博客的链接。
- 爬取博客内容:再次使用爬虫技术访问CSDN博客链接,获取博客内容,并去除其中的img标签。
2、爬取工具
在本次实现过程中,我们结合了Jvpeteer和Jsoup这两工具来操作和解析网页内容。接下来,对这两个工具及其相关的技术概念进行简单阐述。
需要注意的是,如果目标网站并未设置反爬虫机制,那么仅使用Jsoup便足以完成网页的解析工作。Jsoup作为一款纯Java编写的HTML解析库,能够方便地处理HTML文档,提取并操作其中的数据。然而,当目标网站存在复杂的JavaScript渲染或反爬虫策略时,我们需要借助Jvpeteer这样的工具来模拟浏览器行为,从而成功获取网页内容。其中,百度、Bing以及csdn站内搜索,均采用了反爬虫机制。
Jvpeteer能够模拟浏览器的各种操作,如页面导航、表单提交、点击等,非常适合处理那些需要JavaScript动态加载或渲染的网页。通过Jvpeteer,我们可以先获取到完整的、经过JavaScript渲染的网页内容,然后再利用Jsoup进行进一步的数据提取和解析。
3、爬取过程分析
1、搜索与定位
为了精确地在CSDN博客平台内搜索并爬取相关信息,可以利用百度搜索引擎,并限定搜索范围在CSDN博客(blog.csdn.net
)。具体做法是,在百度搜索框中输入特定的URL结构,格式为:"https://www.baidu.com/s?wd=site:blog.csdn.net "+keyword
",其中keyword
代表我们感兴趣的关键词。例如,若我们想搜索"什么是设计模式,列举几种常见的设计模式",则完整的搜索URL会是:"https://www.baidu.com/s?wd=site:blog.csdn.net 什么是设计模式, 列举几种常见的设计模式?
"。
2、页面结构分析:
在浏览器中打开上述URL后,我们会看到百度返回的搜索结果页面。这些结果中包含了大量CSDN博客的文章列表,这些正是我们想要爬取的数据。接下来通过开发者工具对页面的HTML结构进行分析,通过检查元素,可以发现搜索结果中的每一条信息都被包裹在一个具有特定类名(如class="result c-container xpath-log new-pmd"
)的div
标签内。这个div
标签内部包含了文章的标题、链接、摘要等关键信息,这些信息正是我们爬取的目标。
3、博客详情页分析
选择搜索结果中的第一个博客,点击进入CSDN博客详情页。在详情页中,博客内容的主体部分被包裹在一个article
标签中,且该标签具有类名baidu_pl
。通过浏览器的开发者工具,我们可以定位到这个article
标签,并确认它包含了我们要爬取的博客文章全部内容。
4、爬取实现
1、maven引入Jvpeteer和Jsoup
xml
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.16.2</version>
</dependency>
<dependency>
<groupId>io.github.fanyong920</groupId>
<artifactId>jvppeteer</artifactId>
<version>1.1.6</version>
</dependency>
2、构建CsdnSearchService
首先,启动浏览器并配置为无头模式,根据传入的关键词形成搜索URL并打开相应的搜索页面。
java
// 初始化浏览器相关
BrowserFetcher.downloadIfNotExist(null);
ArrayList<String> argList = new ArrayList<>();
// withHeadless 为true,则不显示浏览器界面 无头模式
LaunchOptions options = new LaunchOptionsBuilder().withArgs(argList).withHeadless(true).build();
argList.add("--no-sandbox");
argList.add("--disable-setuid-sandbox");
System.out.println("==1=");
Browser browser = Puppeteer.launch(options);
// 打开页面,开爬
Page page = browser.newPage();
page.goTo("https://www.baidu.com/s?wd=site:blog.csdn.net "+keyword);
// 获取页面所有内容(HTML格式)
String content = page.content();
// 解析页面元素,方便后面定位
Document document = Jsoup.parse(content);
接着,对搜索页面进行解析,以抓取并提取搜索结果列表。
java
// 找出我们上面说的那个class所在的div标签
Elements elements = document.getElementsByClass("result c-container xpath-log new-pmd");
最后,选择第一个搜索结果链接,进一步解析csdn博客页面,并获取其中的内容。
java
if (elements.size() > 1) {
Element secondItem = elements.get(1); // baidu第二个搜索结果
String blogLink = secondItem.attr("mu"); // 博客链接,可以通过div中的mu属性获取
Document blogDoc = Jsoup.connect(blogLink).get(); // 获取博客内容
blogDoc.select("img").remove(); // 移除博客中的图片
Element article = blogDoc.selectFirst("article.baidu_pl"); // 获取博客正文内容
String articleHtml = null; // 获取<article>标签内的HTML内容
String articleText = null; // 获取<article>标签内的文本内容,如果需要的话
if (article != null) {
// 获取<article>标签内的HTML内容
articleHtml = article.html();
// 如果你只需要文本内容,可以使用text()方法
articleText = article.text();
} else {
browser.close();
System.out.println("没有找到class为'baidu_pl'的<article>标签。");
}
// 注意:这里提取博客内容的方式可能需要根据实际的博客页面结构进行调整
browser.close(); // 关闭浏览器
return articleHtml; // 或者你可以返回一个更结构化的对象,而不仅仅是HTML或文本
} else {
browser.close();
return "未找到足够的搜索结果。";
}
全部代码:
java
package com.tuoyu.project.system.service;
import com.ruiyun.jvppeteer.core.Puppeteer;
import com.ruiyun.jvppeteer.core.browser.Browser;
import com.ruiyun.jvppeteer.core.browser.BrowserFetcher;
import com.ruiyun.jvppeteer.core.page.Page;
import com.ruiyun.jvppeteer.options.LaunchOptions;
import com.ruiyun.jvppeteer.options.LaunchOptionsBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
@Service
public class CsdnSearchService {
public String searchCsdn(String keyword) throws IOException, ExecutionException, InterruptedException {
// 初始化浏览器相关
BrowserFetcher.downloadIfNotExist(null);
ArrayList<String> argList = new ArrayList<>();
// withHeadless 为true,则不显示浏览器界面
LaunchOptions options = new LaunchOptionsBuilder().withArgs(argList).withHeadless(true).build();
argList.add("--no-sandbox");
argList.add("--disable-setuid-sandbox");
System.out.println("==1=");
Browser browser = Puppeteer.launch(options);
// 打开页面,开爬
Page page = browser.newPage();
page.goTo("https://www.baidu.com/s?wd=site:blog.csdn.net "+keyword);
// 获取页面所有内容(HTML格式)
String content = page.content();
// 解析页面元素,方便后面定位
Document document = Jsoup.parse(content);
// 找出我们上面说的那个class所在的div标签
Elements elements = document.getElementsByClass("result c-container xpath-log new-pmd");
if (elements.size() > 1) {
Element secondItem = elements.get(1); // baidu第二个搜索结果
String blogLink = secondItem.attr("mu"); // 博客链接,可以通过div中的mu属性获取
Document blogDoc = Jsoup.connect(blogLink).get(); // 获取博客内容
blogDoc.select("img").remove(); // 移除博客中的图片
Element article = blogDoc.selectFirst("article.baidu_pl"); // 获取博客正文内容
String articleHtml = null; // 获取<article>标签内的HTML内容
String articleText = null; // 获取<article>标签内的文本内容,如果需要的话
if (article != null) {
// 获取<article>标签内的HTML内容
articleHtml = article.html();
// 如果你只需要文本内容,可以使用text()方法
articleText = article.text();
} else {
browser.close();
System.out.println("没有找到class为'baidu_pl'的<article>标签。");
}
// 注意:这里提取博客内容的方式可能需要根据实际的博客页面结构进行调整
browser.close(); // 关闭浏览器
return articleHtml; // 或者你可以返回一个更结构化的对象,而不仅仅是HTML或文本
} else {
browser.close();
return "未找到足够的搜索结果。";
}
}
}
综上所述,通过结合Jvpeteer与Jsoup这两个强大的工具,能够实现一个功能高效的Java爬虫程序。该程序能够服务于在线编程评测系统,自动地在CSDN博客中搜索并抓取与题目紧密相关的回答内容。
在今天,对于问答需求,更为先进且高效的解决方案是接入大型语言模型,利用AI技术来回答用户的问题,不仅能够提供更快速、更准确的答案,还能够根据用户的上下文和需求进行智能推理和定制化回答。 :-)