2009-11-01 44 views
4

我遇到了机械化下列链接的问题。这是我打算做的一个片段:机械化follow_link()和back()的问题

for link in mech.links(url_regex='/test/'): 
    mech.follow_link(link) 

    // Do some processing on that link 

    mech.back() 

根据机械化示例,这应该工作得很好。但事实并非如此。尽管调用.back(),循环结束,即使有更多的访问链接。如果我注释掉mech.follow_link(link)和mech.back(),用print link.text替换它,它会打印出所有50个左右的链接。但是,只要我取消mech.follow_link的注释,循环会在第一个follow_link后立即终止。 back()正在工作,因为如果我打印mech.title(),然后再调用mech.back()并再次打印mech.title(),它会清楚地显示第一个标题,然后显示“后退”页面的标题。我非常困惑,而这正是它在文档中的做法。不知道发生了什么事。

回答

2

海盗,我同意,这不应该发生,你正在做的东西几乎正是wwwsearch.sourceforge.net/mechanize/的文档页面说的;我尝试了类似于你的代码,并得到了第一次迭代后停止的结果。

我没有,但是,找到一个变通,即挽救链接()成一个列表的链接网址,然后按照每个URL从该列表:

from mechanize import Browser 
br = Browser() 
linklist = [] 
br.open(your_page_here) 
for link in br.links(url_regex='/test/'): linklist.append(link.url) 
for url in linklist: 
    br.open(url) 
    print br.title() 

它的丑陋,你不应该不必这样做,但它似乎有效。

我不是机械化像这样Bug多多(和问题,我与mechanize handling two submit buttons poorly)真高兴,但它的安装非常简单,似乎是相当便携,可以比较轻松地脱机运行(通过简单的cron作业)其他测试框架如Selenium(seleniumhq dot org),看起来不错,但似乎更多涉及实际设置和使用。

1

比保存链接列表更简单的解决方法是简单地获取第二个浏览器对象。这可以等同于在“真实”浏览器中打开第二个选项卡。如果您还需要验证,则需要浏览器实例之间共享cookie罐:

import mechanize 
import cookielib 

br = mechanize.Browser() 
br2 = mechanize.Browser() 
cj = cookielib.LWPCookieJar() 
br.set_cookiejar(cj) 
br2.set_cookiejar(cj) 

br.open("http://yoursite.com/login") 
br.select_form(nr=0) 
br["username"] = "..." # The hash keys are the names of the form fields 
br["password"] = "..." 
br.submit() # This will save the authentication cookie to the shared cookie jar! 
br.open("http://yoursite.com/page-to-parse") 

for link in br.links(url_regex="/link_text"): 
    req = br.click_link(url=link.url) 
    html = br2.open(req).read() 

注意,有必要从第一个实例获得一个请求对象,然后用第二提交。这相当于“真实”浏览器中的“在新窗口/标签中打开”命令。

+0

您介意解释为什么有必要从第一个实例获取请求对象并将其提交给第二个实例吗?为什么我们不能'br2.open(link.url)'? – Terrence 2017-03-03 08:35:48

+0

好吧,这已经有一段时间了,我一直没有使用机械化;从我记得,_real_需要是使用两个单独的浏览器实例,因此到链接的连接不会重置第一个实例中的链接列表。我不记得为什么我必须使用click_link,也许这只是我的编码习惯,也许它设置了我必须在jar中分享的一些cookie。如果一个简单的'open'在你的情况下工作,只要继续... – 2017-03-06 09:26:56

+0

我看了一下'click_link'方法,我认为关键是它[添加引用标头](https:// github.com/jjlee/mechanize/blob/068fc642ae705aa0c71732db4b77d342e5b23628/mechanize/_mechanize.py#L559)with'self._add_referer_header(request)' – Terrence 2017-03-06 18:56:04

0

每次访问都会将links()迭代器重置为新页面上的链接。因此您需要将其保存到一个单独的变量中,例如:links = mech.links()或Chirael指示的links = list(mech.links()),其优点是可以使用print >>sys.stderr, '# links: %d' % len(links)进行计数。这不是机械化的bug。浏览器,这只是一个有状态对象的副作用。

其他疑难杂症,同时玩这个我注意到的是,你不能使用mech.back()如果mech.request并没有从一开始就设定,仿佛mech.set_response()已用于设置原始页面内容它不会是。在这种情况下,您必须明确将第一个请求设置为:mech.request = mechanize.Request('about://config')。否则你会得到一个BrowserStateError: already at start of history

和完整性的考虑,如果有人从谷歌搜索来这里,像我一样,一定要设置的标头中mechanize.make_response来,至少,(('content-type', 'text/html'),)mech.viewing_html仍将Falsemech.links()将提高BrowserStateError("not viewing HTML")