第八章:Java网络编程(下)
在上一部分中,我们学习了Java网络编程的基础知识,包括网络通信的基本概念、Socket编程和URL处理。在这一部分中,我们将继续深入学习更多高级的网络编程内容,包括HTTP客户端、网络爬虫入门和邮件发送等。
1. HTTP客户端
HTTP(超文本传输协议)是互联网上最常用的协议之一,用于在Web浏览器和网站服务器之间传输数据。Java提供了多种方式来实现HTTP客户端。
1.1 使用HttpURLConnection
HttpURLConnection
是URLConnection
的子类,专门用于处理HTTP连接。
想象一下:如果说普通的URLConnection
就像是一个通用的信使,可以传递各种类型的消息,那么HttpURLConnection
就像是一个专门负责HTTP协议的信使,它懂得HTTP的所有规则和礼仪。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpURLConnectionDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://www.baidu.com");
// 打开连接并转换为HttpURLConnection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法(默认为GET)
connection.setRequestMethod("GET");
// 设置连接超时时间(毫秒)
connection.setConnectTimeout(5000);
// 设置读取超时时间(毫秒)
connection.setReadTimeout(5000);
// 设置请求头
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("响应码: " + responseCode);
// 如果响应成功(状态码为200)
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取响应内容
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
response.append("\n");
}
reader.close();
// 打印前100个字符的响应内容
System.out.println("响应内容(前100个字符): " +
response.substring(0, Math.min(100, response.length())));
} else {
System.out.println("HTTP请求失败");
}
// 断开连接
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.2 发送POST请求
有时我们需要向服务器发送数据,这通常通过POST请求完成。
想象一下:如果GET请求就像是在信箱上贴一张便条询问信息,那么POST请求就像是寄出一个包裹,里面装满了你想发送的数据。
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
public class HttpPostDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("http://httpbin.org/post");
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置为POST请求
connection.setRequestMethod("POST");
// 启用输入输出流
connection.setDoOutput(true);
connection.setDoInput(true);
// 设置请求头
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// 准备POST参数
String name = URLEncoder.encode("张三", "UTF-8");
String age = URLEncoder.encode("25", "UTF-8");
String postData = "name=" + name + "&age=" + age;
// 发送POST数据
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
wr.writeBytes(postData);
wr.flush();
}
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("响应码: " + responseCode);
// 读取响应内容
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
response.append("\n");
}
reader.close();
// 打印响应内容
System.out.println("响应内容: " + response.toString());
// 断开连接
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.3 使用HttpClient(Java 11+)
Java 11引入了新的HTTP客户端API,它提供了更现代、更易用的HTTP客户端实现。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientDemo {
public static void main(String[] args) {
try {
// 创建HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 使用HTTP/2
.connectTimeout(Duration.ofSeconds(5)) // 设置连接超时
.build();
// 创建HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.baidu.com"))
.header("User-Agent", "Java HttpClient")
.GET() // 设置为GET请求
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
// 打印响应信息
System.out.println("响应码: " + response.statusCode());
System.out.println("响应头: " + response.headers());
System.out.println("响应内容(前100个字符): " +
response.body().substring(0, Math.min(100, response.body().length())));
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
2. 网络爬虫入门
网络爬虫是一种自动获取网页内容的程序。在Java中,我们可以使用前面学习的HTTP客户端和一些HTML解析库来实现简单的网络爬虫。
2.1 HTML解析
为了解析HTML内容,我们可以使用第三方库,如Jsoup。Jsoup是一个Java的HTML解析器,可以直接解析URL或HTML文本。
想象一下:如果说HTML页面是一本书,那么Jsoup就像是一个能够理解这本书结构的助手,它可以帮你找到书中的章节、段落和重要内容。
首先,我们需要添加Jsoup依赖。如果使用Maven,可以在pom.xml中添加:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
</dependency>
如果不使用Maven,可以从Jsoup官网下载JAR文件并添加到项目的类路径中。
下面是一个使用Jsoup解析网页的例子:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class JsoupDemo {
public static void main(String[] args) {
try {
// 从URL加载文档
Document doc = Jsoup.connect("https://news.baidu.com/")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
.timeout(5000)
.get();
// 获取页面标题
String title = doc.title();
System.out.println("页面标题: " + title);
// 使用CSS选择器选择元素
Elements newsLinks = doc.select(".hotnews a");
System.out.println("\n热门新闻链接:");
for (Element link : newsLinks) {
// 获取链接文本和URL
String linkText = link.text();
String linkHref = link.attr("href");
System.out.println(linkText + " - " + linkHref);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 实现简单的网络爬虫
下面是一个更完整的网络爬虫示例,它可以爬取一个网站的所有链接:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class SimpleCrawler {
// 已访问的URL集合
private Set<String> visitedUrls = new HashSet<>();
// 待访问的URL集合
private Set<String> urlsToVisit = new HashSet<>();
// 限制爬取的URL数量
private int maxUrlsToVisit = 100;
// 基础URL,用于解析相对URL
private String baseUrl;
public SimpleCrawler(String startUrl, int maxUrls) {
this.urlsToVisit.add(startUrl);
this.maxUrlsToVisit = maxUrls;
this.baseUrl = extractBaseUrl(startUrl);
}
private String extractBaseUrl(String url) {
// 简单地提取基础URL(域名部分)
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
int slashIndex = url.indexOf('/');
if (slashIndex != -1) {
url = url.substring(0, slashIndex);
}
return url;
}
public void crawl() {
while (!urlsToVisit.isEmpty() && visitedUrls.size() < maxUrlsToVisit) {
// 获取下一个要访问的URL
String currentUrl = urlsToVisit.iterator().next();
urlsToVisit.remove(currentUrl);
// 如果已经访问过,则跳过
if (visitedUrls.contains(currentUrl)) {
continue;
}
try {
// 访问URL并解析页面
System.out.println("正在爬取: " + currentUrl);
Document doc = Jsoup.connect(currentUrl)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
.timeout(5000)
.get();
// 标记为已访问
visitedUrls.add(currentUrl);
// 提取页面中的所有链接
Elements links = doc.select("a[href]");
for (Element link : links) {
String nextUrl = link.absUrl("href");
// 只爬取同一域名下的URL
if (nextUrl.contains(baseUrl) && !visitedUrls.contains(nextUrl)) {
urlsToVisit.add(nextUrl);
}
}
// 处理当前页面(这里只是打印标题)
System.out.println("标题: " + doc.title());
System.out.println("已爬取: " + visitedUrls.size() + " 个页面\n");
// 休眠一秒,避免请求过于频繁
Thread.sleep(1000);
} catch (IOException | InterruptedException e) {
System.out.println("爬取失败: " + currentUrl);
System.out.println("错误: " + e.getMessage());
}
}
System.out.println("爬取完成,共爬取了 " + visitedUrls.size() + " 个页面");
}
public static void main(String[] args) {
// 创建爬虫实例,从指定URL开始,最多爬取20个页面
SimpleCrawler crawler = new SimpleCrawler("https://www.baidu.com", 20);
crawler.crawl();
}
}
2.3 爬虫注意事项
在编写和使用网络爬虫时,需要注意以下几点:
- 尊重robots.txt:这是网站用来告诉爬虫哪些页面可以爬取,哪些不可以的文件。
- 控制爬取速度:不要在短时间内发送大量请求,这可能会被视为攻击行为。
- 识别自己:在请求头中设置合适的User-Agent,表明你的爬虫身份。
- 处理异常:网络爬虫可能会遇到各种异常情况,如连接超时、页面不存在等。
- 遵守法律法规:不要爬取和使用违反法律法规的内容。
3. 邮件发送
Java提供了JavaMail API来发送和接收电子邮件。在Java EE中,JavaMail是标准组件;在Java SE中,需要额外添加依赖。
3.1 JavaMail基础
想象一下:如果说普通的邮件客户端(如Outlook或Gmail)是我们用来发送邮件的工具,那么JavaMail就像是一个程序化的邮件助手,它可以按照我们的指令自动发送和接收邮件。
首先,我们需要添加JavaMail依赖。如果使用Maven,可以在pom.xml中添加:
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
如果不使用Maven,可以从JavaMail官网下载JAR文件并添加到项目的类路径中。
3.2 发送简单邮件
下面是一个发送简单文本邮件的例子:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
public class SendEmailDemo {
public static void main(String[] args) {
// 发件人邮箱和密码(或授权码)
final String username = "your_email@example.com";
final String password = "your_password_or_app_password";
// 收件人邮箱
String toEmail = "recipient@example.com";
// 设置邮件服务器属性
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.example.com"); // 如:smtp.gmail.com
props.put("mail.smtp.port", "587"); // 常用端口:587或465
// 创建会话
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
// 创建邮件消息
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(username));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(toEmail));
message.setSubject("测试邮件");
message.setText("你好,这是一封测试邮件!");
// 发送邮件
Transport.send(message);
System.out.println("邮件发送成功!");
} catch (MessagingException e) {
System.out.println("邮件发送失败: " + e.getMessage());
e.printStackTrace();
}
}
}
3.3 发送HTML邮件和附件
在实际应用中,我们可能需要发送HTML格式的邮件或带有附件的邮件:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
public class SendHtmlEmailWithAttachment {
public static void main(String[] args) {
// 发件人邮箱和密码(或授权码)
final String username = "your_email@example.com";
final String password = "your_password_or_app_password";
// 收件人邮箱
String toEmail = "recipient@example.com";
// 设置邮件服务器属性
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.example.com");
props.put("mail.smtp.port", "587");
// 创建会话
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
// 创建邮件消息
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(username));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(toEmail));
message.setSubject("HTML邮件测试");
// 创建多部分消息
Multipart multipart = new MimeMultipart();
// 创建HTML内容部分
MimeBodyPart htmlPart = new MimeBodyPart();
String htmlContent =
"<h1>你好</h1>" +
"<p>这是一封<b>HTML格式</b>的测试邮件!</p>" +
"<p>你可以在这里添加<a href='https://www.example.com'>链接</a>。</p>";
htmlPart.setContent(htmlContent, "text/html; charset=utf-8");
multipart.addBodyPart(htmlPart);
// 创建附件部分
MimeBodyPart attachmentPart = new MimeBodyPart();
String filename = "test.txt"; // 附件文件路径
attachmentPart.attachFile(filename);
attachmentPart.setFileName("测试文件.txt"); // 附件显示的文件名
multipart.addBodyPart(attachmentPart);
// 设置邮件内容为多部分内容
message.setContent(multipart);
// 发送邮件
Transport.send(message);
System.out.println("HTML邮件(带附件)发送成功!");
} catch (Exception e) {
System.out.println("邮件发送失败: " + e.getMessage());
e.printStackTrace();
}
}
}
3.4 使用第三方邮件服务
在实际应用中,我们通常会使用第三方邮件服务来发送邮件,如Gmail、QQ邮箱等。不同的邮件服务可能有不同的配置要求,下面是一些常见邮件服务的配置示例:
Gmail
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
注意:使用Gmail发送邮件时,可能需要在Google账户设置中启用"不够安全的应用访问权限",或者使用应用专用密码。
QQ邮箱
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // QQ邮箱需要SSL
props.put("mail.smtp.host", "smtp.qq.com");
props.put("mail.smtp.port", "465");
注意:使用QQ邮箱发送邮件时,密码应该使用授权码,而不是QQ密码。可以在QQ邮箱设置中获取授权码。
总结
在本章的第二部分中,我们学习了Java网络编程的更多高级内容,包括HTTP客户端、网络爬虫入门和邮件发送。这些知识使我们能够开发更复杂、更实用的网络应用程序。
通过HTTP客户端,我们可以与Web服务器进行交互,获取和发送数据;通过网络爬虫,我们可以自动获取和分析网页内容;通过JavaMail,我们可以在应用程序中实现邮件发送功能。
这些技术在实际应用中非常有用,例如:开发Web API客户端、数据采集工具、自动化报告系统等。
练习
- 编写一个程序,使用HttpClient获取指定网页的内容,并统计页面中出现的特定单词的次数。
- 实现一个简单的网络爬虫,爬取某个新闻网站的最新新闻标题和链接。
- 编写一个程序,定时发送邮件报告系统状态(如CPU使用率、内存使用情况等)。
- 实现一个HTTP文件上传客户端,可以将本地文件上传到指定的服务器。
- 创建一个邮件客户端应用,支持发送HTML格式的邮件和添加多个附件。