Zipfile password recovery program I would like to get a few tips on how to improve this program, which is supposed to help recovering lost passwords from .zip files.

import zipfile
import string
import itertools
import random
import time

"""
Zipfile password cracker using a dictionary attack and, if not successful, 
switches to bruteforce
"""

def crack(zipfilename,dictionary):

    t0=time.time()
    password = None
    zip_file = zipfile.ZipFile(zipfilename)
    alphabet = string.ascii_letters + string.digits + string.punctuation 

    #Tries at first a dictionary attack 

    with open(dictionary, 'r') as f:
        for line in f.readlines():
            password_string = line.strip('\n')
            try:
                password = bytes(password_string, 'utf-8')
                zip_file.extractall(pwd=password)
                t1=time.time()
                total = t1 - t0
                print('Password found : %s' %password_string)
                print('Time spent : %f seconds' %total)
                return
            except:
                pass

    #If the password hasn't been found yet, the function switches to bruteforce

    nbcharmax = 10 #Maximum length to test

    for i in range(1,nbcharmax):
        print('Testing length = %i' % i)
        for j in itertools.product(alphabet, repeat=i):
            try:
                password_string=''.join(j)
                password = bytes(password_string, 'utf-8')
                zip_file.extractall(pwd=password)
                t1=time.time()
                total = t1 - t0
                print('Password found : %s' %password_string)
                print('Time spent : %f seconds' %total)
                return
            except:
               pass


if __name__ == "__main__":
    crack('test.zip','dictionary.txt')


So now the program seems to work fine but is a bit slow in my opinion. I'm wondering if there's a faster way than zip_file.extractall() to open archive files?
1. Follow the styleguide (PEP8)

don't import modules which you're not using (random)
more, imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.
I always like to put my imports in alphabetical order, so that it gets easier to find them.
you should have two newlines before defining your methods
you should also have a space after every ,
you should put a space before and after operators (x = 'something'); this is not required when defining arguments.
instead of commenting code in the middle of the method, try to split the logic of your program in separate functions.
you should be using the new .format() style

2. Working on the code:

you should really let the user choose where the files actually are
don't use bare excepts.
you can directly use .encode() if you want to convert a string to bytes. That's actually the pythonic way of doing it as per this SO discussion
when reading a file, you can omit the r mode, as that's the default one

3. Efficiency
The problem does not come from the zip module but rather from the cartesian product you're doing. As @jonsharpe mentioned in his comments, your program has to go through a lot of passwords.
The final code would look like this:
&quot;&quot;&quot;
Zipfile password cracker using a dictionary attack and, if not successful, 
switches to bruteforce
&quot;&quot;&quot;

import itertools
import string
import time
import zipfile


ARCHIVE_PATH = 'test.zip'
DICTIONARY_PATH = 'dictionary.txt'


def dictionary_attack():
    &quot;&quot;&quot;Tries at first a dictionary attack&quot;&quot;&quot;

    t0 = time.time()
    with open(DICTIONARY_PATH) as f:
        for password in f:
            password = password.rstrip().encode()
            try:
                zipfile.ZipFile(ARCHIVE_PATH).extractall(pwd=password)
                t1 = time.time()
                print('Password found: {}\nTime spent: {} seconds'.format(password.decode(), t1 - t0))

                return True
            except RuntimeError:
                pass
    return False


def bruteforce_attack(nbcharmax):
    &quot;&quot;&quot;If the password hasn't been found yet, the function switches to bruteforce&quot;&quot;&quot;

    alphabet = string.ascii_letters + string.digits + string.punctuation

    t0 = time.time()
    for i in range(1, nbcharmax):
        for j in itertools.product(alphabet, repeat=i):
            password = ''.join(j).encode()
            try:
                zipfile.ZipFile(ARCHIVE_PATH).extractall(pwd=password)
                t1 = time.time()
                print('Password found: {}\nTime spent: {} seconds'.format(password.decode(), t1 - t0))

                return True
            except RuntimeError:
                pass
    return False

if __name__ == &quot;__main__&quot;:
    if not dictionary_attack():
        bruteforce_attack(4)