在Python中使用Mechanize打开多个页面

1 投票
1 回答
1404 浏览
提问于 2025-04-17 07:41

我正在尝试使用 mechanize 打开多个页面,按照特定的格式进行操作。我想从一个特定的页面开始,然后让 mechanize 跟踪所有带有特定类名或链接中包含某段文字的链接。例如,根网址可能是这样的:

http://hansard.millbanksystems.com/offices/prime-minister

我想跟踪页面上每一个格式类似于

<li class='office-holder'><a href="http://hansard.millbanksystems.com/people/mr-tony-blair">Mr Tony Blair</a> May  2, 1997 - June 27, 2007</li>

换句话说,我想跟踪每一个类名为 'office-holder' 的链接,或者链接中包含 /people/ 的网址。我尝试了以下代码,但没有成功。

import mechanize

br = mechanize.Browser()
response = br.open("http://hansard.millbanksystems.com/offices/prime-minister")
links = br.links(url_regex="/people/")

print links

我想打印出这些链接,以确保我获取到正确的链接和信息,然后再写更多的代码。我遇到的错误是:

<generator object _filter_links at 0x10121e6e0>

任何建议或提示都非常感谢。

1 个回答

2

这不是错误——这意味着 Browser.links() 返回的是一个生成器对象,而不是一个列表。

生成器就像一个“列表”的对象,这意味着你可以做一些类似的操作:

for link in links:
    print link

等等。但你只能按照它定义的顺序来访问元素;你不能随便用 link[5] 来获取某个元素,而且一旦你遍历完这个生成器,它就用完了。

生成器在大多数情况下就是一个迭代器,但它不一定知道所有的结果。这在 生成器表达式 中非常有用,你实际上可以写一些简单的函数,使用 yield 关键字返回生成器:

def odds():
    x = 1
    while True:
        yield x
        x += 2

 os = odds()
 os.next() # returns 1
 os.next() # returns 3

这很好,因为这意味着你不需要一次性把所有数据都存储在内存中(对于 odds() 来说,这几乎是不可能的……),而且如果你只需要结果的前几个元素,就不必计算剩下的部分。itertools 模块 有很多方便的函数可以处理迭代器。


总之,如果你只是想打印出 links 的内容,可以用 list() 函数把它转换成一个列表(这个函数接受一个可迭代对象并返回它的元素列表):

 print list(links)

或者用列表推导式生成一个字符串列表:

 print [l.url for l in list(links)]

或者遍历它的元素并打印出来:

 for l in links:
      print l.url

但要注意,做完这些之后,links 会被“耗尽”——所以如果你想对它做其他操作,就需要重新获取它。

也许最简单的办法就是直接把它转换成一个列表,这样就不用担心它是一个迭代器了:

links = list(br.links(url_regex="/people/"))

另外,显然你还没有获取到你想要的类的链接。这里可能有一些 mechanize 的技巧可以实现“或”的功能,但用集合和生成器表达式来做的一个巧妙方法可能是这样的:

 links = set(l.url for l in br.links(url_regex='/people/'))
 links.update(l.url for l in br.get_links_with_class('office-holder'))

显然要把 get_links_with_class 替换成获取这些链接的实际方法。这样你就会得到一个包含所有链接 URL 的集合,这些 URL 中包含 /people/ 或者有类 office-holder,而且没有重复的链接。(注意,你不能直接把 Link 对象放入集合,因为它们是不可哈希的。)

撰写回答