Created
April 30, 2018 10:40
-
-
Save answerquest/9f575120e7cae5e3601d77ff33519fe5 to your computer and use it in GitHub Desktop.
Shopify 2 WooCommerce import CSV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Usage: python3 shopify2woocommerce.py https://ABC.myshopify.com | |
| # or for interactive mode, just: python3 shopify2woocommerce.py | |
| import json | |
| import os.path | |
| import requests | |
| from collections import OrderedDict | |
| import pandas as pd | |
| from sys import argv, exit | |
| # Flags, accumulators | |
| interactiveMode = False | |
| collector = [] | |
| # create folders if they don't exist | |
| if not os.path.exists('jsons'): | |
| os.makedirs('jsons') | |
| ############################## | |
| # Functions: | |
| def downloadFile(x,y): | |
| if os.path.exists(y) : | |
| # print('skipping '+ y + ' as its already been downloaded.') | |
| return | |
| print('\nDownloading '+x) | |
| try: | |
| r = requests.get(x, allow_redirects=True) | |
| except requests.exceptions.RequestException as e: # from https://stackoverflow.com/a/16511493/4355695 | |
| print('\nInvalid URL.') | |
| if interactiveMode: interactiveExit() | |
| else: exit() | |
| with open(y, 'wb') as f: | |
| f.write(r.content) | |
| print( 'Saved as '+ y ) | |
| def interactiveExit(): | |
| print('\n\nPress any key to exit.\n') | |
| a = input() | |
| exit() | |
| ############################# | |
| # Main Program Start: | |
| # taking first argument as URL, or asking user to provide URL | |
| if len(argv) > 1 : | |
| shopifyURL = argv[1] | |
| else: | |
| print('\n\nEnter a shopify URL, like https://abc.myshopify.com:') | |
| shopifyURL = input() | |
| interactiveMode = True | |
| downloadFile( shopifyURL + '/collections.json', 'jsons/collections.json' ) | |
| collections = json.load(open('jsons/collections.json')).get('collections',[]); | |
| ######################################### | |
| # Looping through each collection | |
| print('\n\nShopify API to WooCommerce-import-CSV converter.\n\nStarting to loop through collections') | |
| for collection in collections: | |
| handle = collection.get('handle') | |
| category = collection.get('title') | |
| countCheck = collection.get('products_count') | |
| # https://ecoexist-enterprises.myshopify.com/collections/cloth-bags-15-x-17/products.json | |
| productJsonURL = 'https://ecoexist-enterprises.myshopify.com/collections/' + handle + '/products.json' | |
| productJson = 'jsons/' + handle + '.json' | |
| if countCheck: downloadFile( productJsonURL, productJson ) | |
| else: continue | |
| products = json.load(open(productJson)).get('products',[]) | |
| print( '\nCATEGORY: ' + category + ' has ' + str(len(products)) + ' products.') | |
| ######################################### | |
| # Looping through each product entry in the collection | |
| for product in products: | |
| common = OrderedDict() | |
| numVariants = len( product.get('variants',[])) | |
| common['SKU'] = product.get('handle') | |
| ######################################### | |
| # duplicates check, and if so, then just add the collection to categories and skip to next loop. | |
| repeatingSKU = False | |
| for x in collector: | |
| if x['SKU'] == common['SKU']: | |
| x['Categories'] += ', ' + category | |
| categorycount = len( x['Categories'].split(', ') ) | |
| print( 'handle=' + x['SKU'] + ' encountered again, it now has ' + str(categorycount) + ' Categories.') | |
| repeatingSKU = True | |
| break | |
| if repeatingSKU: | |
| continue | |
| common['Name'] = product.get('title') | |
| common['Description'] = product.get('body_html').replace('\n','') | |
| common['Categories'] = category | |
| common['Tags'] = ', '.join( product.get('tags') ) | |
| # Images: | |
| imageURLs = [ x.get('src') for x in product.get('images',[]) ] | |
| common['Images'] = ', '.join(imageURLs) | |
| numOptions = len( product.get('options',[])[0].get('values')) | |
| if numOptions > 1: print('OPTIONS for product ' + common['SKU'] +' : ' + str(numOptions)) | |
| ######################################### | |
| # assumptions | |
| common['Is featured?'] = 0 | |
| common['Stock'] = '' | |
| common['Backorders allowed?'] = 0 | |
| common['Sold individually?'] = 0 | |
| common['Length (in)'] = '' | |
| common['Width (in)'] = '' | |
| common['Height (in)'] = '' | |
| common['Allow customer reviews?'] = 0 | |
| common['Stock'] = '' | |
| # defaults | |
| common['Published'] = 1 | |
| common['Visibility in catalog'] = 'visible' | |
| row = OrderedDict(common) # making a copy.. see if this works | |
| ######################################### | |
| # now, here we check for variants | |
| # firstly, if it's a simple product with no variants, it'll have ONE variant in shopify. | |
| if (numVariants < 2): | |
| row['Type'] = 'simple' | |
| # print( product.get('variants')[0].get('taxable') ) | |
| taxStatus = product.get('variants')[0].get('taxable') | |
| # row['Tax status'] = ('taxable' if taxStatus else 'not taxable') | |
| # skipping tax status.. woocommerce didn't accept "not taxable" | |
| row['In stock?'] = 1 if product.get('variants')[0].get('available') else 0 | |
| grams = product.get('variants')[0].get('grams') if ( row['Type'] == 'simple' ) else 0 | |
| #row['Weight (lbs)'] = 0.00220462 * grams if ( grams > 0 ) else '' | |
| # not doing weight, importer isn't taking it. | |
| row['Regular price'] = product.get('variants')[0].get('price') if (numVariants == 1) else '' | |
| collector.append(row) | |
| ######################################### | |
| # next, if there's variants then processing each variant and adding as a new entry to the table | |
| else: | |
| print( 'VARIATIONS for product ' + row['SKU'] + ' : ' + str(numVariants) ) | |
| # first, let's complete the parent row. Most fields will be left blank here as their variants step in. | |
| row['Type'] = 'variable' | |
| row['Attribute 1 name'] = product.get('options',[])[0].get('name') | |
| row['Attribute 1 value(s)'] = ', '.join( product.get('options',[])[0].get('values') ) | |
| collector.append(row) | |
| # next, make a new row for each variation | |
| for variant in product.get('variants',[]): | |
| row2 = OrderedDict(common) | |
| row2['Type'] = 'variation' | |
| row2['SKU'] = common['SKU'] + '-' + variant.get('title').lower() | |
| # have to over-write the parent's SKU with a new one. This was what was causing a duplicating issue for all variants. Shopify didn't give | |
| row2['Name'] = common['Name'] + ' - ' + variant.get('title') | |
| # have to pre-pend the parent's title to variant's title to get full title. | |
| row2['In stock?'] = 1 if variant.get('available') else 0 | |
| grams = variant.get('grams') | |
| #row2['Weight (lbs)'] = 0.00220462 * grams if ( grams > 0 ) else '' | |
| # not doing weight, importer isn't taking it. | |
| row2['Regular price'] = variant.get('price') if (numVariants == 1) else '' | |
| # if there's a featured image, add at beginning of images list | |
| if variant.get('featured_image'): | |
| featured = variant.get('featured_image').get('src') | |
| if featured in imageURLs: | |
| # if the featured image is there in the list, remove it so it can be inserted at beginning without repeating. | |
| imageURLs.remove(featured) | |
| imageURLs.insert(0,featured) | |
| common['Images'] = ', '.join(imageURLs) | |
| collector.append(row2) | |
| df = pd.DataFrame(collector) | |
| df.to_csv('woocommerce-import.csv', index=False) | |
| print('\n\nProcessed ' + shopifyURL + '. Total ' + str(len(df)) + ' products found.') | |
| print('Created woocommerce-import.csv in the same folder where you ran this script.') | |
| if interactiveMode: | |
| interactiveExit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment