零基础学Java——第八章:Java网络编程(下)

第八章:Java网络编程(下)

在上一部分中,我们学习了Java网络编程的基础知识,包括网络通信的基本概念、Socket编程和URL处理。在这一部分中,我们将继续深入学习更多高级的网络编程内容,包括HTTP客户端、网络爬虫入门和邮件发送等。

1. HTTP客户端

HTTP(超文本传输协议)是互联网上最常用的协议之一,用于在Web浏览器和网站服务器之间传输数据。Java提供了多种方式来实现HTTP客户端。

1.1 使用HttpURLConnection

HttpURLConnectionURLConnection的子类,专门用于处理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 爬虫注意事项

在编写和使用网络爬虫时,需要注意以下几点:

  1. 尊重robots.txt:这是网站用来告诉爬虫哪些页面可以爬取,哪些不可以的文件。
  2. 控制爬取速度:不要在短时间内发送大量请求,这可能会被视为攻击行为。
  3. 识别自己:在请求头中设置合适的User-Agent,表明你的爬虫身份。
  4. 处理异常:网络爬虫可能会遇到各种异常情况,如连接超时、页面不存在等。
  5. 遵守法律法规:不要爬取和使用违反法律法规的内容。

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客户端、数据采集工具、自动化报告系统等。

练习

  1. 编写一个程序,使用HttpClient获取指定网页的内容,并统计页面中出现的特定单词的次数。
  2. 实现一个简单的网络爬虫,爬取某个新闻网站的最新新闻标题和链接。
  3. 编写一个程序,定时发送邮件报告系统状态(如CPU使用率、内存使用情况等)。
  4. 实现一个HTTP文件上传客户端,可以将本地文件上传到指定的服务器。
  5. 创建一个邮件客户端应用,支持发送HTML格式的邮件和添加多个附件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值