What is this?

malaffinity provides a simple way to calculate affinity (Pearson’s correlation * 100) between a “base” user and another user on MyAnimeList.


The term “base user” refers to the user whose scores other users’ scores will be compared to (and affinities to said scores calculated for).

Just assume the “base user” is referring to you, or whoever will be running your script, unless you’re getting into some advanced mumbo-jumbo, in which case you’re on your own.

malaffinity is meant to be used in bulk, where one user (the “base”)’s scores are compared against multiple people, but there’s nothing stopping you from using this as a one-off.

But why should I bother using this? Doesn’t MAL give me an affinity?

Let’s consider what you’d have to do if you wanted MAL to give you an affinity value, and a good estimation as to whether it’s “accurate” or not:

  • Create a requests.Session()
  • Make a GET request to MAL’s login page
  • Retrieve the csrf_token from one of the meta headers
  • Make a POST request to the login page, providing a username, password, a bunch of stupid form data, and the csrf_token you’ve just obtained
  • Confirm you are logged in, by seeing if the is_logged_in cookie is present in the CookieJar
  • Visit a users’ profile
  • Look for the affinity value (hint: .user-compatability-graph .anime .bar-inner [sic])
  • Read its innerHTML, retrieve the affinity value, add a case in to get rid of the double-negative that appears on any negative value for some reason
  • Find how many rated anime you share. The value MAL gives you includes unrated anime, and PTW stuff. It’s not an accurate indicator
    • Visit /shared.php?u1=you&u2=them and you find yourself trying to navigate through the dark and murky world of bad HTML table parsing

Congrats, you’ve just wasted a few hours of your life, and you’re probably a bit stressed right now. HTML parsing does that to you.

Let’s see how you could handle all this with malaffinity, assuming your username is Xinil and you want to calculate affinity with Luna:

from malaffinity import MALAffinity

ma = MALAffinity("Xinil")

affinity, shared = ma.calculate_affinity("Luna")

# Do whatever you like with ``affinity`` and ``shared``
# 37.06659111674594
print(shared)  # Note: Is referring to shared, rated anime
# 171


ma now holds your scores. You can easily call ma.calculate_affinity on anyone, and you’d get your affinity with them.

If you don’t want your scores to be stored, an option exists for quick, one-off calculations:

import malaffinity

affinity, shared = malaffinity.calculate_affinity("Xinil", "Luna")

# ...

I’m no expert, but the code(s) above looks a lot neater than the alternative would’ve looked.