2016-03-11 2 views
2

У меня есть следующий код:Mechanize дело не с печеньем, как браузер делает

use WWW::Mechanize; 
$url = "http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/28&Lang=E"; 
$mech = WWW::Mechanize->new(); 
$mech->get($url); 
$content = $mech->content(); 
while ($content =~ m/<META HTTP-EQUIV="refresh" CONTENT="(\d+); URL=(.+?)">/) { 
    $refresh = $1; 
    $link = $2; 
    sleep $refresh; 
    $mech->get($link); 
    $content = $mech->content(); 
} 
$mech->save_content("output.txt"); 

Когда я поставил URL, назначенный $url в браузере конечный результат загрузка из PDF файла, но когда я выполните приведенный выше код. В итоге я получаю другой файл. Я думаю, что, возможно, Mechanize не может правильно обрабатывать файлы cookie. Как я могу заставить это работать?

ответ

2

При запросе http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/28&Lang=E вы сначала получаете редирект https.

Затем вы получаете страницу с META REFRESH. Это дает вам файл в /TMP.

После получения https://daccess-ods.un.org/TMP/xxx.xxx.html и после META REFRESH до https://documents-dds-ny.un.org/doc/UNDOC/GEN/G15/263/87/PDF/G1526387.pdf?OpenElement он по-прежнему не загружает документ, но дает сообщение об ошибке.

Причина, когда вы исследуете заголовки из браузера, так как браузер устанавливает три куки, и WWW :: Механизируйте только один:

  • citrix_ns_id = ххх
  • citrix_ns_id_.un.org_% 2F_wat = ххх
  • LtpaToken = ххх

Так где же эти печенье взялось? Оказывается, что TMP html имеет больше, чем META REFRESH. Он также имеет этот HTML:

<frameset ROWS="0,100%" framespacing="0" FrameBorder="0" Border="0"> 
    <frame name="footer" scrolling="no" noresize target="main" src="https://documents-dds-ny.un.org/prod/ods_mother.nsf?Login&Username=freeods2&Password=1234" marginwidth="0" marginheight="0"> 
    <frame name="main" src="" scrolling="auto" target="_top"> 
    <noframes> 
    <body> 
    <p>This page uses frames, but your browser doesn't support them.</p> 
    </body> 
    </noframes> 
</frameset> 

Этот URL https://documents-dds-ny.un.org/prod/ods_mother.nsf?Login&Username=freeods2&Password=1234 делает набор этого печенья.

Set-Cookie: LtpaToken=xxx; domain=.un.org; path=/ 
Set-Cookie: citrix_ns_id=xxx; Domain=.un.org; Path=/; HttpOnly 
Set-Cookie: citrix_ns_id_.un.org_%2F_wat=xxx; Domain=.un.org; Path=/ 

Так, изменив свой код, чтобы принять во внимание:

use strict; 
use WWW::Mechanize; 

my $url = "http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/28&Lang=E"; 
my $mech = WWW::Mechanize->new(); 
$mech->get($url); 
my $more = 1; 
while ($more) { 
    $more = 0; 
    my $follow_link; 
    my @links = $mech->links; 
    foreach my $link (@links) { 
     if ($link->tag eq 'meta') { 
      $follow_link = $link; 
     } 
     if (($link->tag eq 'frame') && ($link->url)) { 
      $mech->follow_link(url => $link->url); 
      $mech->back; 
     } 
    } 
    if ($follow_link) { 
     $more = 1; 
     $mech->follow_link(url => $follow_link->url); 
    } 
} 
$mech->save_content("output.txt"); 

output.txt успешно включает PDF.

+1

Вы, очевидно, дали хороший ответ, который решает проблему. Но только одно: вы говорите, что 'mech-> get ($ link)' не будет работать без полного URL-адреса. Это неверно. Я могу получить относительные ссылки с 'get'. – CJ7

+0

Извините. Я не знаю, почему я так думал. Я, должно быть, сделал еще одну ошибку. Я обновлю ответ. Вы можете использовать '-> follow_link' или' -> get' по своему усмотрению. Если вы используете '-> get', вы можете отправить объект WWW :: Mechanize :: Link. – bolav

-1

Вы можете попробовать добавить печенье банку конструктору, то вдоль этих линий

use HTTP::Cookies; 

my $cookie_jar = HTTP::Cookies->new(file => $cookie_file, autosave => 1, ignore_discard => 1); 
my $mech = WWW::Mechanize->new('ssl_opts'=> {'SSL_verify_mode'=>'SSL_VERIFY_NONE'}, cookie_jar => $cookie_jar, autocheck => 0); 

Если вы хотите сохранить куки, а затем загрузить его позже, чтобы сохранить ваши сеансы сделать что-то вроде этого:

$cookie_jar->save; 
#after the content call 

Для загрузки печенья:

$mech->cookie_jar->load($cookie_file); 
#before the get function (but you may want a conditional statement to check if the cookie even exists 

Надеется, что это помогает

1

Я получаю 404 при вводе этого URL-адреса в браузере, но попробуйте этот код, чтобы получить более подробный вывод отладки.

use strict; 
use warnings; 

use LWP::ConsoleLogger::Easy qw(debug_ua); 
use WWW::Mechanize; 
my $url 
    = "http://daccess-ods.un.org/access.nsf/GetOpen&DS=A/HRC/WGAD/2015/28&Lang=E"; 
my $mech = WWW::Mechanize->new(); 
debug_ua($mech); 

$mech->get($url); 
my $content = $mech->content(); 
while (
    $content =~ m/<META HTTP-EQUIV="refresh" CONTENT="(\d+); URL=(.+?)">/) 
{ 
    my $refresh = $1; 
    my $link = $2; 
    sleep $refresh; 
    $mech->get($link); 
    $content = $mech->content(); 
} 
$mech->save_content("output.txt"); 
+0

Извините, что это опечатка. Я редактировал вопрос, чтобы исправить URL. – CJ7

+0

Пробовал ли вы сценарий, который я разместил? – oalders

+0

Поскольку я не работаю, я попробовал запустить свой код дома на компьютере под управлением Windows, а 'LWP :: ConsoleLogger :: Easy' не представляется доступным. Вы попробовали исправленный URL? – CJ7

-1

Это, как я автоматизирована это в VBA:

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ 
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long 

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _ 
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _ 
ByVal lpsz2 As String) As Long 

Private Declare Function SetCursorPos Lib "user32" _ 
(ByVal X As Integer, ByVal Y As Integer) As Long 

Private Declare Function GetWindowRect Lib "user32" _ 
(ByVal hwnd As Long, lpRect As RECT) As Long 

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 

Private Declare Sub mouse_event Lib "user32.dll" (ByVal dwFlags As Long, _ 
ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long) 

Private Declare Sub SetWindowPos Lib "user32" (ByVal hwnd As Integer, ByVal _ 
    hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As _ 
    Integer, ByVal cy As Integer, ByVal wFlags As Integer) 


'~~> Constants for pressing left button of the mouse 
Private Const MOUSEEVENTF_LEFTDOWN As Long = &H2 
'~~> Constants for Releasing left button of the mouse 
Private Const MOUSEEVENTF_LEFTUP As Long = &H4 

Private Type RECT 
    Left As Long 
    Top As Long 
    Right As Long 
    Bottom As Long 
End Type 

Const HWND_TOPMOST = -1 
Const HWND_NOTOPMOST = -2 
Const SWP_NOSIZE = &H1 
Const SWP_NOMOVE = &H2 
Const SWP_NOACTIVATE = &H10 
Const SWP_SHOWWINDOW = &H40 

Dim ie As InternetExplorer 

Sub GetFiles() 

Set ie = New InternetExplorer 

GetFileFromUrl "http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/28&Lang=E" 
GetFileFromUrl "http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/31&Lang=F" 

End Sub 



Sub GetFileFromUrl(url As String) 



Dim pos As RECT 

ie.Navigate url 

ie.Visible = True 

While ie.ReadyState <> 4 
    DoEvents 
Wend 

Sleep 7000 

ie.ExecWB 4, 1, "c:\test.pdf" 

Sleep 5000 


SaveAsHwnd = FindWindow(vbNullString, "Save As") 

If SaveAsHwnd <> 0 Then 
    Debug.Print "Found Save As window" 
Else 
    Debug.Print "Did not find Save As window" 
End If 

SaveButtonHwnd = FindWindowEx(SaveAsHwnd, ByVal 0&, "Button", "&Save") 

If SaveButtonHwnd <> 0 Then 

    Debug.Print "Found Save button" 

    ' click button 
    'res = SendMessage(SaveButtonHwnd, TCM_SETCURFOCUS, 1, ByVal 0&) 
    'res = PostMessage(SaveButtonHwnd, BM_CLICK, ByVal 0&, ByVal 0&) 
    'res = SendMessage(SaveButtonHwnd, WM_COMMAND, 0&, 0&) 

    GetWindowRect SaveButtonHwnd, pos 

    '~~> Move the cursor to the specified screen coordinates. 
    SetCursorPos (pos.Left - 10), (pos.Top - 10) 
    '~~> Suspends the execution of the current thread for a specified interval. 
    '~~> This give ample amount time for the API to position the cursor 
    Sleep 100 
    SetCursorPos pos.Left, pos.Top 
    Sleep 100 
    SetCursorPos (pos.Left + pos.Right)/2, (pos.Top + pos.Bottom)/2 

    '~~> Set the size, position, and Z order of "File Download" Window 
    SetWindowPos Ret, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE 
    Sleep 100 

    '~~> Simulate mouse motion and click the button 
    '~~> Simulate LEFT CLICK 
    mouse_event MOUSEEVENTF_LEFTDOWN, (pos.Left + pos.Right)/2, (pos.Top + pos.Bottom)/2, 0, 0 
    Sleep 700 
    '~~> Simulate Release of LEFT CLICK 
    mouse_event MOUSEEVENTF_LEFTUP, (pos.Left + pos.Right)/2, (pos.Top + pos.Bottom)/2, 0, 0 

Else 

    Debug.Print "Did not find Save button" 

End If 


Sleep 5000 


End Sub 

В качестве альтернативы, UIAutomationCOM объект может быть использован:

Sub GetFilesAutomation() 

Dim o As IUIAutomation 
Dim e As IUIAutomationElement 

Dim SaveAsHwnd As LongPtr 
Dim ie As New InternetExplorer 
Set o = New CUIAutomation 

ie.Navigate "http://daccess-ods.un.org/access.nsf/Get?Open&DS=A/HRC/WGAD/2015/28&Lang=E" 

ie.Visible = True 

Sleep 10000 

ie.ExecWB 4, 1 

Sleep 5000 

SaveAsHwnd = FindWindow(vbNullString, "Save As") 

Set e = o.ElementFromHandle(ByVal SaveAsHwnd) 
Dim iCnd As IUIAutomationCondition 
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Save") 

Dim Button As IUIAutomationElement 
Set Button = e.FindFirst(TreeScope_Subtree, iCnd) 
Dim InvokePattern As IUIAutomationInvokePattern 
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId) 
InvokePattern.Invoke 

End Sub 
Смежные вопросы