{"id":712,"date":"2012-04-24T22:05:54","date_gmt":"2012-04-25T05:05:54","guid":{"rendered":"https:\/\/dylanmc.ca\/\/-\/?p=712"},"modified":"2022-08-05T09:23:33","modified_gmt":"2022-08-05T16:23:33","slug":"formstack-submission-email-scraper","status":"publish","type":"post","link":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/","title":{"rendered":"Rescuing your data from Formstack&#8217;s &#8220;excess form submission&#8221; email"},"content":{"rendered":"\n<p><em>I seem to be accumulating all sorts of little scripts to solve odd little problems sensible people don&#8217;t encounter (and therefore don&#8217;t have scripts for). Normally I keep these to myself because they&#8217;re really ugly, but I&#8217;m going to start sharing the more useful ones on my blog. Maybe some day I&#8217;ll save someone a bit of time. That would be fun!<\/em><\/p>\n\n\n\n<p>Using a Formstack form? Went over your form submission limit and now your email is flooded with thousands of useless error messages?<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Yeah, that happened to us, too. (If it didn&#8217;t happen to you, just ignore me and carry on not being miserable).<\/p>\n\n\n\n<p>Usually, Formstack stores submissions on its end (and they do a decent enough job of that), but after a point that stops being allowed and the service sends an email for each submission, instead. Thousands of really poorly formatted emails. I think they&#8217;re trying to sell something\u2026<\/p>\n\n\n\n<p>So, here is a Python script to turn all that Formstack email back into a single CSV file that you can actually use. The end result looks just like a CSV you would normally download from them, so you can get everything back to normal.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted prettyprint lang-python\">#!\/usr\/bin\/python\n\n# Formstack email scraper, by Dylan McCall, https:\/\/dylanmc.ca\/\/-\n\nimport argparse\n\nimport codecs\nimport sys\nimport csv\n\n# Fields that have arbitrary, user-entered text as values, so the parser knows\n# to ignore reserved characters like \"=\"\nFREE_INPUT_FIELD_NAMES = [\n\t\"Other\",\n\t\"Please specify\",\n\t\"Do you have any other comments about toxic ingredients in home cleaning products?\"\n]\n\ndef main():\n\tparser = argparse.ArgumentParser(description=\"Convert a CSV file full of Formstack overflow emails back to a useful format.\")\n\n\tparser.add_argument('input', metavar='mail',\n\t\thelp=\"The CSV file with Formstack email text in its \\\"Body\\\" column\")\n\tparser.add_argument('output', metavar='output',\n\t\thelp=\"A file to store the results in\")\n\n\tparser.add_argument('--input-delimiter', default=',',\n\t\thelp=\"Value separator used for input file. Escape sequences like \\\\t are allowed.\")\n\tparser.add_argument('--headers', default='headers.csv',\n\t\thelp=\"A CSV file from Formstack's Export Submissions feature. Only the top part (column names) will be used.\")\n\tparser.add_argument('--unique-id-start', metavar=\"N\", default=99900000, type=int,\n\t\thelp=\"Starting point for unique submission IDs\")\n\n\targs = parser.parse_args()\n\targs.input_delimiter = args.input_delimiter.decode('string_escape')\n\n\ttry:\n\t\theaderFile = open(args.headers, 'r')\n\texcept IOError as error:\n\t\tprint \"I couldn't open %s.\" % args.headers\n\t\tprint \"Please use Formstack's Export Submissions feature to download form\"\n\t\tprint \"submissions as a CSV file. Save the file to the same place as this\"\n\t\tprint \"script, and name it headers.csv.\"\n\t\tprint \"The file doesn't need any real submissions. I only need it for the\"\n\t\tprint \"row of column names at the top.\"\n\t\tsys.exit()\n\n\tinFile = open(args.input, 'r')\n\toutFile = open(args.output, 'wb')\n\n\tsubmissionData = []\n\n\t# This bit of code really hinges on how your mail format works.\n\t# Right now, it's written for Microsoft Outlook's \"Export messages as CSV\"\n\t# feature, which has a Body column for message text\n\tmailReader = csv.DictReader(inFile, delimiter=args.input_delimiter, quotechar='\"')\n\tfor row in mailReader:\n\t\tmailBody = row['Body']\n\t\tdata = getDataFromMail(mailBody)\n\t\t# Some messages may not have interesting data, so we ignore those\n\t\tif len(data) &gt; 0:\n\t\t\tsubmissionData.append(data)\n\n\t# Field names can repeat, so we need to figure out the order they appear\n\tcolumns = []\n\theaderReader = csv.reader(headerFile, delimiter=',', quotechar='\"')\n\tcolumns.extend(headerReader.next())\n\n\tdataWriter = csv.writer(outFile)\n\tdataWriter.writerow(columns) # Print column names at top of CSV file\n\n\tfor index, data in enumerate(submissionData):\n\t\t# Populate the Unique ID field, which isn't in the message body\n\t\tdata['Unique ID'] = args.unique_id_start + index\n\n\t\toutputRow = list(['']) * len(columns)\n\t\tfor fieldID, fieldValue in data.iteritems():\n\t\t\tfieldParts = fieldID.split('###', 2)\n\t\t\tfieldName = fieldParts[0]\n\t\t\tfieldNumber = int(fieldParts[1]) if len(fieldParts) &gt; 1 else 0\n\n\t\t\t# Find the [fieldNumber]th repetition of the column called fieldName\n\t\t\tcolumnRepeats = 0\n\t\t\tcolNumber = findNth(columns, fieldName, fieldNumber)\n\t\t\tif colNumber &gt;= 0:\n\t\t\t\toutputRow[colNumber] = fieldValue\n\n\t\tdataWriter.writerow(outputRow)\n\ndef findInList(lst, needle, start=0):\n\ttry:\n\t\tposition = lst.index(needle, start)\n\texcept:\n\t\tposition = -1\n\treturn position\n\ndef findNth(lst, needle, n):\n\tneedleRepeats = 0\n\tfoundIndex = findInList(lst, needle, 0)\n\twhile needleRepeats &lt; n and foundIndex &gt;= 0:\n\t\tneedleRepeats += 1\n\t\tfoundIndex = findInList(lst, needle, foundIndex+1)\n\treturn foundIndex\n\n'''\nExtract all of the fields listed in the given message body.\n@return dictionary of raw field data, mapping a field name+++number to its value\n'''\ndef getDataFromMail(text):\n\tfields = {}\n\n\tIGNORE_COLUMNS = frozenset([\n\t\t\"WARNING\" #Formstack's warning message preceding information dump\n\t])\n\n\tlines = text.split('\\n')\n\n\tcurrentKey = None\n\tcurrentValue = None\n\n\tfor line in lines:\n\t\tif currentKey == None:\n\t\t\t# Looking for the start of a new field\n\n\t\t\tif line.find(': ') &gt; -1:\n\t\t\t\t# Start of a new field.\n\t\t\t\t# This field can span many lines. Store currentKey so the next lines will be appended.\n\t\t\t\t# Record field name and first value (after \": \") if applicable\n\t\t\t\tkey, val = line.split(': ', 1)\n\t\t\t\tkey = key.strip()\n\t\t\t\tval = val.strip()\n\n\t\t\t\tif not key in IGNORE_COLUMNS:\n\t\t\t\t\tcurrentKey = key\n\t\t\t\t\tcurrentValue = list()\n\t\t\t\t\tif val:\n\t\t\t\t\t\tcurrentValue.append(val)\n\n\t\telif currentKey and line.strip() == '':\n\t\t\t# Blank line always means the end of the current field.\n\n\t\t\tif len(currentValue) == 0:\n\t\t\t\tcurrentValue.append('')\n\n\t\t\tprocessedKeyValues = processField( (currentKey, currentValue) )\n\n\t\t\tfor finalKey, finalValue in processedKeyValues:\n\t\t\t\tduplicates = 0\n\t\t\t\tuniqueFinalKey = \"%s###%d\" % (finalKey, duplicates)\n\t\t\t\twhile uniqueFinalKey in fields:\n\t\t\t\t\tduplicates += 1\n\t\t\t\t\tuniqueFinalKey = \"%s###%d\" % (finalKey, duplicates)\n\n\t\t\t\tfields[uniqueFinalKey] = finalValue\n\n\t\t\tcurrentKey = None\n\t\t\tcurrentValue = None\n\n\t\telif currentKey:\n\t\t\tcurrentValue.append(line.strip())\n\n\treturn fields\n\ndef processField(field):\n\tfieldKey = field[0]\n\tfieldValue = field[1]\n\n\torderedSubFieldKeys = list()\n\tsubFields = dict()\n\n\tdata = list()\n\n\tfor value in fieldValue:\n\t\t# This behaves poorly for values that, themselves, contain \" = \"\n\n\t\t# Ugly hack for cases with user input\n\t\tif fieldKey in FREE_INPUT_FIELD_NAMES or value.startswith(\"Other:\"):\n\t\t\tsplitValue = [value]\n\t\telse:\n\t\t\tsplitValue = value.split(' = ', 1)\n\n\t\tsubFieldKey = ''\n\t\tsubFieldValue = ''\n\n\t\tif len(splitValue) == 2:\n\t\t\tsubFieldKey = splitValue[0]\n\t\t\tsubFieldValue = splitValue[1]\n\t\telif len(splitValue) == 1:\n\t\t\tsubFieldValue = splitValue[0]\n\n\t\tif not subFieldKey in orderedSubFieldKeys:\n\t\t\torderedSubFieldKeys.append(subFieldKey)\n\t\t\tsubFields[subFieldKey] = list()\n\t\tsubFields[subFieldKey].append(subFieldValue)\n\n\tfor subFieldKey in orderedSubFieldKeys:\n\t\tif subFieldKey == '':\n\t\t\tcompleteHeader = fieldKey\n\t\telse:\n\t\t\tcompleteHeader = \"%s (%s)\" % (fieldKey, subFieldKey)\n\n\t\tsubFieldValueList = subFields[subFieldKey]\n\t\tcompleteData = '|'.join(subFieldValueList)\n\n\t\t#Final output!\n\t\tdata.append( (completeHeader, completeData) )\n\n\treturn data\n\nif __name__ == \"__main__\":\n\tmain()<\/pre>\n\n\n\n<p>Using this is a little involved. But that&#8217;s why you&#8217;re still here, right?<\/p>\n\n\n\n<p>First, put that script somewhere. Name it extract-data.py. If you haven&#8217;t guessed, you&#8217;ll need Python. I&#8217;m assuming you&#8217;re using Ubuntu, MacOS X or something of the sort, but Windows should work as well.<\/p>\n\n\n\n<p>Now, you need two more files.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignright\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"271\" height=\"51\" data-attachment-id=\"744\" data-permalink=\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/screenshot-from-2012-04-24-205400\/\" data-orig-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png?fit=271%2C51&amp;ssl=1\" data-orig-size=\"271,51\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"Screenshot from 2012-04-24 20:54:00\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png?fit=271%2C51&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png?fit=271%2C51&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png?resize=271%2C51&#038;ssl=1\" alt=\"\" class=\"wp-image-744\" title=\"Screenshot from 2012-04-24 20:54:00\"\/><\/figure>\n<\/div>\n\n\n<p>Go to the Form Submissions page for your form on Formstack. Click the button near the bottom, to export some submissions as a CSV file. I don&#8217;t really care how many submissions you export: this output is just being used to get a proper set of column headers. Save that CSV file as headers.csv and put it in the same place as our Python script.<\/p>\n\n\n\n<p>Next, we need your email, ideally in something like a CSV file. In this case, I was working with a big CSV file from Microsoft Outlook. (We actually used tab-separated values, because Outlook was doing something nightmarish with character encodings. Somewhere between exporting as a TSV and opening the thing differently in LibreOffice, something started to cooperate). Mozilla Thunderbird has an extension to export a folder as a CSV file, too, and it probably <em>won&#8217;t<\/em> be trying to murder kittens.<\/p>\n\n\n\n<p>Now, because there&#8217;s no real standard way to do a CSV-formatted heap of messages, you might need to edit the script to get it to read your messages. There&#8217;s a little bit of code at the top \u2014 basically everything involving <code class=\"prettyprint lang-python\">mailReader<\/code>&nbsp;\u2014 which you can edit. It just loops through each message in the CSV file and passes that message&#8217;s body text to the message scraper. (If you&#8217;re feeling creative, you could use an mbox file with Python&#8217;s <a href=\"http:\/\/docs.python.org\/library\/mailbox.html\">mailbox library<\/a>).<\/p>\n\n\n\n<p>While you&#8217;re at it, you need to edit another variable. <code class=\"prettyprint lang-python\">FREE_INPUT_FIELD_NAMES<\/code> is a list of form fields where people can enter whatever they want. (Text fields, for example). You need to add every field that is like this on your form. Otherwise, the scraper will trip over peoples&#8217; inputs.<\/p>\n\n\n\n<p>\u2026Okay, now we&#8217;re ready to go!<\/p>\n\n\n\n<p>Open a terminal, go to the place with your script (and mail.csv, and headers.csv), and do this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted prettyprint lang-bash\">python extract-data.py mail.csv output.csv --headers=headers.csv<\/pre>\n\n\n\n<p>Boom! Magical, formatted data.<\/p>\n\n\n\n<p>The resulting file, output.csv, should look something like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"458\" height=\"192\" data-attachment-id=\"745\" data-permalink=\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/screenshot-from-2012-04-24-213242\/\" data-orig-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?fit=458%2C192&amp;ssl=1\" data-orig-size=\"458,192\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"Screenshot from 2012-04-24 21:32:42\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?fit=300%2C125&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?fit=458%2C192&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?resize=458%2C192&#038;ssl=1\" alt=\"\" class=\"wp-image-745\" title=\"Screenshot from 2012-04-24 21:32:42\" srcset=\"https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?w=458&amp;ssl=1 458w, https:\/\/i0.wp.com\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-213242.png?resize=300%2C125&amp;ssl=1 300w\" sizes=\"auto, (max-width: 458px) 100vw, 458px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"has-text-align-center\"><\/p>\n\n\n\n<p>Of course, please be gentle and don&#8217;t take this as an excuse to misuse Formstack&#8217;s service. They probably wouldn&#8217;t be happy with that and they&#8217;re being very kind by processing submissions above the maximum <em>at all<\/em>. Especially, in our case, 1600 over the maximum :)<\/p>\n\n\n\n<p>We used this to recover our data and, barring the hours of cursing and hair-pulling as I tried to make this scraper work, it was quick and painless. I hope it helps someone else out there, too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><em>I seem to be accumulating all sorts of little scripts to solve odd little problems sensible people don&#8217;t encounter (and therefore don&#8217;t have scripts for). Normally I keep these to myself because they&#8217;re really ugly, but I&#8217;m going to start sharing the more useful ones on my blog. Maybe some day I&#8217;ll save someone a bit of time&hellip; That would be fun!<\/em><\/p>\n<p>Using a Formstack form? Went over your form submission limit and now your email is flooded with thousands of useless error messages?<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-712","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Rescuing your data from Formstack&#039;s &quot;excess form submission&quot; email - Dylan McCall<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Rescuing your data from Formstack&#039;s &quot;excess form submission&quot; email - Dylan McCall\" \/>\n<meta property=\"og:description\" content=\"I seem to be accumulating all sorts of little scripts to solve odd little problems sensible people don&#8217;t encounter (and therefore don&#8217;t have scripts for). Normally I keep these to myself because they&#8217;re really ugly, but I&#8217;m going to start sharing the more useful ones on my blog. Maybe some day I&#8217;ll save someone a bit of time&hellip; That would be fun!  Using a Formstack form? Went over your form submission limit and now your email is flooded with thousands of useless error messages?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\" \/>\n<meta property=\"og:site_name\" content=\"Dylan McCall\" \/>\n<meta property=\"article:published_time\" content=\"2012-04-25T05:05:54+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-08-05T16:23:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png\" \/>\n<meta name=\"author\" content=\"Dylan McCall\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Dylan McCall\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\"},\"author\":{\"name\":\"Dylan McCall\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5\"},\"headline\":\"Rescuing your data from Formstack&#8217;s &#8220;excess form submission&#8221; email\",\"datePublished\":\"2012-04-25T05:05:54+00:00\",\"dateModified\":\"2022-08-05T16:23:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\"},\"wordCount\":678,\"image\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png\",\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\",\"url\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\",\"name\":\"Rescuing your data from Formstack's \\\"excess form submission\\\" email - Dylan McCall\",\"isPartOf\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png\",\"datePublished\":\"2012-04-25T05:05:54+00:00\",\"dateModified\":\"2022-08-05T16:23:33+00:00\",\"author\":{\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage\",\"url\":\"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png\",\"contentUrl\":\"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png\"},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#website\",\"url\":\"https:\/\/dylanmc.ca\/\/-\/\",\"name\":\"Dylan McCall\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/dylanmc.ca\/\/-\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5\",\"name\":\"Dylan McCall\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c94ab3a7e6a884542205e0408711cd54bb1fd5f4e90e7a5f621a54656a18a037?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c94ab3a7e6a884542205e0408711cd54bb1fd5f4e90e7a5f621a54656a18a037?s=96&d=mm&r=g\",\"caption\":\"Dylan McCall\"},\"description\":\"Software developer, tea drinker, GNOME contributor. Occasionally a raving fanatic.\",\"sameAs\":[\"https:\/\/dylanmc.ca\/\/-\"],\"url\":\"https:\/\/dylanmc.ca\/\/-\/blog\/author\/dylan\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Rescuing your data from Formstack's \"excess form submission\" email - Dylan McCall","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/","og_locale":"en_US","og_type":"article","og_title":"Rescuing your data from Formstack's \"excess form submission\" email - Dylan McCall","og_description":"I seem to be accumulating all sorts of little scripts to solve odd little problems sensible people don&#8217;t encounter (and therefore don&#8217;t have scripts for). Normally I keep these to myself because they&#8217;re really ugly, but I&#8217;m going to start sharing the more useful ones on my blog. Maybe some day I&#8217;ll save someone a bit of time&hellip; That would be fun!  Using a Formstack form? Went over your form submission limit and now your email is flooded with thousands of useless error messages?","og_url":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/","og_site_name":"Dylan McCall","article_published_time":"2012-04-25T05:05:54+00:00","article_modified_time":"2022-08-05T16:23:33+00:00","og_image":[{"url":"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png","type":"","width":"","height":""}],"author":"Dylan McCall","twitter_misc":{"Written by":"Dylan McCall","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#article","isPartOf":{"@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/"},"author":{"name":"Dylan McCall","@id":"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5"},"headline":"Rescuing your data from Formstack&#8217;s &#8220;excess form submission&#8221; email","datePublished":"2012-04-25T05:05:54+00:00","dateModified":"2022-08-05T16:23:33+00:00","mainEntityOfPage":{"@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/"},"wordCount":678,"image":{"@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage"},"thumbnailUrl":"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png","inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/","url":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/","name":"Rescuing your data from Formstack's \"excess form submission\" email - Dylan McCall","isPartOf":{"@id":"https:\/\/dylanmc.ca\/\/-\/#website"},"primaryImageOfPage":{"@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage"},"image":{"@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage"},"thumbnailUrl":"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png","datePublished":"2012-04-25T05:05:54+00:00","dateModified":"2022-08-05T16:23:33+00:00","author":{"@id":"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dylanmc.ca\/\/-\/blog\/2012\/04\/24\/formstack-submission-email-scraper\/#primaryimage","url":"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png","contentUrl":"https:\/\/dylanmc.ca\/wp-content\/uploads\/Screenshot-from-2012-04-24-205400.png"},{"@type":"WebSite","@id":"https:\/\/dylanmc.ca\/\/-\/#website","url":"https:\/\/dylanmc.ca\/\/-\/","name":"Dylan McCall","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dylanmc.ca\/\/-\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/c244419a779c5414c768bc53ac5fb2d5","name":"Dylan McCall","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dylanmc.ca\/\/-\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/c94ab3a7e6a884542205e0408711cd54bb1fd5f4e90e7a5f621a54656a18a037?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c94ab3a7e6a884542205e0408711cd54bb1fd5f4e90e7a5f621a54656a18a037?s=96&d=mm&r=g","caption":"Dylan McCall"},"description":"Software developer, tea drinker, GNOME contributor. Occasionally a raving fanatic.","sameAs":["https:\/\/dylanmc.ca\/\/-"],"url":"https:\/\/dylanmc.ca\/\/-\/blog\/author\/dylan\/"}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pcXOQX-bu","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/posts\/712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/comments?post=712"}],"version-history":[{"count":29,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/posts\/712\/revisions"}],"predecessor-version":[{"id":11372,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/posts\/712\/revisions\/11372"}],"wp:attachment":[{"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/media?parent=712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/categories?post=712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dylanmc.ca\/\/-\/wp-json\/wp\/v2\/tags?post=712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}