75
\$\begingroup\$

Challenge

Given the number of an XKCD comic, output the title text of that comic (the mouseover text).

However, the program must throw an error when given the numbers 859 or 404.

Rules

The number given will always be an existing comic (except 404).

Your program must not throw an error for any other numbers than 859 or 404.

For reference, comic 404 does not exist and 859 is:

Brains aside, I wonder how many poorly-written xkcd.com-parsing scripts will break on this title (or ;;"''{<<[' this mouseover text."

Url shorteners are disallowed. You may use the internet to get the title text.

Examples

Input > Output
1642 > "That last LinkedIn request set a new record for the most energetic physical event ever observed. Maybe we should respond." "Nah."
1385 > ::PLOOOOSH:: Looks like you won't be making it to Vinland today, Leaf Erikson.
1275 > If replacing all the '3's doesn't fix your code, remove the 4s, too, with 'ceiling(pi) / floor(pi) * pi * r^floor(pi)'. Mmm, floor pie.
1706 > Plus, now I know that I have risk factors for elbow dysplasia, heartworm, parvo, and mange.

Bounty

I will award a bounty to the shortest answer which fails on comic 859 because it's poorly written instead of checking for the number.

Your program may break on other alt texts (such as 744) providing they have unmatched parentheses, quotation marks etc.

Winning

Shortest code in bytes wins.

\$\endgroup\$
  • 2
    \$\begingroup\$ Since there are other comics with script-breaking alt texts (see 744), is it okay if a program breaks on those too? \$\endgroup\$ – totallyhuman Jun 30 '17 at 17:16
  • 8
    \$\begingroup\$ @totallyhuman You should've added a slightly-NSFW-warning to that :P \$\endgroup\$ – HyperNeutrino Jun 30 '17 at 17:27
  • 11
    \$\begingroup\$ Contradiction in the challenge: "must not throw an error for any other numbers than 859 or 404" and "may break on other alt texts". \$\endgroup\$ – aschepler Jun 30 '17 at 19:44
  • 3
    \$\begingroup\$ @aschepler The latter is only for the bounty \$\endgroup\$ – Beta Decay Jun 30 '17 at 20:58
  • 4
    \$\begingroup\$ @Kzqai Good question, but I think you may be slightly underestimating how much traffic is involved in a DDOS, and also how much traffic xkcd.com already has. I wouldn't expect the traffic generated from answers here to be significant compared to either of those. \$\endgroup\$ – trichoplax Jul 12 '17 at 1:26

16 Answers 16

107
\$\begingroup\$

Python 2.7 + xkcd, 55 bytes

xkcd is a third-party Python package. In Python, there is a package for everything!

lambda n:[xkcd.getComic(n).altText][n==859]
import xkcd

For 404: urllib2.HTTPError: HTTP Error 404: Not Found

For 859: IndexError: list index out of range

\$\endgroup\$
  • 89
    \$\begingroup\$ The package was written before this challenge and wasn't written specifically for this challenge, it just ends up being highly appropriate. \$\endgroup\$ – Draco18s Jun 30 '17 at 16:49
  • 4
    \$\begingroup\$ Wow, Python just got more attractive! \$\endgroup\$ – Nat Jun 30 '17 at 22:56
  • 14
    \$\begingroup\$ @Nat Come join Us! \$\endgroup\$ – jkd Jul 1 '17 at 2:16
  • 6
    \$\begingroup\$ Coincidentally, python does indeed support import'ing antigravity. \$\endgroup\$ – Yet Another User Jul 1 '17 at 17:43
  • 39
    \$\begingroup\$ Did Python just Mathematica this challenge? \$\endgroup\$ – Arcturus Jul 2 '17 at 2:21
22
\$\begingroup\$

Python 2 + Requests, 104 102 95 94 bytes

-2 bytes thanks to Erik the Outgolfer. -1 byte thanks to Jonathan Allan.

lambda n:[get('http://xkcd.com/%d/info.0.json'%n).json()['alt']][n==859]
from requests import*

Obligatory:

import antigravity

Poorly written script, 98 bytes

So, writing poor scripts is actually hard to do intentionally... This also breaks on other comics because they contain quotes, not sure if that's okay.

from requests import*
exec'print "%s"'%get('http://xkcd.com/%d/info.0.json'%input()).json()['alt']
\$\endgroup\$
  • 4
    \$\begingroup\$ I think you can remove ,a. \$\endgroup\$ – Erik the Outgolfer Jun 30 '17 at 13:54
  • 1
    \$\begingroup\$ You can change n in[404,859] to n==859, because the JSON decoder fails for 404 anyway. \$\endgroup\$ – musicman523 Jun 30 '17 at 14:11
  • 7
    \$\begingroup\$ ... http:// can be used here too, I think. \$\endgroup\$ – Jonathan Allan Jun 30 '17 at 14:53
  • 1
    \$\begingroup\$ how do you actually run this with a parameter? Like, how do you run a nameless lambda? \$\endgroup\$ – MrZander Jun 30 '17 at 16:32
  • 1
    \$\begingroup\$ @MrZander The first line is an anonymous lambda that can be assigned to a variable to be run. For example, both f = lambda n: n * 2; print f(2) or (lambda n: n * 2)(2) will print 4. \$\endgroup\$ – totallyhuman Jun 30 '17 at 16:34
18
+100
\$\begingroup\$

Python 2 + xkcd, 82 bytes

Poorly written script

lambda n:eval("'''%s'''"%xkcd.getComic(n).altText.replace(';;',"'''"))
import xkcd

Appends and prepends ''', which, unless the text contains ''', will not break, even for other quotation marks. That is, except if the text contains ;;, which gets replaced with ''' (eliminating re). This only applies for 859, and thus this code breaks on 859. :P

Also, one should never eval random internet content, because if xkcd.getComic(n).altText somehow became '''+__import__('os').system('rm -rf / --no-preserve-root')+''', it would cause many bad things to happen. Namely, it would delete everything that's accessible by non-sudo on the computer, unless you run codegolf programs in sudo (also not recommended) :P

\$\endgroup\$
  • 1
    \$\begingroup\$ Poorly written and fails for that test case, 859? Someone is going to get a bounty, I suppose... \$\endgroup\$ – Mr. Xcoder Jun 30 '17 at 17:55
  • 12
    \$\begingroup\$ Ah the cringe for evaling random content from the internet - bravo! :P \$\endgroup\$ – Luke Briggs Jul 3 '17 at 0:17
  • \$\begingroup\$ @LukeBriggs It should theoretically be safe... I mean, my computer hasn't exploded (yet) so it should be fine, right? :P But alternatively you could use __import__('ast').literal_eval in place of eval if you really wanted :P \$\endgroup\$ – HyperNeutrino Jul 5 '17 at 2:30
  • \$\begingroup\$ Does it break on 744? \$\endgroup\$ – Draco18s Jul 5 '17 at 19:17
  • \$\begingroup\$ @Draco18s It shouldn't, because the triple quotes don't care about the mismatched quotes, and there's no ;;. \$\endgroup\$ – HyperNeutrino Jul 5 '17 at 19:54
11
\$\begingroup\$

Wolfram Language/Mathematica, 118 117 bytes

saved a byte thanks to numbermanic

If[ImportString[#,"HTML"]===#,#,$Failed]&@Import[StringTemplate["http://xkcd.com/``/info.0.json"]@#,"RawJSON"]@"alt"&

Explanation:

Use StringTemplateto form the URL from the input.

Import[..., "RawJSON"] imports the JSON object and parses it into an Assocation.

Select the value for the key "alt".

Take this result and try to interpret the string as HTML (Import[#,"HTML"]). If this doesn't change anything pass the result through, if it does return $Failed. This catches 859 because

ImportString[
 "Brains aside, I wonder how many poorly-written xkcd.com-parsing 
  scripts will break on this title (or ;;\"''{<<[' this mouseover text.\"","HTML"]

results in:

Brains aside, I wonder how many poorly-written xkcd.com-parsing 
scripts will break on this title (or ;;"''{

404 fails because

If[
 ImportString[$Failed["alt"], "HTML"] === $Failed["alt"], 
 $Failed["alt"],
 $Failed]

results in $Failed.

\$\endgroup\$
  • \$\begingroup\$ What version are you using? I get The Import element "RawJSON" is not present when importing as JSON on 10.0.1. \$\endgroup\$ – Julian Wolf Jun 30 '17 at 16:46
  • \$\begingroup\$ @totallyhuman Well it probably doesn't need to check for 859. (See the bounty condition in the question) \$\endgroup\$ – Beta Decay Jun 30 '17 at 16:49
  • \$\begingroup\$ @JulianWolf I am using 11.1.0. I think "RawJSON" support was added in 10.2. \$\endgroup\$ – chuy Jun 30 '17 at 16:58
  • 4
    \$\begingroup\$ @totallyhuman It doesn't do an explicit check, but that's what the ImportString[#,"HTML"] bit is all about. \$\endgroup\$ – chuy Jun 30 '17 at 17:02
  • 1
    \$\begingroup\$ @numbermaniac Indeed I can. Can't believe I missed that, thanks! \$\endgroup\$ – chuy Jul 6 '17 at 13:16
8
\$\begingroup\$

Java 8, 255 176 bytes

Thanks to @OlivierGrégoire for making me feel like an idiot and 79 bytes off. ;)

i->new java.util.Scanner(new java.net.URL("http://xkcd.com/"+i+"/info.0.json").openStream()).useDelimiter("\\a").next().replaceFirst(".*\"alt\": \"","").replaceFirst("\".*","")

This feels way too heavy... Still heavy, but "okay" for java...

Explanation:

  • i->{...} Lambda that works like String <name>(int i) throws Exception
  • new java.util.Scanner(...).setDelimiter("\\a").next() read everything from the given InputStream
    • new java.net.URL("http://xkcd.com/"+i+"/info.0.json").openStream() this creates an InputStream which references the response body of http://xkcd.com/{comic_id}/info.0.json which is the info page of the desired comic
    • replaceFirst(".*\"alt\": \"","").replaceFirst("\".*","") Removes everything except for the alt text (till the first double quote)
  • implicit return

Alternate shorter approach, Java + json.org, 150

i->i==859?new Long(""):new org.json.JSONObject(new org.json.JSONTokener(new java.net.URL("http://xkcd.com/"+i+"/info.0.json").openStream())).get("alt")

This is not my solution so I don't want to post this as the first. All the credits belong to @OlivierGrégoire.

\$\endgroup\$
  • 1
    \$\begingroup\$ Your imports are missing!. Also, there's nearly zero attempt to golf this answer... \$\endgroup\$ – Olivier Grégoire Jun 30 '17 at 18:39
  • \$\begingroup\$ Added. Just under 2^8. At least the size of my programm fits in one byte :) \$\endgroup\$ – Roman Gräf Jun 30 '17 at 18:44
  • \$\begingroup\$ i->new java.util.Scanner(new java.net.URL("http://xkcd.com/"+i+"/info.0.json").openStream()).useDelimiter("\\a").next().replaceFirst(".*\"alt\": \"","").replaceFirst("\".*","") (176 bytes, careful to the SO's comment cutter characters) And I just barely golfed anything here. \$\endgroup\$ – Olivier Grégoire Jun 30 '17 at 18:49
  • \$\begingroup\$ Oh! I thought Scanner#useDelimiter returns void... Better read the docs next time ;) \$\endgroup\$ – Roman Gräf Jun 30 '17 at 18:52
  • 1
    \$\begingroup\$ I just noticed you can create your own Function class that allows you to throw Exception.. Today's​ not my day. \$\endgroup\$ – Roman Gräf Jun 30 '17 at 18:54
7
\$\begingroup\$

PHP, 89 86 85 bytes

<?=($a=$argv[1])==859?_:@json_decode(file("http://xkcd.com/$a/info.0.json")[0])->alt;

Returns null for 404 and 859

Save as xkcd.php and run with the comic number...

$ php xkcd.php 386
\$\endgroup\$
  • \$\begingroup\$ use $argninstead of $argv[1] , _ instead of NULL \$\endgroup\$ – Jörg Hülsermann Jun 30 '17 at 22:52
  • \$\begingroup\$ @JörgHülsermann Thanks! I didn't know about _. $argn doesn't seem to work though. \$\endgroup\$ – Jared Mellentine Jun 30 '17 at 23:19
  • \$\begingroup\$ php.net/manual/en/features.commandline.options.php $argn is available if you run PHP from the command Line with the -R or the -F option \$\endgroup\$ – Jörg Hülsermann Jun 30 '17 at 23:25
  • \$\begingroup\$ _ is not equivalent to NULL in PHP. This script throws an error about _ being an undefined constant. \$\endgroup\$ – Andy Jun 30 '17 at 23:45
  • \$\begingroup\$ @Andy If a Notice is not allowed "" is a better alternative as NULL Jared here is an example for $argn codegolf.stackexchange.com/questions/114146/… \$\endgroup\$ – Jörg Hülsermann Jun 30 '17 at 23:55
5
\$\begingroup\$

PHP 5.3, 280 268 262 261 180 bytes


1. Saved 11 thanks to some of Roman Gräf's suggestions
2. Saved 1 byte by using http link instead of https
3. Saved another 6 bytes thanks to Kevin_Kinsay
4. Saved another 1 byte with Andy's suggestion
5. A major revision:

  • suppressed errors with @ instead of changing libxml_use_internal_errors
  • used implode(0,file("")) instead of file_get_contents("") (2 bytes)
  • moved the $x definition inside the if
  • Using throw 0 instead of actually throwing an exception (it crashes the program)
  • with the @ I now can omit the comicLink replace.


My first try on golfing.

The DOMDocument breaks when encounters dobule ID comicLinks so I had to remove these. There's probably a nicer way of doing that.

Crashes when trying to get no. 859 ;)

<?php if(($x=$argv[1])==859)throw 0;$a=new DOMDocument;$b=@$a->loadHTML(implode(0,file("http://xkcd.com/$x")));echo $a->getElementsByTagName('img')->item(1)->getAttribute('title');
\$\endgroup\$
  • 2
    \$\begingroup\$ Welcome to PPCG! I think you can remove the test whether $x==404 because the other code will fail on the 404 response... Also you can replace throw new Exception by a die call and remove the brackets around throw new Exception("")/die because it is only a single statement \$\endgroup\$ – Roman Gräf Jun 30 '17 at 15:50
  • 1
    \$\begingroup\$ Thanks! I wasn't sure if die() would count as "throw an error" ;) \$\endgroup\$ – Ezenhis Jun 30 '17 at 16:28
  • 1
    \$\begingroup\$ Use "1" instead of 'true' on libxml_use_internal_errors. You can probably pass 0 to the Exception and save one quote equivalent. Closing ?> should be optional. \$\endgroup\$ – Kevin_Kinsey Jun 30 '17 at 16:55
  • \$\begingroup\$ Variables are interpolated inside double quotes, so "http://xkcd.com/".$x can become "http://xkcd.com/$x" saving one byte :) \$\endgroup\$ – Andy Jun 30 '17 at 23:28
  • \$\begingroup\$ BTW, +1 for using "proper" parsing technique (XML parser) as opposed to my ugly regex hack ;) \$\endgroup\$ – Kevin_Kinsey Jul 1 '17 at 12:17
5
\$\begingroup\$

Python + xkcd, 54 bytes

import xkcd
lambda n:xkcd.getComic(*{n}-{859}).altText

Verification

>>> import sys
>>> sys.tracebacklimit = 0
>>>
>>> import xkcd
>>> f = lambda n:xkcd.getComic(*{n}-{859}).altText
>>>
>>> print f(149)
Proper User Policy apparently means Simon Says.
>>>
>>> f(404)
urllib2.HTTPError: HTTP Error 404: Not Found
>>>
>>> f(859)
TypeError: getComic() takes at least 1 argument (0 given)
\$\endgroup\$
  • \$\begingroup\$ I've only just noticed this. Nice golf! \$\endgroup\$ – Beta Decay Jul 24 '18 at 10:18
5
\$\begingroup\$

The Python one has already won, but regardless...

bash + curl + sed; 88 ~91 heh bytes

printf "$(curl -s https://xkcd.com/2048/info.0.json|sed 's/.*"alt": "//;s/", "img":.*//')\n"

Yay for regex JSON parsing!

EDIT NoLongerBreathedIn noticed (648 days into the future!) that this failed on post 2048 because of an unexpected \" in that entry's JSON. The regex has been updated above; it used to be sed 's/.*alt": "\([^"]\+\).*/\1/').

The printf wrapper neatly handles the fact that Unicode characters are represented in \unnnn notation:

$ printf "$(curl -s https://xkcd.com/1538/info.0.json | sed 's/.*"alt": "//;s/", "img":.*//')\n"
To me, trying to understand song lyrics feels like when I see text in a dream but it𝔰 hอᵣd t₀ ᵣeₐd aกd 𝒾 canٖt fཱྀcu༧༦࿐༄

 

This fails with posts 404 and 859:

404

$ printf "$(curl -s https://xkcd.com/404/info.0.json | sed 's/.*alt": "\([^"]\+\).*/\1/')\n"
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

859

$ printf "$(curl -s https://xkcd.com/859/info.0.json | sed 's/.*alt": "\([^"]\+\).*/\1/')\n"
Brains aside, I wonder how many poorly-written xkcd.com-parsing scripts will break on this title (or ;;\n$

The $ at the end of the output is my prompt, and the literally-printed \n immediately before it is part of the printf string.

I deliberately used printf because it would parse Unicode and fall over terribly on this specific post.

\$\endgroup\$
  • \$\begingroup\$ Also barfs on 2048. I think it barfs on double quotes? \$\endgroup\$ – NoLongerBreathedIn Apr 9 at 18:33
  • \$\begingroup\$ Nice catch. Post updated. Looking at the sed bit, you can see it was looking for alt": " then reading until it found a ". Woops, apparently... (I wonder how many of these solutions would fail a unit test of e̲͕̲̪v̲̺̗̱̬er̶͎y̦ ͖̙̝̦s҉̟̜i͓͜n̡g̸l͎̠̹̪͈͉͚͟e̩͙̙̣̲͕͘ ̴͎͉̳̮a̢͕l̯̦̮̥̺̱̤t̕ ͕̮̪̙̬̲̪͘t̰͙̘̪̼ͅex̺͕͍͔̠̮ͅt̪͔̀? :P) \$\endgroup\$ – i336_ Apr 10 at 1:58
4
\$\begingroup\$

Python 2, 115 106 bytes

-8 bytes thanks to ovs. -1 byte thanks to Jonathan Allan.

Just thought I'd put a standard library answer out there.

lambda n:[json.load(urllib.urlopen('http://xkcd.com/%d/info.0.json'%n))['alt']][n==859]
import urllib,json
\$\endgroup\$
  • 1
    \$\begingroup\$ lambda n:[json.load(urllib.urlopen('https://xkcd.com/%d/info.0.json'%n))['alt']][n==859] for -8 bytes. \$\endgroup\$ – ovs Jun 30 '17 at 14:49
  • 1
    \$\begingroup\$ Should work with http:// too saving a byte. \$\endgroup\$ – Jonathan Allan Jun 30 '17 at 14:52
4
\$\begingroup\$

Bash + curl + jq: 73 66 bytes

Shortest answer that doesn't use an xkcd-specific library. jq is a tool for manipulating json objects in the shell, and it comes complete with a parsing language to do that.

curl -Ls xkcd.com/$1/info.0.json|jq -r 'if.num==859then.num.a else.alt end'

curl -Ls xkcd.com/$1/info.0.json|jq -r '(.num!=859//.[9]|not)//.alt'

Expansion below:

curl -Ls - Query, but feel free to redirect (in this case to the https site) and give no unrelated output.

xkcd.com/$1/info.0.json - Shamelessly stolen from another answer.

|jq -r - Run jq in "raw output" mode on the following command.

if .num == 859 then .num.a # This fails because you can't get the key 'a' from a property that's an integer else .alt # And this pulls out the 'alt' key from our object. end

Now the script has been re-worked to use // which is the equivalent of a or b in python, and we use a |not to make any true value be considered false, so the second // can print .alt

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 177 175 bytes

p=(x)=>{eval(`console.log("${x.alt}")`)};f=(y)=>{var d=document,e=d.createElement("script");e.src=`//dynamic.xkcd.com/api-0/jsonp/comic/${y}?callback=p`;d.body.appendChild(e)}}

Paste this into your browser console, then execute f(859) or f(404) etc - those two should error in the console, despite not being hard coded, the others display.

First post in a while, sorry if it doesn't quite meet the rules...!

\$\endgroup\$
  • \$\begingroup\$ Use x=> instead of (x)=>. \$\endgroup\$ – user75200 Jan 13 '18 at 18:11
2
\$\begingroup\$

PHP, 160 bytes

<? preg_match_all('/(tle=\")(.+)(\")\sa/',join(0,file('http://xkcd.com/'.$argv[1])),$a);echo(strstr($c=$a[2][0],'Brains asid'))?$b:html_entity_decode($c,3);
\$\endgroup\$
  • \$\begingroup\$ Hang on ... this isn't to spec. Fixing ... \$\endgroup\$ – Kevin_Kinsey Jun 30 '17 at 17:00
  • \$\begingroup\$ Fixed. Had to add about 50 bytes though ... :( \$\endgroup\$ – Kevin_Kinsey Jun 30 '17 at 17:15
  • 1
    \$\begingroup\$ you can remove 7 chars removing the echo and moving the $c assign inside the substr \$\endgroup\$ – Einacio Jun 30 '17 at 18:11
  • 1
    \$\begingroup\$ @BetaDecay because not checking for the input number gives extra points \$\endgroup\$ – Einacio Jun 30 '17 at 18:43
  • 1
    \$\begingroup\$ @BetaDecay well, a script that depends on content looks poorly written to me. Any other title starting like that would break it. Kevin_Kinsey you can replace ENT_QUOTES by its value=3 \$\endgroup\$ – Einacio Jun 30 '17 at 19:54
1
\$\begingroup\$

Perl, 129 167 bytes

use LWP::Simple;use HTML::Entities;print decode_entities($1)if(get("http://www.xkcd.com/$ARGV[0]")=~m/text: ([^<]*)\}\}<\/div>/)

EDIT: Psyche it's actually

use LWP::Simple;use HTML::Entities;$x=$ARGV[0];if($x==404||$x==859){die}else{print decode_entities($1)if(get("http://www.xkcd.com/$x")=~m/text: ([^<]*)\}\}<\/div>/)}

Import HTML decoding and HTTP accessing, then print the group matching the (...) in

{{Title text: (...)}}</div>

(saving a bit by omitting {{Title from the query)

For 404 and 859, death.

\$\endgroup\$
  • \$\begingroup\$ What do you mean by "properly handles 859"? \$\endgroup\$ – Beta Decay Jun 30 '17 at 18:35
  • \$\begingroup\$ @BetaDecay It prints the actual alt-text \$\endgroup\$ – archaephyrryx Jun 30 '17 at 18:36
  • 1
    \$\begingroup\$ the program must throw an error when given the numbers 859 or 404 \$\endgroup\$ – Beta Decay Jun 30 '17 at 18:37
  • \$\begingroup\$ What is "throw an error" defined as? \$\endgroup\$ – archaephyrryx Jun 30 '17 at 19:57
  • \$\begingroup\$ Nvm die is short enough \$\endgroup\$ – archaephyrryx Jun 30 '17 at 20:00
1
\$\begingroup\$

BASH, 111 108 bytes

a=$(cat) curl -s https://xkcd.com/$a/ |grep -oP '(?<=Title text:)([^}}]*)' [ $a = 404 ] && echo "$a not found"

a=#;curl -s https://xkcd.com/$a/ |grep -oP '(?<=Title text:)([^}}]*)';[ $a = 404 ] && echo "$a not found"


To Run:
change # to number of comic. Run from command line.

Thanks @Ale for the suggestion!

\$\endgroup\$
  • \$\begingroup\$ Why reading from standard input using cat instead of just using $1 from the command line? It would save some bytes... \$\endgroup\$ – Ale Jul 2 '17 at 0:04
1
\$\begingroup\$

Javascript (ES6), 118 96 94 bytes

f=n=>fetch(`//xkcd.com/${n}/info.0.json`).then(x=>x.json()).then(y=>eval(`alert('${y.alt}')`))

You can paste that in your browser console and run f(123). But do so on a page that is already on xkcd.com or else you'll see a CORS error.

For 404, it fails with:

Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0

For 859, it fails with:

Uncaught (in promise) SyntaxError: missing ) after argument list

Update: the lastest version properly checks the alt text instead of checking for just 859 and shaves of another 2 bytes.

\$\endgroup\$
  • \$\begingroup\$ Sadly, this fails on any titletext containing an apostrophe (e.g. 1084). \$\endgroup\$ – ETHproductions Jul 3 '17 at 22:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.