报名机器人

类别:Delphi 点击:0 评论:0 推荐:
报名机器人

昨天帮人报一个参加日语能力考试的朋友报名,无奈报名比较火暴,到处名额已满,需要不断重试碰运气。想到本科的时候,老师组织我们去上机,结果是帮人报TOFEL的名,那场景,如打仗一般,大家先对表,精确到秒。先把表单的数据填好,等时间一到,马上按提交按钮。早了不行,要重定向回来充填好几项数据耽误时间,晚了也不行,要不就是报满,要不就是服务器负荷太重处理不过来。记得当时前后花了半个小时,每人发了30元的报酬,看了报名还是一种待遇不错的劳动,也许以后会出现不少职业的代报名公司或个人。

手工操作几次,像一般的注册或报名程序一样,没报成功要重定向回来,有几项填写的结果(如密码框)会被清掉,重试还得重填,很麻烦,就决定做一个机器人试试,于是翻出了去年做的一个投票机器人(见《一个简单的投票机器人》)。

       看了一下,原来的投票页面做得很业余,就一个ASP页面,没有用户注册和IP限制,完全的匿名投票。而这个报名程序看来是花了一定心思的,有5个(根据页面的代码猜的,因为没有成功,只看到前面的三个)步骤(JSP页面),更麻烦的是几个步骤后会弹出对话框(这大大增加了难度,后面会描述解决办法)。因此不能想投票机器人那样简单的构造页面模拟提交,要想别的办法。

       还是轻车熟路,用Delphi的Webbrowser控件开发。

       首先就是决定不再在本地生成临时页面,而是直接打开它的页面。因为试过想跳过前面两个无关紧要的步骤而直接提交关键的第三步,但发现它检查Cookie,本地无法生成匹配的Cookie,也无法跳过,只好从头开始。

       然后自动填写表单。

       为了保证灵活性,表单要填的数据保存在一个XML文件中。

表格 1

读如要填数据的XML文件: 

procedure TfrmMain.readInfo;

var

   // xmlDom1: TXMLDocument;

    Nroot,Nentry,NsubEntry:IXMLNode;

    filePath : string;

 

    i:integer;

 

begin

      filePath := ExtractFilePath(Application.ExeName) + 'infor.xml';

      try

          xmlDom1.LoadFromFile(filePath);

      except

        on e:exception do

           showmessage(e.Message);

      end;

      Nroot:=xmldom1.DocumentElement;

 

       Nentry:= Nroot.ChildNodes['level'];

       level := Nentry.NodeValue;

 

       Nentry:= Nroot.ChildNodes['ID'];

       id := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['pwd'];

       pwd := Nentry.NodeValue;

 

         Nentry:= Nroot.ChildNodes['sex'];

       sex := Nentry.NodeValue;

 

         Nentry:= Nroot.ChildNodes['sr'];

       sr := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['cFirstName'];

       cFirstName := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['cLastName'];

       cLastName := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['eFirstName'];

       eFirstName := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['eLastName'];

       eLastName := Nentry.NodeValue;

 

       Nentry:= Nroot.ChildNodes['question'];

       question := Nentry.NodeValue;

 

        Nentry:= Nroot.ChildNodes['answer'];

       answer := Nentry.NodeValue;

 

       Nentry:= Nroot.ChildNodes['places'];

       placeNum :=  Nentry.ChildNodes.Count;

       for i := 1 to placeNum do

       begin

              places[i] := Nentry.ChildNodes[i-1].NodeValue;

              placesID[i] := strtoint(Nentry.ChildNodes[i-1].NodeValue);

       end;

end;

用读入的数据填写第一步的表单。

表格 2

procedure TfrmMain.FillForm1;

var

     idInput:IHTMLInputElement;

    pinInput:IHTMLInputElement;

 

     doc: ihtmldocument2;

begin

 

        doc:=(WebBrowser1.Document as IHTMLDocument2);

        idInput := (doc.all.item('user_name',0) as IHTMLInputElement) ;

        idInput.value := id;

 

        pinInput := (doc.all.item('pin',0) as IHTMLInputElement) ;

        pinInput.value := pwd;

end;

注意填表单的时机,是在页面下载完毕以后,由webbrowser的DocumentComplete事件触发。填完表单后马上模拟点击Submit按钮,进入第二步。

表格 3

procedure TfrmMain.WebBrowser1DocumentComplete(Sender: TObject;

  const pDisp: IDispatch; var URL: OleVariant);

var

    kk:hwnd;

    obj:OleVariant;

    doc: ihtmldocument2;

begin

    if (stage = 0) then //第一步

    begin

        FillForm1;    //填表单1

        stage := stage +1;

 

        doc:=(WebBrowser1.Document as IHTMLDocument2);

 

        obj:=doc.all.item('Submit',0);

        obj.click;    //点“下一步”按钮

        statusBar1.Panels[0].Text := '正在打开报名协议页面。。。';

    end

    else if (stage = 1)  then   //第二步

    begin

        //showmessage(inttostr(stage) + '  WebBrowser1DocumentComplete');

        doc:=(WebBrowser1.Document as IHTMLDocument2);

 

        obj:=doc.all.item('Agree',0);

        obj.click;

        stage := stage +1;

 

    end

    else if (stage = 2) then   //第三步

    begin

         FillForm2;  //填表单2

         TryAgain;  //报名一次

         timer1.Enabled := true;

         timer1.Interval := strtoint(edtInterval.Text) * 1000;

         stage := stage +1;

         statusBar1.Panels[0].Text := '正在报名。。。';

    end   

    else if  (stage >= 3)  then

    begin

        if (isRun) then

        begin

            TryAgain;

           stage := stage +1;

        end;

    end

end;

 

第一步后,会弹出一个对话框,确定后才进入第二步,因为只有一次,这里没有处理,由手工点击确定。

第二步比较简单,一个协议条款,直接模拟点击“同意”按钮,进入第三步。

第三步是关键步骤,因为报名信息都在这里填,并提交。首先用读入的XML中的数据填第二个表单,然后提交表单。并启动一个定时器,定时提交表单重复尝试报名。

表格 4

定时器重复报名:

procedure TfrmMain.Timer1Timer(Sender: TObject);

var

     doc: ihtmldocument2;

     placeSelect:IHTMLSelectElement;

     pwd1Input:IHTMLInputElement;

begin

   TryAgain;

  Application.ProcessMessages;

 

end;

每次报名希望重试不同的考点,以扩大选择的范围,因此重复时轮流用选择的几个考点报名:

表格 5

procedure TfrmMain.TryAgain;

var

     doc: ihtmldocument2;

     placeSelect:IHTMLSelectElement;

     pwd1Input:IHTMLInputElement;

 

     kk:hwnd;

begin

    if (webbrowser1.LocationURL = url3) then

    begin

        doc:=(WebBrowser1.Document as IHTMLDocument2);

        placeSelect := (doc.all.item('tp_cname',0) as IHTMLSelectElement) ;

        placeSelect.selectedIndex := placesID[(times mod placeNum) + 1]; //轮流换考点

 

        pwd1Input := (doc.all.item('pwd1',0) as IHTMLInputElement) ;

        pwd1Input.value := pwd;

 

        submitForm;

        times := times + 1;

        statusBar1.Panels[0].Text := '正在第' + inttostr(times) + '次报名。。。';

        Application.ProcessMessages;

 

    end

    else  //报名成功,报名成功,我也不知道成功是什么样子,只好根据页面地址判断

    begin

            timer1.Enabled := false;

            btnReg.Caption := '开始';

            isRun := false;

 

            statusBar1.Panels[0].Text := '报名已成功。。。';

            showmessage('报名成功!!!!!!!!!!!!');

    end;

end;

在重复报名中遇到一个问题:报名不成功会弹出一个对话框,然后页面阻塞在那里,无法进行下去。没有办法,只有再起一个进程,找到IE弹出的对话框,强行关掉。(其实关掉并不是最好的办法,如果能发送一个消息点击确定按钮最好,可惜没找到相关资料)。

表格 6

procedure TfrmMain.NewThread;

var

  hThread:Thandle;//定义一个句柄

  ThreadID:DWord;

begin

//创建线程,同时线程函数被调用

  //messagebox(Handle,'创建线程',nil,MB_OK);

  hthread:=CreateThread(nil,0,@ClosePopDialog,nil,0,ThreadID);

  if hThread=0 then

    messagebox(Handle,'创建线程失败',nil,MB_OK);

end;

 

 

 

function ClosePopDialog(P:pointer):Longint;stdcall;

var

  i:longint;

  DC:HDC;

  kk:hwnd;

begin

 while (true) do

  begin

    if ((frmMain.stage = 3) )  then

    begin

        kk := findwindow(nil,'Microsoft Internet Explorer');

        if kk<>0 then  //如果找到IE弹出对话框,就干掉它

        begin

            setActiveWindow(kk);

            sendmessage(kk,wm_close,0,0);

        end

    end;

    sleep(5000);

  end;

end;

 

 

 

图表 1 主界面

 

本文地址:http://com.8s8s.com/it/it4981.htm