{
"metadata": {
"name": "",
"signature": "sha256:4c05d403de092669880ff0942cd89e435c3082e3670be5050c3d8087621c3bd5"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#Getting data from markup languages\n",
"\n",
"So far we've discussed a number of sources for data: CSV files, web APIs, and unstructured text. There's a lot of data on the internet locked up in one of two \"markup\" languages: XML and HTML. Our goal today is to discuss and put into practice a few methods for extracting data from documents written in these languages.\n",
"\n",
"##HTML\n",
"\n",
"HTML stands for \"hypertext markup language.\" Most of the documents you see when you're browsing the web are written in this format. In most browsers, there's a \"View Source\" option that allows you to see the HTML source code for any page you're looking at. For example, in Chrome, you can CTRL-click anywhere on the page, or go to `View > Developer > View Source`:\n",
"\n",
"\n",
"\n",
"You'll see something that looks like this, a mish-mash of angle brackets and quotes and slashes and text. This is HTML.\n",
"\n",
"\n",
"\n",
"###What HTML looks like\n",
"\n",
"HTML consists of a series of *tags*. Tags have a *name*, a series of key/value pairs called *attributes*, and some textual *content*. Attributes are optional. Here's a simple example, using the HTML `
` tag (`p` means \"paragraph\"):\n",
"\n",
"
Mother said there'd be days like these.
\n",
" \n",
"This example has just one tag in it: a `
` tag. The source code for a tag has two parts, its opening tag (`
`) and its closing tag (`
`). In between the opening and closing tag, you see the tag's contents (in this case, the text `Mother said there'd be days like these.`).\n",
"\n",
"Here's another example, using the HTML `
` tag:\n",
"\n",
"
Mammoth Falls
\n",
" \n",
"In this example, the tag's name is `div`. The tag has two attributes: `class`, with value `header`, and `style`, with value `background: blue;`. The contents of this tag is `Mammoth Falls`.\n",
"\n",
"Tags can contain other tags, in a hierarchical relationship. For example, here's some HTML to make a bulletted list:\n",
"\n",
"
\n",
"
Item one
\n",
"
Item two
\n",
"
Item three
\n",
"
\n",
"\n",
"The `
` tag (`ul` stands for \"unordered list\") in this example has three other `
` tags inside of it (`li` stands for \"list item\"). The `
` tag is said to be the \"parent\" of the `
` tags, and the `
` tags are the \"children\" of the `
` tag. All tags grouped under a particular parent tag are called \"siblings.\"\n",
"\n",
"###HTML's shortcomings\n",
"\n",
"HTML documents are intended to add \"markup\" to text to add information that allows browsers to display the text in different ways---e.g., HTML markup might tell the browser to make the font of the text a particular size, or to position it in a particular place on the screen.\n",
"\n",
"Because the primary purpose of HTML is to change the appearance of text, HTML markup usually does *not* tell us anything useful about what the text means, or what kind of data it contains. When you look at a web page in the browser, it might appear to contain a list of newspaper articles, or a table with birth rates, or a series of names with associated biographies, or whatever. But that's information that we get, as humans, from reading the page. There's (usually) no easy way to extract this information with a computer program.\n",
"\n",
"HTML is also notoriously messy---web browsers are very forgiving of syntax errors and other irregularities in HTML (like mismatched or unclosed tags). (This is in contrast to data formats like JSON, where even small errors will fail to be parsed.) For this reason, we need special libraries to parse HTML into data structures that our Python programs can use, libraries that can make a \"good guess\" about what the structure of an HTML document is, even when that structure is written incorrectly or inconsistently.\n",
"\n",
"[Beautiful Soup](http://www.crummy.com/software/BeautifulSoup/) is a Python library that parses HTML (even if it's poorly formatted) and allows us to extract and manipulate its contents. We'll be using this library in the examples that follow.\n",
"\n",
"Keep in mind that there are only sketchy rules for what HTML elements \"mean\"---semantic information you figure out for one web page might not apply to the next. Values for `class` attributes especially are meaningful only in the context of a single page.\n",
"\n",
"> Note: There's an effort to add semantic information to HTML markup called [HTML Microformats](http://microformats.org/). If sites added microformats to their markup, you'd be able to write code that could more reliably extract information from web pages, because there would be a common language for what tags with particular classes and attributes mean. Alas, microformats remain unpopular, and until the anarcho-collectivists win a greater mindshare, we can count only on our own individual readings of individual HTML documents.\n",
"\n",
"###Inspecting HTML's anatomy with Developer Tools\n",
"\n",
"I've crafted a very simple example of HTML for us to work with. It concerns kittens. [Here's the rendered version](http://static.decontextualize.com/kittens.html), and [here's the HTML source code](https://raw.githubusercontent.com/ledeprogram/courses/master/databases/data/kittens.html).\n",
"\n",
"Now we're going to use Developer Tools in Chrome to take a look at how `kittens.html` is organized. Click on the \"rendered version\" link above. In Chrome, ctrl-click (or right click) anywhere on the page and select \"Inspect Element.\" This will open Chrome's Developer Tools. Your screen should look (something) like this:\n",
"\n",
"\n",
"\n",
"In the upper panel, you see the web page you're inspecting. In the lower panel, you see a version of the HTML source code, with little arrows next to some of the lines. (The little arrows allow you to collapse parts of the HTML source that are hierarchically related.) As you move your mouse over the elements in the top panel, different parts of the source code will be highlighted. Chrome is showing you which parts of the source code are causing which parts of the page to show up. Pretty spiffy!\n",
"\n",
"This relationship also works in reverse: you can move your mouse over some part of the source code in the lower panel, which will highlight in the top panel what that source code corresponds to on the page. We'll be using this later to visually identify the parts of the page that are interesting to us, so we can write code that extracts the contents of those parts automatically.\n",
"\n",
"###Characterizing the structure of kittens\n",
"\n",
"Here's what the source code of kittens.html looks like:\n",
"\n",
"\t\n",
"\t\n",
"\t \n",
"\t Kittens!\n",
"\t \n",
"\t \n",
"\t
\n",
"\t \n",
"\t\n",
"\n",
"This is pretty well organized HTML, but if you don't know how to read HTML, it will still look like a big jumble. Here's how I would characterize the structure of this HTML, reading in my own idea of what the meaning of the elements are.\n",
"\n",
"* We have two \"kittens,\" both of which are contained in `
` tags with class `kitten`.\n",
"* Each \"kitten\" `
` has an `
` tag with that kitten's name.\n",
"* There's an image for each kitten, specified with an `` tag.\n",
"* Each kitten has a list (a `
` with class `tvshows`) of television shows, contained within `
` tags share a parent tag---what is it? What attributes are present on both `` tags?\n",
"\n",
"###Scraping kittens with Beautiful Soup\n",
"\n",
"We've examined `kittens.html` a bit now. What we'd like to do is write some code that is going to extract information from the HTML, like \"what is the last checkup date for each of these kittens?\" or \"what are Monsieur Whiskeur's favorite TV shows?\" To do so, we need to *parse* the HTML, and create a representation of it in our program that we can manipulate with Python.\n",
"\n",
"As mentioned above, HTML is hard to parse by hand. (Don't even try it. In particular, [don't parse HTML with regular expressions](http://stackoverflow.com/a/1732454).)\n",
"\n",
"Beautiful Soup is a Python library that will parse the HTML for us, and give us some Python objects that we can call methods on to poke at the data contained therein.\n",
"\n",
"The first thing we need to do is fetch the source code of that page. We can do that with our old friend `urllib.urlopen()`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import urllib\n",
"\n",
"html_str = urllib.urlopen(\"https://raw.githubusercontent.com/ledeprogram/courses/master/databases/data/kittens.html\").read()"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now `html_str` is a string that contains the HTML source code of the page in question:"
]
},
{
"cell_type": "code",
"collapsed": true,
"input": [
"print html_str"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"\t\n",
"\t\tKittens!\n",
"\t\n",
"\t\n",
"\t\t
\n",
"\t\n",
"\n",
"\n",
"\n"
]
}
],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Rad. Now we want to be able to ask questions about what's in the HTML. To do so, we're going to give the string to Beautiful Soup to parse."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from bs4 import BeautifulSoup\n",
"\n",
"document = BeautifulSoup(html_str)\n",
"print type(document)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We've created a `BeautifulSoup` object and assigned it to a variable `document`. This object supports a number of interesting methods. We'll focus on just a few.\n",
"\n",
"###Finding a tag\n",
"\n",
"HTML documents are composed of tags. To represent this, Beautiful Soup has a type of value that represents tags. We can use the `.find()` method of the `BeautifulSoup` object to find a tag that matches a particular tag name. For example:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"h1_tag = document.find('h1')\n",
"print type(h1_tag)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `Tag` object has several interesting attributes and methods. The `string` attribute of a `Tag` object, for example, returns a string representing that tag's contents:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print h1_tag.string"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Kittens and the TV Shows They Love\n"
]
}
],
"prompt_number": 16
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can access the attributes of a tag by treating the tag object as though it were a dictionary, using the square-bracket index syntax, with the name of the attribute whose value you want as a string inside the brackets. For example, to print out the `src` attribute of the first `` tag in the document:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"img_tag = document.find('img')\n",
"print img_tag['src']"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"http://placekitten.com/100/100\n"
]
}
],
"prompt_number": 19
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> Note: You might have noticed that there is more than one `` tag in `kittens.html`! If more than one tag matches the name you pass to `.find()`, it returns only the *first* matching tag. (A better name for `.find()` might be `find_first`.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"###Finding multiple tags\n",
"\n",
"It's often the case that we want to find not just one tag that matches particular criteria, but ALL tags matching those criteria. For that, we use the `.find_all()` method of the `BeautifulSoup` object. For example, to find all `h2` tags in the document:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"h2_tags = document.find_all('h2')\n",
"print type(h2_tags)\n",
"[tag.string for tag in h2_tags]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 13,
"text": [
"[u'Fluffy', u'Monsieur Whiskeurs']"
]
}
],
"prompt_number": 13
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Both the `.find()` and `.find_all()` methods can search not just for tags with particular names, but also for tags that have particular attributes. For that, we use the `attrs` keyword argument, giving it a dictionary that associates attribute names as keys and the desired attribute value as values. For example, to find all `span` tags with a `class` attribute of `lastcheckup`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"checkup_tags = document.find_all('span', attrs={'class': 'lastcheckup'})\n",
"[tag.string for tag in checkup_tags]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 15,
"text": [
"[u'2014-01-17', u'2013-11-02']"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> Note: Beautiful Soup's `.find()` and `.find_all()` methods are actually more powerful than we're letting on here. [Check out the details in the official Beautiful Soup documentation.](http://www.crummy.com/software/BeautifulSoup/bs4/doc/#find-all)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"###Finding tags within tags\n",
"\n",
"Let's say that we wanted to print out a list of the name of each kitten, along with a list of the names of that kitten's favorite TV shows. In other words, we want to print out something that looks like this:\n",
"\n",
" Fluffy: Deep Space Nine, Mr. Belvedere\n",
" Monsieur Whiskeurs: The X-Files, Fresh Prince\n",
" \n",
"In order to do this, we need to find *not just* tags with particular names, but tags with *particular hierarchical relationships* with other tags. I.e., we need to identify all of the kittens, and then find the shows that belong to that kitten. This kind of search is made easy by the fact tht you can use `.find()` and `.find_all()` methods not just on the entire document, but on individual tags. When you use these methods on tags, they search for matching tags that are specifically *children of* the tag that you call them on.\n",
"\n",
"In our kittens example, we can see that information about individual kittens is grouped together under `
` tags with a `class` attribute of `kitten`. So, to find a list of all `
` tags with `class` set to `kitten`, we might do this:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"kitten_tags = document.find_all(\"div\", attrs={\"class\": \"kitten\"})"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 20
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we'll loop over that list of tags and find, inside each of them, the `
` tag that is its child:\n"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"for kitten_tag in kitten_tags:\n",
" h2_tag = kitten_tag.find('h2')\n",
" print h2_tag.string"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Fluffy\n",
"Monsieur Whiskeurs\n"
]
}
],
"prompt_number": 21
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we'll go one extra step. Looping over all of the kitten tags, we'll find not just the `
` tag, so I'll find all of those.\n",
"* For each `
` tag, I need to find an `` tag---specifically, I need to grab the `src` attribute from that tag.\n",
"* The faculty member's name is inside an `` tag---specifically, an `` tag inside of an `
\n"
]
}
],
"prompt_number": 60
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When you `print` a `Tag` object, BeautifulSoup displays the source code for those tags. Here we can see that the `
` tags we've found aren't quite the `
` tags we were looking for---these seem to be `
` tags from another part of the page! Whoops. I guess we need to be more specific about which `
` tags we want. How do we do that, though? Let's go back to Developer Tools.\n",
"\n",
"\n",
"\n",
"Now it *looks* like all of the relevant `
` tags have a single parent tag---`
`. So what we need to do is find not *all `
` tags on the page*, but *only those `
` tags that are children of this particular `
` tag*. Here's some revised code to do just that:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"experts_ul_tag = document.find('ul', attrs={'class': 'experts-list'})\n",
"for faculty_tag in experts_ul_tag.find_all('li')[:5]:\n",
" print faculty_tag"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"
\n"
]
}
],
"prompt_number": 61
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This looks a little bit better, but we've still got a few weird things: namely, there are some `
` tags, (those with a `class` attribute of `label`) which don't seem to contain `h4` tags---or any other content we're interested in at all. We need to put in a check so our code will disregard any `
\n",
" \n",
" \n",
" John Doe\n",
" johndoe@example.com\n",
" \n",
"\t \n",
" \n",
"\n",
"As you can see, XML looks a lot like HTML: tags, with attributes and contents, exist in a hierarchical relationship with other tags. The main difference is that in XML, there isn't a pre-defined list of \"valid\" tag names---when you create a document, you can use whatever tag and attribute names you want. As you can see in the example above, there are tags called `feed` and `entry` that aren't a part of the HTML standard, but are valid XML.\n",
"\n",
"The second important difference between XML and HTML is that in XML, all tags must consist of both an opening tag AND a closing tag. HTML doesn't have this restriction (as we saw with `` tags in the HTML examples above). Also, in general, tools that work with XML are much more strict about syntax than tools that work with HTML. Browsers tend to be very forgiving of errors in HTML, but will immediately reject XML that isn't well-formed.\n",
"\n",
"XML documents generally conform to a \"standard\" or \"format,\"---that is, a pre-defined list of tag names and attribute names and rules for which tags can have which attributes and which tags can contain which other tags. For example, the document in the above is in the Atom XML format, [which you can find out more about here](http://en.wikipedia.org/wiki/Atom_(standard)). XML standards also give you some idea of what the document *means*---a consistent mapping between the document's structure and its semantics.\n",
"\n",
"In sum: XML documents conform to standards, they must be syntactically valid, and they have agreed-upon semantics. For these reasons, XML documents are considered to be much more friendly for computers to read than HTML documents. \n",
"\n",
"> CLEVER PEOPLE NOTE: XML and HTML work similarly enough, and XML documents can have standards, so why not just make an XML standard that defines all of the tags and attributes in HTML, and have the best of both worlds? [It's been tried before](http://en.wikipedia.org/wiki/XHTML), and there are several drawbacks, [enumerated here](http://stackoverflow.com/questions/5558502/is-html5-valid-xml), but mostly having to do with backwards compatibility."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"###Dealing with XML data\n",
"\n",
"Now, you *can* parse XML data with Beautiful Soup ([with one important caveat](http://www.crummy.com/software/BeautifulSoup/bs4/doc/#id17)). But one of the benefits of data in XML is that there are many pre-existing libraries for Python that are purpose-built for working with data in whichever XML standard. These libraries will save you the effort of having to figure out how documents in that particular standard are put together.\n",
"\n",
"There are [a truly bewildering number of XML standards](http://en.wikipedia.org/wiki/Category:XML-based_standards), each devised for more or less domain-specific tasks. (There is even [a truly bewildering number of XML standards for writing documents that define XML standards](http://en.wikipedia.org/wiki/XML_schema#XML_schema_languages)). Listed below are a few standards of interest to journalists, along with links to Python libraries for dealing with documents using those standards:\n",
"\n",
"* [Keyhole Markup Language](http://en.wikipedia.org/wiki/Keyhole_Markup_Language) (KML), used for geographic data: [fastkml](https://pypi.python.org/pypi/fastkml/)\n",
"* [Scalable Vector Graphics](http://en.wikipedia.org/wiki/Scalable_Vector_Graphics) (SVG), used for images and drawings: [pySVG](http://codeboje.de/pysvg/)\n",
"* [SOAP](http://en.wikipedia.org/wiki/SOAP_(protocol)), used for some web services: [pysimplesoap](https://code.google.com/p/pysimplesoap/)\n",
"* [Atom](http://en.wikipedia.org/wiki/Atom_(standard)), a set of standards used for web publishing and services: [feedparser](https://pypi.python.org/pypi/feedparser). (The `feedparser` library also helps to parse all manner of other web syndication formats.)\n",
"* \n",
"\n",
"###An example: RSS feeds\n",
"\n",
"One of the first tasks many students set themselves to after learning about web scraping is to scrape the front page of the New York Times. *DON'T DO THIS* if you can avoid it. You're inviting disaster, as the NYTimes is free at any moment to change the way their HTML is structured, and your scraper will break. Instead, try using the New York Times RSS feed!\n",
"\n",
"RSS is a format that many websites use to publish their articles in computer-readable formats. (RSS support used to be all the rage back in the Internet days, and fewer sites now support it than used to, and some web sites---like the New York Times---support it but don't advertise that fact.) It's an XML format. Here's a link to the New York Times RSS feed for their front-page articles:\n",
"\n",
"http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml\n",
"\n",
"Click on that link, and you'll see a big mess of XML that doesn't make any sense. We're going to use the `feedparser` library mentioned above to parse this RSS and get back a list of all of the article titles. The `feedparser` library essentially takes a big ball of RSS XML and turns it into a Python data structure (to be specific, a list of dictionaries, where each dictionary represents an article in the feed).\n",
"\n",
"First, check to see if you have `feedparser` installed."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import feedparser"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you get `ImportError: No module named feedparser`, try running this line (this will work ONLY on your AWS instances):"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"!sudo pip install feedparser"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Password:"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\r\n"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Otherwise, you can use your `pip` skills to install feedparser however you'd like.\n",
"\n",
"Once you have `feedparser` installed, we can use it to read in a remote RSS file:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import feedparser\n",
"\n",
"feed = feedparser.parse(\"http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml\")\n",
"print type(feed.entries)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `feedparser.parse` function returns a `feedparser` object, which has an attribute `entries` that is a list of articles in the feed. Let's take a look at one of them:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"feed.entries[0]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 18,
"text": [
"{'author': u'By JACK HEALY',\n",
" 'author_detail': {'name': u'By JACK HEALY'},\n",
" 'authors': [{}],\n",
" 'guidislink': False,\n",
" 'id': u'http://www.nytimes.com/2014/06/24/us/Tom-Tancredos-Colorado-Governor-Puts-His-Party-on-Alert-.html',\n",
" 'link': u'http://rss.nytimes.com/c/34625/f/640350/s/3bcb5250/sc/7/l/0L0Snytimes0N0C20A140C0A60C240Cus0CTom0ETancredos0EColorado0EGovernor0EPuts0EHis0EParty0Eon0EAlert0E0Bhtml0Dpartner0Frss0Gemc0Frss/story01.htm',\n",
" 'links': [{'href': u'http://www.nytimes.com/2014/06/24/us/Tom-Tancredos-Colorado-Governor-Puts-His-Party-on-Alert-.html?partner=rss&emc=rss',\n",
" 'rel': u'standout',\n",
" 'type': u'text/html'},\n",
" {'href': u'http://rss.nytimes.com/c/34625/f/640350/s/3bcb5250/sc/7/l/0L0Snytimes0N0C20A140C0A60C240Cus0CTom0ETancredos0EColorado0EGovernor0EPuts0EHis0EParty0Eon0EAlert0E0Bhtml0Dpartner0Frss0Gemc0Frss/story01.htm',\n",
" 'rel': u'alternate',\n",
" 'type': u'text/html'}],\n",
" 'media_content': [{'height': u'151',\n",
" 'lang': u'',\n",
" 'url': u'http://graphics8.nytimes.com/images/2014/06/24/us/TANCREDO1/TANCREDO1-moth.jpg',\n",
" 'width': u'151'}],\n",
" 'media_credit': {'scheme': u'urn:ebu'},\n",
" 'media_description': u'A volunteer campaigned on a bridge in\\xa0Littleton, Colo.',\n",
" 'published': u'Mon, 23 Jun 2014 17:09:14 GMT',\n",
" 'published_parsed': time.struct_time(tm_year=2014, tm_mon=6, tm_mday=23, tm_hour=17, tm_min=9, tm_sec=14, tm_wday=0, tm_yday=174, tm_isdst=0),\n",
" 'summary': u'Some Republicans in Colorado say the views of Mr. Tancredo, a former congressman, could energize the state\\u2019s Democrats while alienating moderate Republicans and unaffiliated voters.
',\n",
" 'summary_detail': {'base': u'http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml',\n",
" 'language': None,\n",
" 'type': u'text/html',\n",
" 'value': u'Some Republicans in Colorado say the views of Mr. Tancredo, a former congressman, could energize the state\\u2019s Democrats while alienating moderate Republicans and unaffiliated voters.
'},\n",
" 'tags': [{'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/keywords/nyt_geo',\n",
" 'term': u'Colorado'},\n",
" {'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/nyt_org_all',\n",
" 'term': u'Facebook Inc|FB|NASDAQ'},\n",
" {'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/keywords/nyt_per',\n",
" 'term': u'Tancredo, Tom'},\n",
" {'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/nyt_org_all',\n",
" 'term': u'Harley-Davidson Inc|HOG|NYSE'},\n",
" {'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/keywords/des',\n",
" 'term': u'Midterm Elections (2014)'},\n",
" {'label': None,\n",
" 'scheme': u'http://www.nytimes.com/namespaces/keywords/nyt_org_all',\n",
" 'term': u'Republican Party'}],\n",
" 'title': u'Tom Tancredo\\u2019s Bid for Colorado Governor Puts His Party on Alert',\n",
" 'title_detail': {'base': u'http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml',\n",
" 'language': None,\n",
" 'type': u'text/plain',\n",
" 'value': u'Tom Tancredo\\u2019s Bid for Colorado Governor Puts His Party on Alert'}}"
]
}
],
"prompt_number": 18
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Okay, cool. Looking over this data structure, it looks like we have a dictionary, and the thing we want---the title of the article---is the value for the `title` key. Let's make a list comprehension to pull them out:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"[article['title'] for article in feed.entries]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 20,
"text": [
"[u'Tom Tancredo\\u2019s Bid for Colorado Governor Puts His Party on Alert',\n",
" u'Justices, With Limits, Let E.P.A. Curb Power-Plant Gases',\n",
" u'Kerry Says ISIS Threat Could Hasten Military Action',\n",
" u'Top Afghan Election Official Resigns Amid Candidate\\u2019s Claims of Vote Fraud',\n",
" u'Here Lies Progress: Asian Actors Fill the Playbill',\n",
" u'Justice Department Found It Lawful to Target Anwar al-Awlaki',\n",
" u'The Global Game: Drawing Lots at World Cup? There Must Be a Better Way',\n",
" u'Top Investigator Has Blistering Criticism for V.A. Response to Whistle-Blowers',\n",
" u'ArtsBeat: Annie Missing? No Worries, Dick Tracy Is on the Case',\n",
" u'Last of Syria\\u2019s Declared Chemical Arms Shipped Abroad',\n",
" u'City Room: New York Today: Fire in the Dark',\n",
" u'Afghan Official Quits in Bid to End Crisis',\n",
" u'Report: Pennsylvania Governor Did Not Deliberately Delay Sandusky Case',\n",
" u'Steve Rossi, Singer Who Found Fame in Comedy Duo, Dies at 82',\n",
" u'Sunni Militants Seize Crossing on Iraq-Jordan Border',\n",
" u'ArtsBeat: \\u2018True Blood\\u2019 Recap: Back to the Beginning',\n",
" u'New Search Plan for Flight 370 Is Based on Farther, Controlled Flying',\n",
" u'Vice Has Many Media Giants Salivating, but Its Terms Will Be Rich',\n",
" u'Egyptian Court Convicts 3 Al Jazeera Journalists',\n",
" u'Egyptian Court Convicts 3 Al Jazeera Journalists',\n",
" u'Baptism by Fire: A New York Firefighter Confronts His First Test',\n",
" u'Soldier Accused of Killing 5 Is Captured in South Korea',\n",
" u'DealBook: An Employee Dies, and the Company Collects the Insurance',\n",
" u'A Survey Says: Poll Shows No Consensus in U.S. for Helping in Iraq',\n",
" u'Netherlands and Chile Will Fight to Win Group B']"
]
}
],
"prompt_number": 20
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##Conclusion\n",
"\n",
"By the end of this tutorial, you should feel confident in your ability to extract information from HTML and XML documents. There are a lot of subtleties we didn't go over, but you're well on your way! Here are some further links to aid in your exploration.\n",
"\n",
"* [A Gentle Introduction to XML](http://www.tei-c.org/release/doc/tei-p5-doc/en/html/SG.html), from [TEI](http://www.tei-c.org/index.xml).\n",
"* [Intro to Beautiful Soup](http://programminghistorian.org/lessons/intro-to-beautiful-soup)"
]
}
],
"metadata": {}
}
]
}