To download the most current version of this system, run
git clone https://github.com/vermiculus/imdb-tex.git && make install && make demo
It's a lot of fun to see how versatile LaTeX is in this respect;
I can imagine a version of this script that incorporates the movie poster as well.
A complete, compilable example is below; it will create the necessary files.
After you typeset the document once (to create these files),
run make all
to see it fly.
I'll explain what I do in the paragraphs that follows.
I would note that, in this case and cases like it, questions like this cross with StackOverflow—all you need to do is insert formatted data into a file.
One of the greatest strengths of TeX systems is its incredible ability to be automatically generated and then processed into a readable document.
(This is, I imagine, how train schedules are typeset.)
The possibilities are amazing, actually;
you could (using IMDB's API, if it exists)
easily create a system that would generate a report of every single movie in the database.
I don't recommend you do this; the result would be huge ;-).
I didn't mess with any APIs or fancy stuff in this;
the following is just a proof-of-concept.
The idea stems from the fact that, like LaTeX documents, (X)HTML documents are innately structured.
Using Python (or your favorite language),
we can take advantage of this structure
to search for key pieces of data in the website.
We'll create a means to search text for key data (by far the hardest part of the system), and then create and download the source code from URL we generate from a movie identifier.
We'll then simply open up our TeX file,
find where we wanted to insert our data,
insert it using the 'key data' function we made,
and close the file.
We'll be able to use the script with a simple
./imdb.py <template file> <movie ids...>
Let's create a file called imdb.py
and make it executable:
touch imdb.py
chmod +x imdb.py
and tell it how to run itself in the very first line:
#!/usr/bin/env python
(Note this will only work on UNIX systems;
make the necessary modifications for Windows if you do not have
a UNIX emulation solution like MinGW or Cygwin.)
We define a function
that will grab the text inside specific delimiters.
(You can imagine a scenario such as <filmrating>6.4</filmrating>
,
although in this case it's not so expressive. Silly IMDB.)
def get_middle(s, start, end):
import re
return re.search(re.escape(start) + '(.*?)' + re.escape(end), s).group(1)
This function uses regular expressions (re
) to search for some <start>...<end>
pattern.
We group the ...
in parentheses and return only that text.
You can learn more about regular expressions from a quick Google search,
although I recommend adding python
to that query;
REs are a CS-theoretical topic by nature.
Next, we want to create a function that will create a URL from a movie ID we give it.
(Note that the movie ID is the title/...
in http://www.imdb.com/title/tt0134273
;
thus tt0134273
.)
This is simpler than the previous function still:
def make_url(movie_id):
return 'http://www.imdb.com/title/{mid}'.format(
mid=movie_id)
Since this is an internet-enabled solution,
we should create a function that will grab the HTML stored at some URL
(perhaps the one we just generated).
We'll create a function to do just this:
def get_source(movie_id):
import urllib2
url = make_url(movie_id)
stream = urllib2.urlopen(url)
return stream.read()
Simple, right?
Now we just want to define a couple 'good reading' functions;
functions that only exist to make the code more readable.
These functions get different metadata,
and I may in a future version accumulate this into a single function
with a nicer interface.
def get_rating(source):
return get_middle(source, '<span itemprop="ratingValue">', '</span>')
def get_title(source):
return get_middle(source, '<span class="itemprop" itemprop="name">','</span>')
def get_release(source):
return get_middle(source, '<meta itemprop="datePublished" content="','" />')
Now we just create a function to handle the heavy lifting and orchestrate everything that we've written so far.
We create a function that takes a movie ID and a template file and an optional marker.
- We want to open the file first, reading the file into a list of lines (
file_lines
).
- We then find our marker in the file.
- We download the page for the movie from IMDB and store it in a variable called
source
.
- We create a variable,
data
, to hold the line we want to insert and format it with all of our metadata, using the 'readability functions' we created earlier.
- We insert
data
right before the marker (so the marker still exists beneath the line we just inserted).
- We're done! Write the file.
def insert(movie_id, file_name, marker='%! FILMDATA !%\n'):
"""Insert information for this `movie-id` before the %! FILMDATA !% line in `file_name`.
This function should not delete the marker line.
"""
with open(file_name, 'r') as f:
file_lines = list(f)
marker_line = file_lines.index(marker)
source = get_source(movie_id)
data = '\\film{%s}{%s}{%s}{%s}\n' % (
make_url(movie_id),
get_title(source),
get_release(source),
get_rating(source))
file_lines.insert(marker_line, data)
with open(file_name, 'w') as f:
f.writelines(file_lines)
The last bit handles its use on the command line.
We get the file_name
as the first command line argument (technically the second, the actual first argument is the name of the program that's running).
We then loop through the rest of the arguments, inserting each of them into the file provided.
def main():
import sys
file_name = sys.argv[1]
for movie_id in sys.argv[2:]:
insert(movie_id, file_name)
if __name__ == '__main__':
import sys
print 'Welcome.'
print 'Inserting data for {}'.format(str(sys.argv[2:]))
main()
print 'Done.'
The point of this exercise is to show that seemingly very complex tasks can be handled easily using a combination of tools.
Since TeX is a text-based format, it is very easy to generate and manipulate in countless programming languages.
I hope this provides a compelling argument for the continued growth of TeX systems. :)
Any further development of this tool will be carried out on Github.
Complete, compilable code:
\begin{filecontents*}{makefile}
IDS=tt0134273 tt0050083 tt0057181
TEMPLATE=imdb-template.tex
grab:
./imdb.py $(TEMPLATE) $(IDS)
tex:
pdflatex $(TEMPLATE)
pdflatex $(TEMPLATE)
all: grab tex
\end{filecontents*}
\begin{filecontents*}{imdb.py}
#!/usr/bin/env python
def get_middle(s, start, end):
import re
return re.search(re.escape(start) + '(.*?)' + re.escape(end), s).group(1)
def make_url(movie_id):
return 'http://www.imdb.com/title/{mid}'.format(
mid=movie_id)
def get_source(movie_id):
import urllib2
url = make_url(movie_id)
stream = urllib2.urlopen(url)
return stream.read()
def get_rating(source):
# YYYY-MM-DD
iso = get_middle(source, '<span itemprop="ratingValue">', '</span>')
return iso.split('-')[0] # YYYY
def get_title(source):
return get_middle(source, '<span class="itemprop" itemprop="name">','</span>')
def get_release(source):
return get_middle(source, '<meta itemprop="datePublished" content="','" />')
def insert(movie_id, file_name, marker='%! FILMDATA !%\n'):
"""Insert information for this `movie-id` before the %! FILMDATA !% line in `file_name`.
This function should not delete the marker line.
"""
with open(file_name, 'r') as f:
file_lines = list(f)
marker_line = file_lines.index(marker)
source = get_source(movie_id)
data = '\\film{%s}{%s}{%s}{%s}\n' % (
make_url(movie_id),
get_title(source),
get_release(source),
get_rating(source))
file_lines.insert(marker_line, data)
with open(file_name, 'w') as f:
f.writelines(file_lines)
def main():
import sys
file_name = sys.argv[1]
for movie_id in sys.argv[2:]:
insert(movie_id, file_name)
if __name__ == '__main__':
import sys
print 'Welcome.'
print 'Inserting data for {}'.format(str(sys.argv[2:]))
main()
print 'Done.'
\end{filecontents*}
\documentclass[
a4paper,
12pt
]{article}
\usepackage[
hmargin=2.4cm,
vmargin=3cm
]{geometry}
\usepackage{amssymb}
\usepackage{enumitem}
\usepackage{totcount}
\usepackage[
colorlinks=true,
urlcolor=black
]{hyperref}
\newcommand*\film[4]{%
\item[\refstepcounter{enumi}\textcolor{blue}{$\maltese$} \textsf{[#4]}] \textsf{\href{#1}{#2}~(#3)}}
\regtotcounter{enumi}
\begin{document}
\begin{enumerate}[leftmargin=5em,labelindent=-5em]
%! FILMDATA !%
\end{enumerate}
I have \total{enumi}~films.
\end{document}
latex
from such languages if you wish, but amakefile
works just as well and maintains modularity.):(