Advertisement
calophi

Export AnimePlanet to MyAnimeList 07/2016

Jul 7th, 2015
2,627
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.63 KB | None | 1 0
  1. #!/usr/bin/python
  2. #This script will take your anime-planet.com username and add your list to MAL using its API
  3. #Errors are output so you can enter those manually
  4. #set debug = True to get more information on all of your entries
  5. #Additional info and packages:
  6. #  Python 3.3.3 - http://python.org/download/
  7. #  BeautifulSoup4 - http://www.crummy.com/software/BeautifulSoup/#Download
  8. #Tips:
  9. # * You can leave your MAL username empty if it's the same as on AnimePlanet.
  10. # * To install BeautifulSoup unpack it anywhere and type "setup.py install" in the console from that folder.
  11. # * In order to successfully import the exported Anime-Planet animelist to MAL, first export MAL animelist,
  12. #    and copy the <myinfo> block just after <myanimelist> tag.
  13.  
  14.  
  15. from bs4 import BeautifulSoup,NavigableString
  16. import urllib.request,urllib.parse,base64,sys,re,codecs
  17. import xml.etree.ElementTree as et
  18.  
  19. debug = False
  20. delimiter = "\t"
  21. userAgent = "Mozilla/5.0 (Windows NT 6.2; Win64; x64;) Gecko/20100101 Firefox/20.0"
  22.  
  23. print("This script will export your anime-planet.com anime list to myanimelist.net")
  24.  
  25. username = input("Enter your AP username: ")
  26. malusername = input("Enter your MAL username: ")
  27. if (malusername == ""): malusername = username
  28. malpassword = input("Enter your MAL password: ")
  29.  
  30. baseURL = "http://www.anime-planet.com/users/%s/anime" % username
  31. apiURL = "http://myanimelist.net/api/anime/search.xml"
  32. apiURLadd = "http://myanimelist.net/api/animelist/add/%s.xml"
  33. apiURLupdate = "http://myanimelist.net/api/animelist/update/%s.xml"
  34.  
  35. passStr = str("%s:%s" % (malusername, malpassword)).replace("\n", "")
  36. authString = str(base64.b64encode(bytes(passStr, "utf-8")), "utf-8")
  37. if debug: print("MAL authorization hash: " + authString)
  38.  
  39. #Try to get HTML of first page.
  40. try:
  41.     req = urllib.request.Request(baseURL)
  42.     req.add_header("User-Agent",  userAgent)
  43.     html = BeautifulSoup(urllib.request.urlopen(req).read(), "html.parser")
  44.     pageNumber = int (html.find("li","next").findPrevious("li").next.contents[0])
  45.     #if your list is only one page, uncomment the line below and comment the line above
  46.     #pageNumber = int (html.find('li','next').findPrevious('li').contents[0])
  47. except BaseException as e:
  48.     print("Request to " + baseURL + " failed. " +str(e))
  49.     raise SystemExit
  50.  
  51. print("Processing AP list and requesting data from MAL...")
  52.  
  53. #loop through all of your pages
  54. for i in range(1,pageNumber+1):
  55.     try:
  56.         req = urllib.request.Request(baseURL + "?" + urllib.parse.urlencode({"page": str(i)}))
  57.         if debug: print("Calling URL:" + baseURL + "?" + urllib.parse.urlencode({"page": str(i)}))
  58.         req.add_header("User-Agent",  userAgent)
  59.         html = BeautifulSoup(urllib.request.urlopen(req).read(), "html.parser")
  60.     except BaseException as e:
  61.             print("Request to " + baseURL + "?" + urllib.parse.urlencode({"page": str(i)}) + " failed. " +str(e))
  62.             raise SystemExit
  63.     #loop through all of the anime posters on page i
  64.     for animeItem in html.findAll("li",class_="card"):
  65.         animeItem = BeautifulSoup(animeItem.renderContents(), "html.parser")
  66.         animeName = "" + animeItem.a.div.img["alt"]
  67.         #pretty apostophe was breaking things
  68.         animeName = animeName.replace("’","'")
  69.         queryTitle = ""
  70.         try:
  71.             titlereq = urllib.request.Request(apiURL + "?" + urllib.parse.urlencode({ "q" : animeName }))
  72.             titlereq.add_header("Authorization", "Basic %s" % authString)
  73.             titlereq.add_header("User-Agent",  userAgent)
  74.             queryTitle = urllib.request.urlopen(titlereq).read().decode("utf-8")
  75.             #I think this removes the synopsis for some reason, whatever
  76.             queryTitle = re.sub(r"(?is)<synopsis>.+</synopsis>", "", queryTitle)
  77.         except BaseException as e:
  78.             print("Anime: " + animeName)
  79.             print("Request to " + apiURL + "?" + urllib.parse.urlencode({ "q" : animeName }) + " failed. " +str(e))
  80.             raise SystemExit
  81.         #get the status, which is now a class name
  82.         status = animeItem.find("div","statusArea").span["class"][0]
  83.         formattedStatus = ""
  84.         if status=="status6":
  85.             formattedStatus = "won't watch"
  86.             status="4"
  87.         elif status=="status3":
  88.             formattedStatus = "dropped"
  89.             status="4"
  90.         elif status=="status4":
  91.             formattedStatus = "want to watch"
  92.             status="6"
  93.         elif status=="status5":
  94.             formattedStatus = "stalled"
  95.             status="3"
  96.         elif status=="status1":
  97.             formattedStatus = "watched"
  98.             status="2"
  99.         elif status=="status2":
  100.             formattedStatus = "watching"
  101.             status="1"
  102.         search = ""
  103.         try:
  104.             if queryTitle != '':
  105.                 search = et.fromstring(queryTitle)
  106.                 if debug: print("================")
  107.                 if debug: print("Anime: " + animeName)
  108.                 if debug: print(apiURL + "?" + urllib.parse.urlencode({ "q" : animeName }))
  109.             else:
  110.                 # This item failed to get a title match
  111.                 if ":" not in animeName:
  112.                     print("================")
  113.                     print("Anime: " + animeName)
  114.                     print(apiURL + "?" + urllib.parse.urlencode({ "q" : animeName }))
  115.                     print("Search failed; no match found.")
  116.                     print("Status: " + formattedStatus)
  117.                     continue
  118.                 else:
  119.                     #try truncated name for initial search
  120.                     formattedName = animeName.split(":")[0]
  121.                     try:
  122.                         titlereq = urllib.request.Request(apiURL + "?" + urllib.parse.urlencode({ "q" : formattedName }))
  123.                         titlereq.add_header("Authorization", "Basic %s" % authString)
  124.                         titlereq.add_header("User-Agent",  userAgent)
  125.                         queryTitle = urllib.request.urlopen(titlereq).read().decode("utf-8")
  126.                         #I think this removes the synopsis for some reason, whatever
  127.                         queryTitle = re.sub(r"(?is)<synopsis>.+</synopsis>", "", queryTitle)
  128.                     except BaseException as e:
  129.                         print("Anime: " + animeName)
  130.                         print("Request to " + apiURL + "?" + urllib.parse.urlencode({ "q" : formattedName }) + " failed. " +str(e))
  131.                         raise SystemExit
  132.                     if queryTitle != '':
  133.                         search = et.fromstring(queryTitle)
  134.                         if debug: print("================")
  135.                         if debug: print("Anime: " + animeName)
  136.                         if debug: print(apiURL + "?" + urllib.parse.urlencode({ "q" : formattedName }))
  137.                     else:
  138.                         # This item failed to get a title match
  139.                         print("================")
  140.                         print("Anime: " + animeName)
  141.                         print(apiURL + "?" + urllib.parse.urlencode({ "q" : formattedName }))
  142.                         print("Search failed; no match found.")
  143.                         print("Status: " + formattedStatus)
  144.                         continue
  145.                 continue
  146.         except BaseException as e:
  147.             print("Decoding of anime data failed. Error: " +str(e))
  148.             # for adding anime manually
  149.             continue
  150.         localName = animeName.lower().replace(":","").replace("(","").replace(")","")
  151.         animeID = ""
  152.         episodeCount = ""
  153.         #check all results for an id
  154.         for entry in search.findall("./entry"):
  155.             try:
  156.                 if entry.find("id") is not None and entry.find("id").text.strip()!="":
  157.                     if entry.find("title") is not None and localName in entry.find("title").text.lower().replace(":","").replace("(","").replace(")",""):
  158.                         animeID=entry.find("id").text
  159.                         episodeCount = entry.find("episodes").text
  160.                         break
  161.                     elif entry.find("english") is not None and localName in entry.find("english").text.lower().replace(":","").replace("(","").replace(")",""):
  162.                         animeID=entry.find("id").text
  163.                         episodeCount = entry.find("episodes").text
  164.                         break
  165.                     elif entry.find("synonyms") is not None and localName in entry.find("synonyms").text.lower().replace(":","").replace("(","").replace(")",""):
  166.                         animeID=entry.find("id").text
  167.                         episodeCount = entry.find("episodes").text
  168.                         break
  169.             except:
  170.                 continue
  171.         if animeID=="":
  172.             print("No MAL ID found in returned results.")
  173.             continue
  174.         if debug: print("MAL ID = " + animeID)
  175.  
  176.         xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  177.         xmlData += "<entry>\n"
  178.  
  179.         if status == "4" or status == "6":
  180.             xmlData += "\t<episode></episode>\n"
  181.         #had to include watched here because there's no way to get the # of eps anymore from AP
  182.         elif status == "2":
  183.             xmlData += "\t<episode>" + episodeCount + "</episode>\n"
  184.         else:
  185.             xmlData += "\t<episode>"+ animeItem.find("div","statusArea").text.replace("eps","").replace("ep","").replace("\t", "").replace("\n", "").replace("\r", "").replace(" ", "") +"</episode>\n"
  186.         xmlData += "\t<status>" + status +"</status>\n"
  187.         try:
  188.             rating = animeItem.find("div", attrs={"class": "ttRating"}).text;
  189.             xmlData += "\t<score>" + str(int(float(rating)*2)) + "</score>\n"
  190.         except:
  191.             xmlData += "\t<score></score>\n"
  192.             continue
  193.         xmlData += "\t<downloaded_episodes></downloaded_episodes>\n"
  194.         xmlData += "\t<storage_type></storage_type>\n"
  195.         xmlData += "\t<storage_value></storage_value>\n"
  196.         xmlData += "\t<times_rewatched></times_rewatched>\n"
  197.         xmlData += "\t<rewatch_value></rewatch_value>\n"
  198.         xmlData += "\t<date_start></date_start>\n"
  199.         xmlData += "\t<date_finish></date_finish>\n"
  200.         xmlData += "\t<priority></priority>\n"
  201.         xmlData += "\t<enable_discussion></enable_discussion>\n"
  202.         xmlData += "\t<enable_rewatching></enable_rewatching>\n"
  203.         xmlData += "\t<comments></comments>\n"
  204.         xmlData += "\t<fansub_group></fansub_group>\n"
  205.         xmlData += "\t<tags></tags>\n"
  206.         xmlData += "</entry>\n"
  207.  
  208.         params = {'id' : animeID, 'data' : xmlData}
  209.         isAdded = False
  210.         try:
  211.             if debug:
  212.                 print("Trying to add anime... ")
  213.             url = urllib.request.Request(apiURLadd % animeID, urllib.parse.urlencode(params).encode("utf-8"))
  214.             url.add_header("Authorization", "Basic %s" % authString)
  215.             url.add_header("User-Agent",  userAgent)
  216.             urllib.request.urlopen(url)
  217.             isAdded = True
  218.         except:
  219.             isAdded = False
  220.         if not isAdded:
  221.             try:
  222.                 if debug:
  223.                     print("\rTrying to update anime... ")
  224.                 url = urllib.request.Request(apiURLupdate % animeID, urllib.parse.urlencode(params).encode("utf-8"))
  225.                 url.add_header("Authorization", "Basic %s" % authString)
  226.                 url.add_header("User-Agent",  userAgent)
  227.                 urllib.request.urlopen(url)
  228.                 isAdded = True
  229.             except:
  230.                 isAdded = False
  231.         if debug:
  232.             if isAdded: sys.stdout.write("OK\n")
  233.             else: sys.stdout.write("FAILED\n")
  234.             sys.stdout.flush()
  235.  
  236. print("\nDone")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement