-1

I was trying to improve my friend's Python 'Twitch account checker' (basically gets a list of usernames from a text file and checks if they're available or taken on Twitch.tv). I was going to improve it in a way that it would output the available usernames into a text file (in the same location as the original list). I was actually searching Stack Overflow and found a post which 'explained' how to actually output a list (I put the available usernames into a separate list) into a text file.

When running the script, it works fine up to the part where it's supposed to save the available usernames. Then, I get the following error:

Traceback (most recent call last):
  File "multithreadtwitchchecker.py", line 44, in <module>
    output_available_usernames('availableusernames.txt')
  File "multithreadtwitchchecker.py", line 37, in output_available_usernames
    AVAILABLE_USERNAMES = f.write(AVAILABLE_USERNAMES.split('\n'))
AttributeError: 'list' object has no attribute 'split'

Here's the code:

from multiprocessing.pool import ThreadPool
import re
import requests
import sys

try:
    input = raw_input
except NameError:
    pass

TWITCH_URL = "https://www.twitch.tv/{username}"
TWITCH_REGEX = re.compile(r"^[a-zA-Z0-9_]{4,25}$")
MAX_THREADS = 25
MESSAGES = {True: "Available", False: "Taken"}
AVAILABLE_USERNAMES = []

def read_valid_usernames(filename):
    """Reads a list of usernames and filters out invalid ones."""
    try:
        with open(filename, "r") as fin:
            return [username for username in map(str.strip, fin) if TWITCH_REGEX.match(username)]
    except IOError:
        sys.exit("[!] '{}' - Invalid File".format(filename))

def username_available(username):
    """Checks if a 404 response code is given when requesting the profile. If it is, it is presumed to be available"""
    try:
        return username, requests.get(TWITCH_URL.format(username=username)).status_code == 404
        AVAILABLE_USERNAMES.append(username)
    except Exception as e:
        print(e)

def output_available_usernames(filename):
    """Gets a filename to output to and outputs all the valid usernames to it"""
    global AVAILABLE_USERNAMES
    f = open(filename, 'w')
    AVAILABLE_USERNAMES = f.write(AVAILABLE_USERNAMES.split('\n'))

usernames = read_valid_usernames(input("Enter path to list of usernames: "))

for username, available in ThreadPool(MAX_THREADS).imap_unordered(username_available, usernames):
    print("{:<{size}}{}".format(username, MESSAGES.get(available, "Unknown"), size=len(max(usernames, key=len)) + 1)) 

output_available_usernames('availableusernames.txt')
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
T. K.
  • 112
  • 1
  • 8
  • 1
    First, you're trying to `split` into a list something that is already a list. Second, you are trying to write an object that isn't a string to a file without first converting it. – TigerhawkT3 Jan 14 '17 at 19:20
  • @TigerhawkT3 Okay, thank you for that. If you know the correct solution, please post it as an answer! I'll attempt fixing this myself, but I'm not sure I'll get it. – T. K. Jan 14 '17 at 19:21
  • It seems to me that `.split` would be going in the wrong direction... – jonrsharpe Jan 14 '17 at 19:26
  • 1
    use `",".join(AVAILABLE_USERNAMES)` to create one string and then write it to file. And don't do `AVAILABLE_USERNAMES = f.write(...)` because you loose your data in `AVAILABLE_USERNAMES` because `write()` returns numbers of written chars. – furas Jan 14 '17 at 19:30
  • @furas I have used all of the things you suggested and now the script ran without any errors! Thanks so much! However, the text file it created is empty. Here's the new code for the function: http://paste.ofcode.org/783P2TaWb8kMFuWteHUup6 – T. K. Jan 14 '17 at 19:43
  • close file - sometimes system keep data in buffer and save on disk when you close it. – furas Jan 14 '17 at 19:51

1 Answers1

1

Well, writing to a file can be done like this:

def output_available_usernames(filename):
    global AVAILABLE_USERNAMES
    with open(filename, 'w') as f:
        for name in AVAILABLE_USERNAMES:
            f.write(name + '\n')

As jonrsharpe said, split is going in the wrong direction.

However, your code has a deeper problem right now. You append to AVAILABLE_USERNAMES after the return statement, so that code never executes, and AVAILABLE_USERNAMES will always be empty. You instead want something like this:

def username_available(username):
    """Checks if a 404 response code is given when requesting the profile. If it is, it is presumed to be available"""
    try:
        if requests.get(TWITCH_URL.format(username=username)).status_code == 404:
            AVAILABLE_USERNAMES.append(username)
            return username, True
        else:
            return username, False
    except Exception as e:
        print(e)
pbaranay
  • 585
  • 5
  • 17
  • I would also suggest re-working the code to avoid the use of a global variable, as [processes don't share state](http://stackoverflow.com/questions/11109776/changing-global-variable-when-multiprocessing-in-python), and global variables are generally discouraged in Python. You could instead do something like inspect the `available` value inside your final `for` loop (after the `print` statement). – pbaranay Jan 14 '17 at 19:46
  • Thank you. I've read over your suggestion and implemented the code and it works perfectly! Also, thanks to @jonrsharpe – T. K. Jan 14 '17 at 19:48