如何写一个自动投票工具

当然,自动投票这个违反了正常竞争,不提倡,这里只就技术问题做个探讨。

投票服务器一般的验证条件:
  • IP地址不能重复
  • Cookie验证
  • 验证码验证

一个自动投票工具可能涉及的问题:
  • 自动变换IP地址
  • 识别验证码
  • 伪装成浏览器提交


自动变换IP地址

一种直接的办法就是控制ADSL的链接状态来实现IP变换,具体可以参考 这里。但如果你使用的不是ADSL,那么更直接的办法就是使用代理了。

首先你需要一个有效代理列表,可以从网上专门提供代理网站收集,也可以有专门的工具,比如商易代理IP获取器。如果是网站获取,你可以写个页面解析程序,分页获取;如果是软件,在获取可用代理后,一般可以直接导成txt文本。

接下来就是要测试代理的可用性,一般可用代理不到采集下来的1/10。
测试代理的有效性:
static String myIP = "X.X.X.X";//自己原来的对外IP
private static boolean testProxy(String ip, String port) {
		System.getProperties().setProperty("http.proxyHost", ip);
		System.getProperties().setProperty("http.proxyPort", port);

		InputStream in = null;
		try {
			in = new URL("http://iframe.ip138.com/ic.asp").openStream();
			String string = IOUtils.toString(in, "GBK");
			System.out.println(string);

			return string.indexOf(myIP) < 0;
		} catch (Exception e) {
			return false;
		} finally {
			if (in != null)
				IOUtils.closeQuietly(in);
		}
	}



伪装成浏览器提交

这里是斗智斗勇的时候,有时候投票服务器会使用一些小手段来阻碍模拟者,你需要伪装成一个普通浏览器,并具有和用户操作一样的行为。

你可以使用firefox(firebug)或是chrome的调试器查看正常投票的网络信息,从而进行模拟。

一般的伪装:
		DefaultHttpClient httpclient = new DefaultHttpClient();
		// 代理的设置
		HttpHost proxy = new HttpHost(ip, Integer.parseInt(port));
		httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
				proxy);
		httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
				CookiePolicy.BROWSER_COMPATIBILITY);
		httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,				"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0");
		httpclient.getParams().setParameter(	CoreProtocolPNames.HTTP_CONTENT_CHARSET, "GBK");

HttpPost httppost = new HttpPost("http://XXX");

		httppost.addHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0");
		httppost.addHeader("Host", "XXX);
		httppost.addHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
		httppost.addHeader("Accept-Encoding", "gzip, deflate");
		httppost.addHeader("Accept-Language","zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
		httppost.addHeader("Cache-Control", "max-age=0");
		httppost.addHeader("Connection", "keep-alive");
		httppost.addHeader("Referer","http://XXX");



识别验证码

这个是个大话题,识别验证码方法很多,比较有效的方法是采用 tesseract-ocr。一般的验证码都不在话下,个别的需要自己训练一下,可以参考 这里
一般的用法:
tesseract test.jpg out -l eng digits

test.jpg为验证码图片,out为识别的验证码存放的txt文件名,-l eng为指定语言,也可以下载支持中文的包识别中文,digits为指定只识别数字,这样对于只是数字的验证码可以提高识别率。

如果你发现tesseract提示empty page!!,则需要修改一下参数:
Here's a summary of compression support and limitations:
    - All formats except JPEG support 1 bpp binary.
    - All formats support 8 bpp grayscale (GIF must have a colormap).
    - All formats except GIF support 24 bpp rgb color.
    - All formats except PNM support 8 bpp colormap.
    - PNG and PNM support 2 and 4 bpp images.
    - PNG supports 2 and 4 bpp colormap, and 16 bpp without colormap.
    - PNG, JPEG, TIFF and GIF support image compression; PNM and BMP do not.
    - WEBP supports 24 bpp rgb color.

比如最后的指令可能为:
tesseract test.jpg out -l eng -psm 7 digits


tesseract识别格式有限,有的网站的验证码格式是bmp或其他,这个时候需要先获取下来byte[],保存成原有格式,然后再转化成支持的格式,比如jpg,tif等。转化工作可以使用 JAI(Java Advanced Imaging API)

对于有的验证码,还需要做一些前期处理,比如去掉背景噪点,灰度化,增大对比度,去掉干扰线条等。

用java调用是必须的:
public static String doOCR(File imageFile) throws Exception {
		String result = "";
		File outputFile = new File(imageFile.getParentFile(), "output");
		StringBuffer strB = new StringBuffer();

		List<String> cmd = new ArrayList<String>();
		cmd.add(tessPath + "\\tesseract");
		cmd.add("");
		cmd.add(outputFile.getName());
		cmd.add(LANG_OPTION);
		cmd.add("eng");
		cmd.add("-psm");
		cmd.add("7");
		// cmd.add("nobatch");
		cmd.add("digits");
		
		ProcessBuilder pb = new ProcessBuilder();
		pb.directory(imageFile.getParentFile());

		cmd.set(1, imageFile.getName());
		System.out.println(cmd.toString());
		pb.command(cmd);
		pb.redirectErrorStream(true);
		Process process = pb.start();

		int w = process.waitFor();
		logger.debug("Exit value = {}", w);

		if (w == 0) {
			BufferedReader in = new BufferedReader(new InputStreamReader(
					new FileInputStream(outputFile.getAbsolutePath() + ".txt"),
					"UTF-8"));

			String str;

			while ((str = in.readLine()) != null) {
				strB.append(str).append(EOL);
			}
			in.close();
		} else {
			String msg;
			switch (w) {
			case 1:
				msg = "Errors accessing files. There may be spaces in your image's filename.";
				break;
			case 29:
				msg = "Cannot recognize the image or its selected region.";
				break;
			case 31:
				msg = "Unsupported image format.";
				break;
			default:
				msg = "Errors occurred.";
			}
			System.err.println("验证码获取失败:" + msg);
		}

		new File(outputFile.getAbsolutePath() + ".txt").delete();
		result = strB.toString().trim();
		return result;
	}

猜你喜欢

转载自yiqieanhao.iteye.com/blog/1923049