通过网络来实现单词的翻译

server/2024/12/27 19:25:54/

接着我们上一讲的结尾,我们获取到了一个高频次排序好的列表,接下来的任务就是要把这么一大堆的单词进行翻译,我们想要得到每个单词的音标,有什么词性以及对应的翻译。现在我们就来讲讲通过网络来实现单词的翻译。

Java的HttpURLConnection类可以帮助我们发送HTTP请求,并获取相应的HTTP响应。我们可以设置请求头、请求方法、请求参数等信息,来模拟浏览器行为。

java"><dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.1</version>
</dependency>
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.3</version>
</dependency>

private static final String TRANSLATE_URL = "http://www.baidu.com/s?wd=";这个网页就是我们平时百度查词的地址

我就希望可以得到两种音标,还有对应的词性和翻译。具体用到的两个依赖,一个是对于网页的解析Jsoup,这个和python的BS4基本原理一致,还有一个就是用于网络请求的http依赖。

java">import org.jsoup.Jsoup;
import java.net.URLEncoder;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.HttpURLConnection;
import java.net.URL;

第一步我们首先要对查询的文字进行一个编码(这一步在项目里不需要有,因为前序操作已经确定这个是String类型了)

String encodedWord = URLEncoder.encode(word, StandardCharsets.UTF_8.toString());
// 编码查询词,防止特殊字符导致的URL解析错误

第二步是向url发起链接

// 构建完整的URL,包括查询参数
URL url = new URL(TRANSLATE_URL + encodedWord);
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

这里我们将URL封装成了带有网络协议等信息的实例对象来方便我们调用Connection方法。

现在建立好连接之后,我们可以看一下连接里面的数据。

可以看到请求体里面没有任何的数据段,这样去进行多次读取页面的操作会让百度感觉很”陌生“,所以现在要去请求头里加一些数据,让这个访问看起来就是真实的。

java">// 创建一个可变的 Map 来存储请求头Map<String, String> headers = new HashMap<>();headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");headers.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");headers.put("Connection", "keep-alive");headers.put("Cookie", "BIDUPSID=2BCC6FC9896B4237256E7EC335CECF0A; PSTM=1726058803....");headers.put("Host", "www.baidu.com");headers.put("Upgrade-Insecure-Requests", "1");headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");

这些内容都可以直接在自己的网页端查到。我们用一个map来进行存储,然后一次添加到

// 添加自定义请求头
for (Map.Entry<String, String> entry : headers.entrySet()) {connection.setRequestProperty(entry.getKey(), entry.getValue());
// 设置请求方法为GET
connection.setRequestMethod("GET");

到这一步我们的请求就算是发送完成了。接下来就是服务器的响应,200就是响应成功了

java">// 获取响应码
int responseCode = connection.getResponseCode();
StringBuilder response = new StringBuilder();
// 如果响应码为200,表示请求成功
if (responseCode == HttpURLConnection.HTTP_OK) {// 获取响应流BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));String line;// 读取响应内容while ((line = reader.readLine()) != null) {response.append(line);}reader.close();
}

在 Java 的网络编程中,当建立了一个网络连接(比如通过 Socket 连接或者 HttpURLConnection 等建立的连接,这里的 connection 就是代表这样一个已建立的连接对象),可以通过调用 getInputStream() 方法获取这个连接对应的输入流。这个输入流里包含了对方(服务器等)发送过来的数据,不过它是以字节流的形式存在的,原始状态下不太方便直接按文本内容进行读取操作。

  • InputStreamReader 是字节流和字符流之间的桥梁,它用于将字节流(也就是前面获取的 connection.getInputStream() 这个字节输入流)转换为字符流,因为很多时候我们期望处理的是文本字符形式的数据,而不是单纯的字节数组。同时,指定了编码格式为 StandardCharsets.UTF_8,这一点很关键,因为要确保正确地将字节解码成对应的字符。不同的字符编码格式下,相同的字节序列可能表示完全不同的字符,如果不指定正确的编码(服务器通常会按照某种编码格式来发送文本响应,UTF-8 是现在网络通信中很常用的一种编码格式),就可能出现乱码问题,导致后续无法正确解析和处理读取到的文本内容。

又有一个问题,为什么不直接使用 connection.getInputStream() 创建 BufferedReader

  1. 字节流与字符流的差异
    • connection.getInputStream()返回的是字节流(InputStream)。字节流是以字节为单位来处理数据的,它并不知道这些字节如何组合成字符。而在很多网络通信场景中,如读取网页内容、解析 JSON 或 XML 等文本格式的数据时,我们需要以字符为单位来处理数据。
    • 直接使用字节流来读取文本数据会比较麻烦。例如,一个汉字在 UTF - 8 编码下可能占用 3 个字节,如果只按字节读取,很难正确地将这些字节组合成完整的字符进行处理。
  2. 编码问题
    • 没有经过字符流转换直接使用字节流读取文本可能会导致编码问题。不同的字符编码方式(如 UTF - 8、GBK 等)对字符的字节表示是不同的。
    • 通过InputStreamReader可以指定编码方式(如StandardCharsets.UTF - 8),将字节流按照正确的编码转换为字符流。这样能确保从网络中读取的文本数据被正确地解码。例如,如果服务器以 UTF - 8 编码发送数据,而客户端没有正确地按照 UTF - 8 解码,就会出现乱码。
  3. 缓冲和高效读取
    • BufferedReader提供了缓冲功能。它内部有一个缓冲区,当读取数据时,会先从缓冲区获取数据,只有当缓冲区为空时才会从底层的输入流(这里是经过InputStreamReader转换后的字符输入流)读取数据并填充缓冲区。
    • 这种缓冲机制可以减少与底层数据源(如网络连接)的交互次数,提高读取效率。直接使用connection.getInputStream()没有这种缓冲机制,每次读取操作可能都会涉及到相对耗时的底层 I/O 操作。例如,在读取一个较大的文本文件或网络响应中的大量文本内容时,缓冲机制可以显著提高性能。
  4. 方便的文本读取方法
    • BufferedReader提供了方便的readLine()方法,可以逐行读取文本内容。在处理文本数据时,很多时候数据是以行分隔的,如网页的 HTML 代码、配置文件等。
    • 直接使用connection.getInputStream()作为字节流没有这种按行读取的便捷方法,需要自己编写复杂的代码来实现按行读取字节流并将其转换为字符的功能

总之,reader读到的信息就是获取的那个页面的源码。我们把他存到一个String对象里面。


接下来才是真正意义上的爬取操作。

java">Document doc = Jsoup.parse(response.toString());Elements posElements = doc.select("span.part-name_3R3ee");
Elements En_phoneticElements = doc.select("span.orginal-txt_3dDqw");
Elements US_phoneticElements = doc.select("sapn.orginal-txt_3dDqw");

能看到在html标签里面,英式的发音藏在一个span标签里面,相对应的翻译

我就不一一列举了,用doc.select方式找到对应的标签进行提取。我们可以定义一个DTO或者一个数据库按照类型把他们都存进来。

wordModel.setWord(word);
wordModel.setEnPronunciation(En_phoneticElements.first().text());
wordModel.setUsPronunciation(US_phoneticElements.first().text());

根据这种写法就得到了我们的音标。但是又有个问题,音标只有一个,但是单词的翻译可能会有很多个,所以单纯的提取词性和翻译就会有问题。

我们得到两个travel的词性,理论上我们需要通过一个for循环来获取。通过分析源码,我们可以看到代码一个词性和对应的翻译都放在一个大的div标签了。

但现在又有一个问题就是这个翻译并不是一个字符串,是一个列表,多个span标签组成的。所以我们想要把翻译搞到一起就得多写两句代码,把列表换成字符串。

Elements translationElements = parent.select("span.mean-text_4MwRe");
StringBuilder translations = new StringBuilder();for (Element element : translationElements) {// 去除分号,避免重复String text = element.text().replace(";", "").trim();if (!text.isEmpty()) {translations.append(text).append(";");}
}

把数据格式安排明白之后,我们就可以继续存储数据了。

java">for (Element posElement : posElements) {// 获取当前词性的翻译和音标Element parent = posElement.parent();if (parent != null) {Elements translationElements = parent.select("span.mean-text_4MwRe");StringBuilder translations = new StringBuilder();for (Element element : translationElements) {// 去除分号,避免重复String text = element.text().replace(";", "").trim();if (!text.isEmpty()) {translations.append(text).append(";");}}// 创建一个结果映射Map<String, String> result = new HashMap<>();result.put("translation", translations.toString());result.put("pos", posElement.text());results.add(result);}}

最终的results当然是多个map映射的列表了,不要嫌麻烦,我们继续转换数据,把得到的词性翻译列表转换成一个可以直观的字符串。

java">StringBuilder combinedResult = new StringBuilder();
for (Map<String, String> result : results) {String pos = result.get("pos");String translation = result.get("translation");combinedResult.append(pos).append(translation).append(" ");
}
wordModel.setDescription(combinedResult.toString());

这样的话,我们就把所有的一个简易的爬虫式的单词翻译讲清楚了。我们后续会继续讲如何把查询到的翻译存到本地数据库。


http://www.ppmy.cn/server/153701.html

相关文章

Docker安装

目录 1. 联网安装 Docker 2. 离线安装 Docker 3. 安装 Docker Compose 4. 卸载 Docker 和 Docker Compose 1. 联网安装 Docker 在 CentOS 上通过 yum 安装 Docker&#xff1a; # 安装 Docker yum -y install docker # 启动 Docker systemctl start docker # 查看 D…

推动开源数据生态:SeaTunnel ByConity技术沙龙精彩回顾

2024年12月15日&#xff0c;Apache SeaTunnel 和 ByConity 社区联合举办的主题为「探索数据生态协同创新」的技术沙龙在万胜广场C塔圆满落幕。 本次活动吸引了超过50位开发者、数据工程师和企业用户参与&#xff0c;技术交流氛围热烈&#xff0c;共同探讨了数据集成与仓库优化的…

数据科学与SQL:如何利用Oracle 计算正态分布概率密度?

目录 1 正态分布概率密度函数计算(在 Oracle 中) 2 均匀分布概率密度函数计算(在 Oracle 中) 3 泊松分布概率密度函数计算(在 Ora

MBox20物联网关——共仓泵房危情报警的智能化守护者

在现代工业生产环境中&#xff0c;共仓泵房作为关键设施&#xff0c;其稳定运行直接关系到企业的生产效率和安全。然而&#xff0c;传统的泵房管理方式存在诸多不足&#xff0c;如人工巡检效率低、误差大&#xff0c;紧急预警处理机制不完善等。随着物联网技术的飞速发展&#…

ios按键精灵自动化的脚本教程:自动点赞功能的实现

使用按键精灵简单模拟在抖音中进行点赞操作的示例脚本&#xff0c;不过需要提醒的是&#xff0c;在等部分平台使用自动化脚本进行操作如果违反平台规则&#xff0c;可能会导致账号被限制等情况&#xff0c;仅用于学习参考哦。 前提准备 确保你的手机屏幕分辨率已经在按键精灵…

低代码开源项目Joget的研究——Joget7社区版安装部署

大纲 环境准备安装必要软件配置Java配置JAVA_HOME配置Java软链安装三方库 获取源码配置MySql数据库创建用户创建数据库导入初始数据 配置数据库连接配置sessionFactory编译下载tomcat启动下载aspectjweaver移动jw.war文件编写脚本运行 测试参考资料 Joget&#xff0c;作为一款开…

Day55 图论part05

并查集理论基础 并查集理论基础很重要,明确并查集解决什么问题,代码如何写,对后面做并查集类题目很有帮助。 并查集理论基础 | 代码随想录 总结 1.并查集主要有两个功能:主要就是集合问题 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个将两个节点接…

DBeaver 咋手动配置sqlite 驱动

目录 1 问题2 下载 1 问题 离线安装了DBeaver 数据库软件&#xff0c;现在需要使用这个数据库打开sqlite 数据库&#xff0c;但是提示没有 驱动&#xff0c;那么我们就需要手动下载驱动&#xff0c;在这个软件里面导入 2 下载 https://repo1.maven.org/maven2/org/xerial/sql…