前言:其实,这个工具最开始是为了两位玩域名的朋友写的,虽然西部数码提供了一个自动化的域名检测工具(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)同时还实现了对探测结果的导出。
运行后的截图基本上是这样的:
界面和导入导出功能不用多说,实现起来很简单,而且代码中有详细注释,一看就懂。这里需要说明的是,为了让检测的状态在界面中实时显示出来(左边的正在检测以及右边的可注册和超时域名),我选择了在一个新的线程里执行文件读取,联网查询这类比较费时的工作,同时使用了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 progressRunnable,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); //组件更新线程 progressRunnable = 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, "邮箱:[email protected]\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(progressRunnable); //更新状态 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) { } }
三 效果测试
注:其实这个软件仅仅只是一个初始版本,还很不完善。因为万网的接口是免费的原因,因此不仅不能使用多线程,而且我还人为的在每次请求后暂停了一秒钟。后期我根据情况或许会将这个软件升级成多线程版的,尽请期待
附:
源代码和软件的下载链接:链接:http://pan.baidu.com/s/1eQNvr8I 密码:rw4h
为方便米农,我还用代码生成了几个实用的字典:
字典文件的下载链接:http://pan.baidu.com/s/1boplCEN