Python - 如何保存itertools.product循环并从中断处继续

时间:2017-10-08 08:30:15

标签: python

我正在编写一个python脚本来基本检查每个可能的url并在它响应请求时记录它。

我在StackOverflow上发现了一个帖子,它提出了一种为url生成字符串的方法。

    for n in range(1, 4 + 1):
    for comb in product(chars, repeat=n):
        url = ("http://" + ''.join(comb) + ".com")
        currentUrl = url
        checkUrl(url)

正如你可以想象的那样,有很多网址可以使用,这需要很长时间,所以我试图找到一种方法来保存我的脚本和恢复,如果它没有了。

我的问题是如何让循环从特定的地方开始,或者是否有人有一段代码执行相同的操作并允许我在起点指定。

这是我的剧本远......

import urllib.request
from string import digits, ascii_uppercase, ascii_lowercase
from itertools import product

goodUrls = "Valid_urls.txt"
saveFile = "save.txt"
currentUrl = ''


def checkUrl(url):
    print("Trying - "+url)
    try:
        urllib.request.urlopen(url)
    except Exception as e:
        None
    else:
        log = open(goodUrls, 'a')
        log.write(url + '\n')



chars = digits + ascii_lowercase

try:
    while True:
        for n in range(1, 4 + 1):
            for comb in product(chars, repeat=n):
                url = ("http://" + ''.join(comb) + ".com")
                currentUrl = url
                checkUrl(url)
except KeyboardInterrupt:
    print("Saving and Exiting")
    open(saveFile,'w').write(currentUrl)

2 个答案:

答案 0 :(得分:3)

尽管通过这种方法查找所有URL的尝试是荒谬的,但提出的一般问题是非常好的。简短的回答是你不能以直接的方式挑选迭代器,因为pickle机制无法保存迭代器的内部状态。但是,您可以挑选实现__iter____next__的对象。因此,如果您创建一个具有所需功能的类并且还可以作为迭代器(通过实现这两个函数),则可以对其进行pickle和重新加载。当您从中创建迭代器时,重新加载的对象将从它停止的位置继续。

#! python3.6
import pickle

class AllStrings:
    CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"
    def __init__(self):
        self.indices = [0]

    def __iter__(self):
        return self

    def __next__(self):
        s = ''.join([self.CHARS[n] for n in self.indices])
        for m in range(len(self.indices)):
            self.indices[m] += 1
            if self.indices[m] < len(self.CHARS):
                break
            self.indices[m] = 0
        else:
            self.indices.append(0)
        return s

try:
    with open("bookmark.txt", "rb") as f:
        all_strings = pickle.load(f)
except IOError:
    all_strings = AllStrings()

try:    
    for s in iter(all_strings):
        print(s)
except KeyboardInterrupt:
    with open("bookmark.txt", "wb") as f:
        pickle.dump(all_strings, f)

此解决方案还消除了对字符串长度的限制。迭代器将永远运行,最终生成所有可能的字符串。当然,在某些时候,由于宇宙的熵增加,应用程序将停止。

答案 1 :(得分:2)

itertools.product的返回值是生成器对象。因此,你所要做的就是:

products = product(...)

for foo in products:
    if bar(foo):
        spam(foo)
        break
# other stuff

for foo in products:
    # starts where you left off.

在您的情况下,迭代可能性的时间非常短,至少与完成所有这些网络请求所需的时间相比。您可以将所有可能性保存到磁盘并转储每次运行程序后剩余的列表,或者您可以保存您正在使用的。由于product具有确定性输出,因此应该这样做。

try:
    with open("progress.txt") as f:
        first_up = int(f.read().strip())
except FileNotFoundError:
    first_up = 0
try:
    for i, foo in enumerate(products):
        if i <= first_up:
            continue  # skip this iteration
        # do stuff down here
except KeyboardInterrupt:
    # this is really rude to do, by the by....
    print("Saving and exiting"
    with open("progress.txt", "w") as f:
        f.write(str(i))

如果出于某种原因需要人类可读的“进度”文件,您可以像上面那样保存上一个密码并执行:

for foo in itertools.dropwhile(products, lambda p != saved_password):
    # do stuff