登入的網站是3g.renren.com 因為是手機人人, 頁面比較簡單
首先用HttpGet取出"http://3g.renren.com"的html代碼, 是用Jsoup解析出登入表單, 包括驗證碼的圖片的url
因為沒法做到繞過驗證碼,所以用驗證碼的url構建一個image, 顯示出來讓使用者自己填寫
構建image時一定要用httpget, 開始使用了ImageIO.read(new URL(url)); 這樣, HttpClient執行個體中沒有管理session
不寫了, 全放到注釋裡去了, 直接上代碼
因為程式很依賴html源碼, 哪天人人的前台改動了html代碼說不定就用不了了
package com.renren.main;import java.awt.Graphics;import java.awt.Image;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.image.BufferedImage;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.imageio.ImageIO;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.JPasswordField;import javax.swing.JTextArea;import javax.swing.JTextField;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.NameValuePair;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.ResponseHandler;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.BasicResponseHandler;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.message.BasicNameValuePair;import org.apache.http.protocol.HTTP;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;public class Login extends JFrame implements ActionListener{private JTextField email;private JPasswordField password;private JTextField verifycode;private JButton login;private ImageBoxPanel imageBox;private Image image;private MsgBox box;private final HttpClient client;private HttpPost post;private HttpGet get;private HttpResponse response;private ResponseHandler<String> responseHandler;private Map<String, String> form_map;private boolean flag;//有沒有驗證碼private String html;public Login() {super("人人登入");client = new DefaultHttpClient();responseHandler = new BasicResponseHandler();form_map = new HashMap<String, String>();Object obj;setLayout(null);setDefaultCloseOperation(EXIT_ON_CLOSE);setResizable(false);email = new JTextField("<email>");password = new JPasswordField("<password>");verifycode = new JTextField();login = new JButton("登入");login.addActionListener(this);html = view("http://3g.renren.com");init();try {imageBox = new ImageBoxPanel(createBufferedImage());} catch (Exception e) {e.printStackTrace();imageBox = new ImageBoxPanel();}//layoutthis.setBounds(500, 300, 280, 220);email.setBounds(10, 10, 250, 30);password.setBounds(10, 50, 250, 30);verifycode.setBounds(10, 90, 150, 30);imageBox.setBounds(205, 80, 54, 46);login.setBounds(10, 130, 250, 30);add(email);add(password);add(verifycode);add(imageBox);add(login);setVisible(true);}private BufferedImage createBufferedImage() {InputStream inputstream=null;try {String source = view("http://3g.renren.com");String url = getVerifycodeUrl(source);if("error".equals(url)) {return null;}///rndimg?post=_REQUESTFRIEND_de6073b242a6fc34b67d228abf982916&rnd=1335096399071//分析登入表單可以發現如果某次登入需要驗證碼, 則表單中會有verifykey和verifycode//而verifykey 的值正好是驗證碼地址中的一部分(中間的32位字元), 所以把verifykey取出來String key = url;key = key.replaceAll("http[\\d\\D]*ND_", "");key = key.replaceAll("&[\\d\\D]*", "");form_map.put("verifykey", key);//更新下verifykey,和當前驗證碼對應System.out.println(key);get = new HttpGet(url);response = client.execute(get);if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {//以下兩個if是網上摘來得HttpEntity entity = response.getEntity();if (entity != null) {inputstream = entity.getContent();//本來返回的是一個InputStream, 但是在finally中調用get.abort()後好像會變成null, 沒辦法, 所以直接構造出BufferedImage返回return ImageIO.read(inputstream);}}} catch (Exception e) {e.printStackTrace();} finally {get.abort();}return null;}/** * 擷取某個url的html代碼 * @param url * @return */private String view(String url) {String html;try {get = new HttpGet("http://3g.renren.com/");html = client.execute(get, responseHandler);} catch (Exception e) {e.printStackTrace();html = "error";} finally {get.abort();}return html;}/** * 擷取驗證碼圖片的地址 * @param source 某個頁面的頁面原始碼 * @return */private String getVerifycodeUrl(String source) {String url;flag = true;try {Document doc = Jsoup.parse(source);//分析表單可知此句可用, 不過用這種方法來做比較不好的一點就是一旦人人頁面稍微改動下, 這個程式就可能用不了了Element e = doc.getElementsByAttributeValueContaining("alt", "此處為驗證碼").get(0);url = e.attr("src");url = "http://3g.renren.com" + url;} catch (Exception e) {//本來會列印異常資訊, 不過看著不舒服, 就刪了//大致就是有時沒有驗證碼, 那麼上面的get(0)肯定就行不通了System.out.println("沒有驗證碼~");url = "error";flag = false;//標記有沒有驗證碼, 可以讓verifykey 和 verifycode 兩個屬性是否通過表單傳過去}return url;}private void init() {String html = view("http://3g.renren.com");Document doc = Jsoup.parse(html);//取出3g.renren.com代碼Element form = doc.getElementsByTag("form").get(0);String action = form.attr("action");form_map.put("action", action);Elements es = form.getElementsByTag("input");for(Element e: es) {form_map.put(e.attr("name"), e.attr("value"));}}@Overridepublic void actionPerformed(ActionEvent e) {login();System.out.println(view("http://3g.renren.com"));//登入成功後顯示發心情的介面, 主要實現了個功能, 沒有考慮到其他的方面//包括是否登入成功啊, 是否發心情成功失敗啊//不過這些還是挺簡單的, 無非就是解析post後的html代碼, 看會不會出現哪些錯誤資訊box = new MsgBox();}private boolean login() { post = new HttpPost(form_map.get("action")); //據說有些網站如果不設定頭會被過濾掉, 畢竟人人還是蠻大的一個網站, 就加上吧 post.setHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3"); List<NameValuePair> nvp = new ArrayList<NameValuePair>();nvp.add(new BasicNameValuePair("origURL", form_map.get("origURL")));nvp.add(new BasicNameValuePair("lbskey", form_map.get("lbskey")));nvp.add(new BasicNameValuePair("c", form_map.get("c")));nvp.add(new BasicNameValuePair("ref", form_map.get("ref")));nvp.add(new BasicNameValuePair("email", email.getText()));nvp.add(new BasicNameValuePair("password", new String(password.getPassword())));nvp.add(new BasicNameValuePair("pq", form_map.get("pq")));if(flag) {nvp.add(new BasicNameValuePair("verifycode", verifycode.getText()));nvp.add(new BasicNameValuePair("verifykey", form_map.get("verifykey")));}//System.out.println(form_map.get("verifykey")); try { post.setEntity(new UrlEncodedFormEntity(nvp, HTTP.UTF_8)); //response = client.execute(post); client.execute(post); } catch (Exception e) { e.printStackTrace(); return false; } finally { post.abort(); } return true; }public static void main(String[] args) {Login renren = new Login();//System.out.println(renren.view("http://3g.renren.com"));}class MsgBox extends JFrame implements ActionListener {JTextArea msg;JButton submit;//某條心情發送次數, 本來想搞個刷屏的, 不過不太給力, 設定了休眠一段時間還是可以成功的JTextField time;public MsgBox() {setLayout(null);setResizable(false);setBounds(500, 500, 365, 125);msg = new JTextArea();submit = new JButton("發送~");time = new JTextField("1");msg.setBounds(10, 10, 250, 80);time.setBounds(270, 10, 81, 40);submit.setBounds(270, 50, 80, 40);submit.addActionListener(this);add(msg);add(submit);add(time);setVisible(true);}@Overridepublic void actionPerformed(ActionEvent e) {for(int i = 0; i < Integer.parseInt(time.getText()); i++) {post = new HttpPost("http://3g.renren.com/status/wUpdateStatus.do");//反正都是從先認為的登入, 再把一些資訊抓下來post.setHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3");List<NameValuePair> nvp = new ArrayList<NameValuePair>();nvp.add(new BasicNameValuePair("_rtk", "xxxxxxxx"));//這個不知道是怎麼產生的, 可能每一個id都有對應一個把nvp.add(new BasicNameValuePair("sour", ""));nvp.add(new BasicNameValuePair("loginbybm", ""));nvp.add(new BasicNameValuePair("status", msg.getText() + i));//其他幾個都無關緊要, 不過還是留著nvp.add(new BasicNameValuePair("pid", ""));nvp.add(new BasicNameValuePair("empty", "1")); try { post.setEntity(new UrlEncodedFormEntity(nvp, HTTP.UTF_8)); response = client.execute(post); System.out.println(response); Thread.sleep(1000L);//沒有設定休眠應該是被人人過濾掉了,具體多少時間間隔可以發一次沒有測試過, 不過估計500毫秒應該是沒有問題的吧 } catch (Exception e1) { e1.printStackTrace(); } finally { post.abort(); }}}}}class ImageBoxPanel extends JPanel {private Image image;public ImageBoxPanel(Image image) {this.image = image;}public ImageBoxPanel() {}@Overrideprotected void paintComponent(Graphics g) {g.drawImage(image, 0, 0, 54, 46, null);}}