Python:AssertionError:代理必须是映射

时间:2013-08-15 22:24:52

标签: python urllib2 urllib

我收到此错误:

Traceback (most recent call last):
  File "script.py", line 7, in <module>
    proxy = urllib2.ProxyHandler(line)
  File "/usr/lib/python2.7/urllib2.py", line 713, in __init__
    assert hasattr(proxies, 'has_key'), "proxies must be a mapping"
AssertionError: proxies must be a mapping

当我运行以下脚本时:

import urllib2  
u=open('urls.txt')
p=open('proxies.txt')
for line in p:
    proxy = urllib2.ProxyHandler(line)
    opener = urllib2.build_opener(proxy)
    urllib2.install_opener(opener)
    for url in u:
        urllib.urlopen(url).read()

u.close()
p.close()

我的urls.txt文件包含:

'www.google.com'
'www.facebook.com'
'www.reddit.com'

和我的proxies.txt有这个:

{'https': 'https://94.142.27.4:3128'}
{'http': 'http://118.97.95.174:8080'}
{'http':'http://66.62.236.15:8080'}

我在hidemyass.com

找到了它们 通过谷歌搜索我已经完成了,大多数有这个问题的人的代理格式错误。这是这种情况吗?

1 个答案:

答案 0 :(得分:2)

正如the documentation所说:

  

如果给出 proxies ,它必须是将协议名称映射到代理URL的字典。

但是在你的代码中,它只是一个字符串。特别是,它是proxies.txt文件中的一行:

p=open('proxies.txt')
for line in p:
    proxy = urllib2.ProxyHandler(line)

查看该文件,看起来这些行的目的是类似于Python字典的repr。并且,鉴于所有键和值都是字符串文字,这意味着可以使用ast.literal_eval来恢复原始字符串:

p=open('proxies.txt')
for line in p:
    d = ast.literal_eval(line)
    proxy = urllib2.ProxyHandler(d)

当然,这对您的示例数据不起作用,因为其中一行缺少'字符。但如果你解决了这个问题,那就......

但是,使用实际用于数据交换的格式可能会更好。例如,JSON与您所拥有的一样具有人类可读性,而不是完全不同:

{"https": "https://94.142.27.4:3128"}
{"http": "http://118.97.95.174:8080"}
{"http": "http://66.62.236.15:8080"}

使用JSON的优点是有很多工具可以验证,编辑等JSON,而且没有自定义格式的工具;什么是有效的规则是显而易见的,而不是你必须猜测的东西;并且无效数据的错误消息可能会更有帮助(例如“在第1行第10列(char 10)预期属性名称”而不是“解析时出现意外的EOF”)。


请注意,一旦解决了这个问题,您就会遇到另一个带有URL的问题。毕竟,'www.google.com'\n不是您想要的,而是www.google.com。因此,您将不得不剥离换行符和引号。同样,您可以在此处使用ast.literal_eval。或者您可以使用JSON作为交换格式。

但实际上,如果你只想尝试每行存储一个字符串,为什么不按原样存储字符串,而不是试图存储这些字符串的字符串表示(带有额外的引号)?


除此之外还有更多问题。

即使您删除了多余的引号,www.google.com也不是URL,它只是一个主机名。 http://www.google.com就是你想要的。除非你想要https://www.google.com或其他一些方案。

您尝试为每个代理循环一次'urls.txt'。那将只处理安装了第一个代理的所有URL,然后安装前两个(其余都没有,因为你已经完成了所有这些),然后安装了前两个,然后剩下的(仍然没有)全部三安装。将url循环移到proxy循环之外。

最后,这些并不是一个真正的问题,但是当我们处理它时......使用with语句可以比编写使用手动close调用更容易编写更强大的代码,并且使您的代码更短,更易读。此外,在尝试打开文件之前,通常最好还等到需要文件。而像up这样的变量名称从长远来看会引起更多的混淆,而不是短期内会节省打字。

哦,只是调用urllib.urlopen(url).read()并且没有对结果做任何事情除了浪费几秒钟和一点网络带宽之外没有任何效果,但我想你已经知道了,只是离开了为了简单起见,详细说明。

将所有内容放在一起,并假设您按上述方法修复了两个文件:

import json
import urllib2  

with open('proxies.txt') as proxies:
    for line in proxies:
        proxy = json.loads(line)
        proxy_handler = urllib2.ProxyHandler(proxy)
        opener = urllib2.build_opener(proxy_handler)
        urllib2.install_opener(opener)
with open('urls.txt') as urls:
    for line in urls:
        url = line.rstrip()
        data = urllib.urlopen(url).read()
        # do something with data

事实证明,您希望通过每个代理尝试所有URL,而不是通过所有代理尝试所有URL,或者通过第一个代码然后通过前两个代理尝试所有URL,依此类推。

您可以通过在第一个with下缩进第二个forfor来完成此操作。但是,一次只读取它们可能更简单(可能更有效,尽管我怀疑这很重要):

with open('urls.txt') as f:
    urls = [line.rstrip() for line in f]
with open('proxies.txt') as proxies:
    for line in proxies:
        proxy = json.loads(line)
        proxy_handler = urllib2.ProxyHandler(proxy)
        opener = urllib2.build_opener(proxy_handler)
        urllib2.install_opener(opener)
        for url in urls:
            data = urllib.urlopen(url).read()
            # do something with data

当然,这意味着在完成任何工作之前阅读整个URL列表。我怀疑这很重要,但如果确实如此,你可以使用the tee trick来避免它。