Relative Link Resolution [Universal Feed Parser]
Keywords: RSS, Atom, CDF, XML, feed, parser, Python

# Universal Feed Parser

Parse RSS and Atom feeds in Python. 3000 unit tests. Open source.

- Download ·
- Documentation ·
- Unit tests ·
- Report a bug

## Relative Link Resolution

###

Many feed elements and attributes are URIs. Universal Feed Parser resolves relative URIs according to the XML:Base specification. We'll see how that works in a minute, but first let's talk about which values are treated as URIs.

### Which Values Are URIs

These feed elements are treated as URIs, and resolved if they are relative:

- feed.link
- feed.links[i].href
- feed.generator_detail.href
- feed.id
- feed.image.href
- feed.image.link
- feed.textinput.link
- feed.author_detail.href
- feed.publisher_detail.href
- feed.contributors[i].href
- feed.docs
- feed.license
- entries[i].link
- entries[i].links[j].href
- entries[i].id
- entries[i].author_detail.href
- entries[i].publisher_detail.href
- entries[i].contributors[j].href
- entries[i].enclosures[j].href
- entries[i].source.author_detail.href
- entries[i].source.contributors[j].href
- entries[i].source.links[j].href
- entries[i].comments
- entries[i].license

In addition, several feed elements may contain HTML or XHTML markup. Certain elements and attributes in HTML can be relative URIs, and Universal Feed Parser will resolve these URIs according to the same rules as the feed elements listed above.

These feed elements may contain HTML or XHTML markup. In Atom feeds, whether these elements are treated as HTML depends on the value of the `type` attribute. In RSS feeds, these values are always treated as HTML.

- feed.title (feed.title_detail.value)
- feed.subtitle (feed.subtitle_detail.value))
- feed.info (feed.info_detail.value)
- feed.rights (feed.rights_detail.value)
- entries[i].title (entries[i].title_detail.value)
- entries[i].summary (entries[i].summary_detail.value)
- entries[i].content[j].value

When any of these feed elements contains HTML or XHTML markup, the following HTML elements are treated as URIs and are resolved if they are relative:

- `<a href="...">`
- `<applet codebase="...">`
- `<area href="...">`
- `<blockquote cite="...">`
- `<body background="...">`
- `<del cite="...">`
- `<form action="...">`
- `<frame longdesc="...">`
- `<frame src="...">`
- `<iframe longdesc="...">`
- `<iframe src="...">`
- `<head profile="...">`
- `<img longdesc="...">`
- `<img src="...">`
- `<img usemap="...">`
- `<input src="...">`
- `<input usemap="...">`
- `<ins cite="...">`
- `<link href="...">`
- `<object classid="...">`
- `<object codebase="...">`
- `<object data="...">`
- `<object usemap="...">`
- `<q cite="...">`
- `<script src="...">`

### How Relative URIs Are Resolved

Universal Feed Parser resolves relative URIs according to the XML:Base specification. This defines a hierarchical inheritance system, where one element can define the base URI for itself and all of its child elements, using an `xml:base` attribute. A child element can then override its parent's base URI by redeclaring `xml:base` to a different value.

If no `xml:base` is specified, the feed has a default base URI defined in the `Content-Location` HTTP header.

If no `Content-Location` HTTP header is present, the URL used to retrieve the feed itself is the default base URI for all relative links within the feed. If the feed was retrieved via an HTTP redirect (any HTTP 3xx status code), then the final URL of the feed is the default base URI.

For example, an `xml:base` on the root-level element sets the base URI for all URIs in the feed.

### Example: `xml:base` on the root-level element

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/base.xml")
    >>> d.feed.link
    u'http://example.org/index.html'
    >>> d.feed.generator_detail.href
    u'http://example.org/generator/'

An `xml:base` attribute on an `<entry>` overrides the `xml:base` on the parent `<feed>`.

### Example: Overriding `xml:base` on an `<entry>`

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/base.xml")
    >>> d.entries[0].link
    u'http://example.org/archives/000001.html'
    >>> d.entries[0].author_detail.href
    u'http://example.org/about/'

An `xml:base` on `<content>` overrides the `xml:base` on the parent `<entry>`. In addition, whatever the base URI is for the `<content>` element (whether defined directly on the `<content>` element, or inherited from the parent element) is used as the base URI for the embedded HTML or XHTML markup within the `content`.

### Example: Relative links within embedded HTML

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/base.xml")
    >>> d.entries[0].content[0].value
    u'<p id="anchor1"><a href="http://example.org/archives/000001.html#anchor2">skip to anchor 2</a></p>
     <p>Some content</p>
     <p id="anchor2">This is anchor 2</p>'

The `xml:base` affects other attributes in the element in which it is declared.

### Example: `xml:base` and sibling attributes

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/base.xml")
    >>> d.entries[0].links[1].rel
    u'service.edit'
    >>> d.entries[0].links[1].href
    u'http://example.com/api/client/37'

If no `xml:base` is specified on the root-level element, the default base URI is given in the `Content-Location` HTTP header. This can still be overridden by any child element that declares an `xml:base` attribute.

### Example: `Content-Location` HTTP header

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/http_base.xml")
    >>> d.feed.link
    u'http://example.org/index.html'
    >>> d.entries[0].link
    u'http://example.org/archives/000001.html'

Finally, if no root-level `xml:base` is declared, and no `Content-Location` HTTP header is present, the URL of the feed itself is the default base URI. Again, this can still be overridden by any element that declares an `xml:base` attribute.

### Example: Feed URL as default base URI

    >>> import feedparser
    >>> d = feedparser.parse("http://feedparser.org/docs/examples/no_base.xml")
    >>> d.feed.link
    u'http://feedparser.org/docs/examples/index.html
    >>> d.entries[0].link
    u'http://example.org/archives/000001.html'

← Namespace Handling
Feed Type and Version Detection →