零基础学Java——第八章:Java网络编程(下)
第八章: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");// 打开连接并转换为HttpURLConnectionHttpURLConnection 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 {// 创建HttpClientHttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2) // 使用HTTP/2.connectTimeout(Duration.ofSeconds(5)) // 设置连接超时.build();// 创建HttpRequestHttpRequest 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) {// 获取链接文本和URLString 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,用于解析相对URLprivate 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) {// 获取下一个要访问的URLString 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");// 只爬取同一域名下的URLif (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.comprops.put("mail.smtp.port", "587"); // 常用端口:587或465// 创建会话Session session = Session.getInstance(props, new Authenticator() {@Overrideprotected 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() {@Overrideprotected 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格式的邮件和添加多个附件。