前言:其实,这个工具最开始是为了两位玩域名的朋友写的,虽然西部数码提供了一个自动化的域名检测工具(http://www.west.cn/web/mi/#),但是比较遗憾的是并没有提供.xin后缀的检测。因此,他们拜托我写下了这个小工具,在这个软件中对单个域名状态的检测我使用了万网提供的接口

注:文末我会放出源代码和打包好的工具的下载链接,同时还会分享几个我生成的实用字典

一 原理简介

       对大量域名的注册情况进行自动化检测,细分开来讲就是多次对单个域名的注册情况的检测。而对单个域名的检测我们可以使用万网提供的接口(http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=xxx.yy),具体细节可以参考我以前写过的一篇文章:http://www.zifangsky.cn/2015/11/java中的get和post请求,使用万网接口判断域名是否已被注/

       因此,在这篇文章里,我主要实现的是:(1)做了一个比较直观的图形化界面;(2)支持从外部导入字典文件进行自动化检测;(3)支持对多种后缀的检测,包括可以自定义后缀;(4)同时还实现了对探测结果的导出。

运行后的截图基本上是这样的:

wKiom1aHMuiwF_7yAAEA9H5gfLA068.png

       界面和导入导出功能不用多说,实现起来很简单,而且代码中有详细注释,一看就懂。这里需要说明的是,为了让检测的状态在界面中实时显示出来(左边的正在检测以及右边的可注册和超时域名),我选择了在一个新的线程里执行文件读取,联网查询这类比较费时的工作,同时使用了SwingUtilities.invokeLater来通知事件分发线程及时更新界面。具体原理和基本实现可参考我写的这篇文章:http://www.zifangsky.cn/2015/12/java中事件分发线程(edt)与swingutilities-invokelater相关总结/

二 具体的代码实现

package view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileSystemView;

public class MainView extends JFrame implements ActionListener, MouseListener{
	/**
	 * 自动化批量检测域名是否已注册
	 * @author zifangsky
	 * @blog http://www.zifangsky.cn
	 * @date 2015-12-30
	 * @version v1.0.0
	 */
	private static final long serialVersionUID = 1L;
	private GridBagLayout gridbag;
	private GridBagConstraints constraints;
	private JPanel mainJPanel,panel1,panel2,panel3,panel4,panel5,panel6,panel7;
	private JButton selectDic,beginCheck;  // 选择字典,开始检测
	private JLabel domainSuffix,custom,progress,result;  //域名后缀,自定义后缀,探测进度,结果
	private JFileChooser dicChooser;  //字典选择
	private JTextField customJTextField;  //自定义
	private JScrollPane progressPane,resultPane;  //进度面板和结果面板
	private JTextArea progressJtJTextArea,resultJTextArea;  //同上
	private JCheckBox[] suffixCheckBoxs = new JCheckBox[15];  //域名后缀多项选择框
	
	private JMenuBar jMenuBar;
	private JMenu help;
	private JMenuItem author,contact,version,readme;
	private JPopupMenu outPutData;  //导出数据
	private JMenuItem availableDomains,timeOutDomains,allDomains;  //可用域名,超时域名,全部域名
	
	private Font menuFont = new Font("宋体", Font.LAYOUT_NO_LIMIT_CONTEXT, 14);  //菜单字体
	private Font contentFont = new Font("宋体", Font.LAYOUT_NO_LIMIT_CONTEXT, 16);  //正文字体
	
	private String dicName = "",currentDomain = "";  //字典名字,当前检测域名
	private DomainsCheckThread myThread = null;  //查询线程
	private Runnable progre***unnable,resultRunnable,timedOutRunnable,endRunnable;  //更新页面线程
	
	public MainView(){
		super("自动化域名检测");
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		setPreferredSize(new Dimension(1000, 650));
		int frameWidth = this.getPreferredSize().width;  //界面宽度
		int frameHeight = this.getPreferredSize().height;  //界面高度
		setSize(frameWidth,frameHeight);
		setLocation((screenSize.width - frameWidth) / 2,(screenSize.height - frameHeight) / 2);
		
		//初始化
		mainJPanel = new JPanel();
		panel1 = new JPanel();
		panel2 = new JPanel();
		panel3 = new JPanel();
		panel4 = new JPanel();
		panel5 = new JPanel();
		panel6 = new JPanel();
		panel7 = new JPanel();
		selectDic = new JButton("导入字典文件");
		beginCheck = new JButton("开始检测");
		domainSuffix = new JLabel("域名后缀:");
		custom = new JLabel("自定义(以英文空格分割,如:.club .win):");
		progress = new JLabel("探测进度:");
		result = new JLabel("探测结果:");
		customJTextField = new JTextField("",70);
		progressPane = new JScrollPane();
		resultPane = new JScrollPane();
		progressJtJTextArea = new JTextArea(20, 20);
		resultJTextArea = new JTextArea(20, 20);
		suffixCheckBoxs[0] = new JCheckBox(".com");
		suffixCheckBoxs[1] = new JCheckBox(".cn");
		suffixCheckBoxs[2] = new JCheckBox(".com.cn");
		suffixCheckBoxs[3] = new JCheckBox(".org");
		suffixCheckBoxs[4] = new JCheckBox(".net");
		suffixCheckBoxs[5] = new JCheckBox(".me");
		suffixCheckBoxs[6] = new JCheckBox(".cc");
		suffixCheckBoxs[7] = new JCheckBox(".xyz");
		suffixCheckBoxs[8] = new JCheckBox(".top");
		suffixCheckBoxs[9] = new JCheckBox(".xin");
		suffixCheckBoxs[10] = new JCheckBox(".biz");
		suffixCheckBoxs[11] = new JCheckBox(".tv");
		suffixCheckBoxs[12] = new JCheckBox(".ren");
		suffixCheckBoxs[13] = new JCheckBox(".wang");
		suffixCheckBoxs[14] = new JCheckBox(".link");
		suffixCheckBoxs[0].setSelected(true);  //第一个默认选中
		
		//布局
		gridbag = new GridBagLayout();
		constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.BOTH;
		mainJPanel.setLayout(gridbag);
		
		constraints.gridwidth = 0;  //该方法是设置组件水平所占用的格子数,如果为0,就说明该组件是该行的最后一个
		constraints.gridheight = 1;
		constraints.weightx = 1;  //该方法设置组件水平的拉伸幅度,如果为0就说明不拉伸,不为0就随着窗口增大进行拉伸,0到1之间
		constraints.weighty = 0;  //该方法设置组件垂直的拉伸幅度,如果为0就说明不拉伸,不为0就随着窗口增大进行拉伸,0到1之间
		gridbag.setConstraints(panel1, constraints);
		mainJPanel.add(panel1);
		
		constraints.gridheight = 2;
		gridbag.setConstraints(panel2, constraints);
		mainJPanel.add(panel2);
		
		constraints.gridheight = 1;
		gridbag.setConstraints(panel3, constraints);
		mainJPanel.add(panel3);
		
		gridbag.setConstraints(panel4, constraints);
		mainJPanel.add(panel4);
		
		constraints.weightx = 1;
		constraints.weighty = 1;
		gridbag.setConstraints(panel5, constraints);
		mainJPanel.add(panel5);
		
		panel1.setLayout(new FlowLayout(FlowLayout.CENTER));
		selectDic.setFont(contentFont);
		panel1.add(selectDic);
		panel2.setLayout(new FlowLayout(FlowLayout.LEFT));
		domainSuffix.setFont(contentFont);
		panel2.add(domainSuffix);
		for(int i=0;i<15;i++){
			suffixCheckBoxs[i].setFont(contentFont);
			panel2.add(suffixCheckBoxs[i]);
		}
		panel3.setLayout(new FlowLayout(FlowLayout.LEFT));
		custom.setFont(contentFont);
		panel3.add(custom);
		customJTextField.setFont(contentFont);
		panel3.add(customJTextField);
		panel4.setLayout(new FlowLayout(FlowLayout.CENTER));
		beginCheck.setFont(contentFont);
		panel4.add(beginCheck);
		panel5.setLayout(new GridLayout(1, 2));
		panel5.add(panel6);
		panel5.add(panel7);
		panel6.setLayout(new BorderLayout());
		progress.setFont(contentFont);
		progress.setHorizontalAlignment(JLabel.CENTER);
		panel6.add(progress,BorderLayout.NORTH);
		panel6.add(progressPane,BorderLayout.CENTER);
		progressJtJTextArea.setFont(contentFont);
		progressPane.setViewportView(progressJtJTextArea);
		progressJtJTextArea.setEditable(false);
		progressJtJTextArea.setLineWrap(true);
		progressJtJTextArea.setWrapStyleWord(true);
		panel7.setLayout(new BorderLayout());
		result.setFont(contentFont);
		result.setHorizontalAlignment(JLabel.CENTER);
		panel7.add(result,BorderLayout.NORTH);
		panel7.add(resultPane,BorderLayout.CENTER);
		resultJTextArea.setFont(contentFont);
		resultPane.setViewportView(resultJTextArea);
		resultJTextArea.setEditable(false);
		resultJTextArea.setLineWrap(true);
		resultJTextArea.setWrapStyleWord(true);
		
		//菜单
		jMenuBar = new JMenuBar();
		help = new JMenu("帮助");
		author = new JMenuItem("作者");
		contact = new JMenuItem("联系方式");
		version = new JMenuItem("版本号");
		readme = new JMenuItem("说明");
		help.setFont(menuFont);
		jMenuBar.add(help);
		author.setFont(menuFont);
		help.add(author);
		contact.setFont(menuFont);
		help.add(contact);
		version.setFont(menuFont);
		help.add(version);
		readme.setFont(menuFont);
		help.add(readme);
		
		//鼠标右键导出菜单
		outPutData = new JPopupMenu();
		availableDomains = new JMenuItem("导出可注册域名");
		availableDomains.setFont(menuFont);
		outPutData.add(availableDomains);
		timeOutDomains = new JMenuItem("导出超时域名");
		timeOutDomains.setFont(menuFont);
		outPutData.add(timeOutDomains);
		allDomains = new JMenuItem("导出所有结果域名");
		allDomains.setFont(menuFont);
		outPutData.add(allDomains);
		
		add(mainJPanel);
		setJMenuBar(jMenuBar);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//点击事件
		selectDic.addActionListener(this);
		beginCheck.addActionListener(this);
		author.addActionListener(this);
		contact.addActionListener(this);
		version.addActionListener(this);
		readme.addActionListener(this);
		availableDomains.addActionListener(this);
		timeOutDomains.addActionListener(this);
		allDomains.addActionListener(this);
		
		//鼠标事件
		resultJTextArea.addMouseListener(this);
		
		//组件更新线程
		progre***unnable = new Runnable() {
			public void run() {
				progressJtJTextArea.setEditable(true);
				progressJtJTextArea.append("正在检测:" + currentDomain + "\n");	
				progressJtJTextArea.setEditable(false);
				//设置显示最新内容
				progressJtJTextArea.selectAll();
				progressJtJTextArea.setCaretPosition(progressJtJTextArea.getSelectionEnd());
			}
		};
		resultRunnable = new Runnable() {
			public void run() {
				resultJTextArea.setEditable(true);
				resultJTextArea.append(currentDomain + "    可以注册\n");
				resultJTextArea.setEditable(false);
				resultJTextArea.selectAll();
				resultJTextArea.setCaretPosition(resultJTextArea.getSelectionEnd());
			}
		};
		timedOutRunnable = new Runnable() {
			public void run() {
				resultJTextArea.setEditable(true);
				resultJTextArea.append(currentDomain + "    超时\n");
				resultJTextArea.setEditable(false);		
				resultJTextArea.selectAll();
				resultJTextArea.setCaretPosition(resultJTextArea.getSelectionEnd());
			}
		};
		endRunnable = new Runnable() {
			public void run() {
				JOptionPane.showMessageDialog(null, "任务全部执行完毕","提示:",JOptionPane.INFORMATION_MESSAGE);		
			}
		};
	}
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new MainView();
			}
		});
	}

	/**
	 * 处理点击事件
	 * */
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == selectDic){
			dicChooser = new JFileChooser();
			dicChooser.setFont(contentFont);
			FileSystemView fileSystemView = FileSystemView.getFileSystemView();
			dicChooser.setCurrentDirectory(fileSystemView.getHomeDirectory());
			dicChooser.setDialogTitle("请选择字典文件:");
			dicChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
			int choose = dicChooser.showDialog(null, "打开");
			if(choose == JFileChooser.APPROVE_OPTION){
				File newFile = dicChooser.getSelectedFile();
				if(newFile != null)
					dicName = newFile.getAbsolutePath();  //字典文件名			
			}
		}
		else if(e.getSource() == beginCheck){
			if(myThread != null)
				return ;
			progressJtJTextArea.setText("");
			resultJTextArea.setText("");
			if(dicName == null || "".equals(dicName)){
				JOptionPane.showMessageDialog(this, "请先选择一个TXT格式的字典文件!!!","警告:",JOptionPane.ERROR_MESSAGE);
				return ;
			}
			//获取选择的后缀
			Set<String> suffixSet = new LinkedHashSet<String>();
			for(int i=0;i<15;i++){
				if(suffixCheckBoxs[i].isSelected())
					suffixSet.add(suffixCheckBoxs[i].getText());
			}
			String customSuffix = customJTextField.getText();  //获取自定义域名后缀
			if(!"".equals(customSuffix)){
				String[] customs = customSuffix.split(" ");
				for(String tString : customs){
					if(!"".equals(tString))
						suffixSet.add(tString);
				}
			}
			
			if(suffixSet.isEmpty()){
				JOptionPane.showMessageDialog(this, "请先选择一个或多个的域名后缀!!!","警告:",JOptionPane.ERROR_MESSAGE);				
				return;
			}
			//开启新的线程,读字典文件,并且查询
			myThread = new DomainsCheckThread(suffixSet);
			Thread thread = new Thread(myThread);
			thread.start();
		}
		else if(e.getSource() == author){
			JOptionPane.showMessageDialog(this, "zifangsky","作者:",JOptionPane.INFORMATION_MESSAGE);
		}
		else if(e.getSource() == contact){
			JOptionPane.showMessageDialog(this, "邮箱:admin@zifangsky.cn\n" +
					"博客:http://www.zifangsky.cn","联系方式:",JOptionPane.INFORMATION_MESSAGE);
		}
		else if(e.getSource() == version){
			JOptionPane.showMessageDialog(this, "v1.0.0","版本号:",JOptionPane.INFORMATION_MESSAGE);
		}
		else if(e.getSource() == readme){
			JOptionPane.showMessageDialog(this, "本程序是判断域名是否可以注册的自动化查询工具,使用的是万网的接口。\n" +
					"使用方式简单,只需要导入相关字典文件即可。\n" +
					"目前还不太完善,仅仅只是单线程的。等过段时间有空了,我或许会将之升级成多线程版。\n" +
					"另外,源码已经开放,需要源码进行自行研究的可自行移步到我的个人博客网站。","说明:",JOptionPane.INFORMATION_MESSAGE);
		}
		else if(e.getSource() == availableDomains){
			Pattern pattern = Pattern.compile("(.*?)\\s*可以注册");
			resultOutPut(pattern,1);
			
		}
		else if(e.getSource() == timeOutDomains){
			Pattern pattern = Pattern.compile("([^\\s]*?)    超时");
			resultOutPut(pattern,2);
		}
		else if(e.getSource() == allDomains){
			Pattern pattern = Pattern.compile("([^\\s]*?)    ");
			resultOutPut(pattern,3);
		}
		
	}
	
	/**
	 * 查询到的结果进行导出,根据不同的正则表达式分为:导出可注册的,导出超时的,导出所有的
	 * @param pattern 导出操作的正则表达式
	 * @param state 状态:可注册-->1;超时-->2;所有-->3
	 * @return null
	 * */
	private void resultOutPut(Pattern pattern,int state){
		String[] data = resultJTextArea.getText().split("\n");
		
		Date date = new Date();
		Format format = new SimpleDateFormat("HHmmss");
		String fileName = "";
		if(state == 1)
			fileName = "可注册域名导出列表" + format.format(date) + ".txt";
		else if(state == 2)
			fileName = "超时域名导出列表" + format.format(date) + ".txt";
		else if(state == 3)
			fileName = "所有结果域名导出列表" + format.format(date) + ".txt";
		
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(new File(fileName)));
			Matcher matcher = null;
			for(String temp : data){
				matcher = pattern.matcher(temp);
				if(matcher.find()){
					writer.write(matcher.group(1));
					writer.newLine();
					writer.flush();
				}				
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 查询线程,用的万网的接口
	 * */
	class DomainsCheckThread implements Runnable{
		private Set<String> suffixSet = new LinkedHashSet<String>();
		
		public DomainsCheckThread(Set<String> suffixSet) {
			this.suffixSet = suffixSet;
		}

		public void run() {
			//读字典
			try {
				BufferedReader reader = new BufferedReader(new FileReader(new File(dicName)));
				String line = "";			
				while((line = reader.readLine()) != null){				
					if(!"".equals(line.trim())){
						Iterator<String> iterator = suffixSet.iterator();
						while(iterator.hasNext()){
							currentDomain = line.trim() + iterator.next();
							SwingUtilities.invokeLater(progre***unnable);  //更新状态
							checkDomain(currentDomain);  //开始查询
							try {
								Thread.sleep(1000);  //单线程,并且每次查询完毕暂停1秒
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
				reader.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//任务结束,参数初始化
			dicName = "";
			currentDomain = "";
			myThread = null;
			SwingUtilities.invokeLater(endRunnable);  //结束通知
		}
		
		/**
		 * 对单个域名向万网的接口发起请求,检测注册情况
		 * @param domain 域名
		 * @return null
		 * */
		public void checkDomain(String domain){
			try {
				URL url = new URL("http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=" + domain);
				HttpURLConnection connection = (HttpURLConnection) url.openConnection();
				connection.setRequestMethod("GET");
				connection.setConnectTimeout(3000);  //毫秒
				connection.setReadTimeout(3000);
				
				if(connection.getResponseCode() == 200){
					InputStream inputStream = new BufferedInputStream(connection.getInputStream());				
					BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
					String line = "";
					String reg = "<original>(.*?)</original>";
					while((line = reader.readLine()) != null){
						if(line.matches(reg)){
							//211表示不可用
							String state = line.substring(10, 13);
							if(!"211".equals(state)){
								//该域名未被使用,更新状态
								SwingUtilities.invokeLater(resultRunnable);
							}
												
						}
					}
					reader.close();
					inputStream.close();
				}
				connection.disconnect();
			}  catch (IOException e) {
				//超时,更新状态
				SwingUtilities.invokeLater(timedOutRunnable);
			}		
		}
		
	}

	/**
	 * 鼠标点击
	 * */
	public void mouseClicked(MouseEvent e) {
		//鼠标右键点击探测结果面板时,弹出数据导出菜单
		if(e.getButton() ==MouseEvent.BUTTON3){
			outPutData.show(resultJTextArea, e.getX(), e.getY());		
		}
	}

	public void mousePressed(MouseEvent e) {
		
	}

	public void mouseReleased(MouseEvent e) {
		
	}

	public void mouseEntered(MouseEvent e) {
		
	}

	public void mouseExited(MouseEvent e) {
		
	}

}

三 效果测试

wKioL1aHM2SCKQv4AAD5CUAwxH0053.png

wKioL1aHM2agsqfmAAEsw4m6Kbc695.png

注:其实这个软件仅仅只是一个初始版本,还很不完善。因为万网的接口是免费的原因,因此不仅不能使用多线程,而且我还人为的在每次请求后暂停了一秒钟。后期我根据情况或许会将这个软件升级成多线程版的,尽请期待


附:

源代码和软件的下载链接:链接:http://pan.baidu.com/s/1eQNvr8I 密码:rw4h

为方便米农,我还用代码生成了几个实用的字典:

wKiom1aHM3zTRITxAABnoOhHgjA310.png

字典文件的下载链接:http://pan.baidu.com/s/1boplCEN