Documentation
¶
Overview ¶
Package feedparser parses syndication feeds into one canonical model.
Seven formats are supported — RSS 0.90, 0.91, 0.92, 1.0 and 2.0, Atom 1.0, and JSON Feed 1.0/1.1 — and the format is detected from the input rather than declared by the caller. Both entry points return a model.Feed; the format-specific data that has no cross-format equivalent is preserved on the Feed.RSS, Feed.Atom and Feed.JSONFeed branches.
Errors are typed: DetectionError when the format cannot be determined, ParseError when the content is malformed or fails validation, and NetworkError for HTTP and transport failures. Match them with errors.As. Each carries its cause, so errors.Is reaches the sentinels that classify what went wrong — ErrUnknownFormat, ErrMalformedXML, ErrMissingRequired and ErrTooLarge — and reaches context.Canceled for a cancelled fetch.
Index ¶
- Constants
- Variables
- func ParseFromSource(ctx context.Context, r io.Reader, opts ...Option) (*model.Feed, error)
- func ParseFromURL(ctx context.Context, url string, opts ...Option) (*model.Feed, error)
- type DetectionError
- type NetworkError
- type Option
- func WithAccept(accept string) Option
- func WithContentType(contentType string) Option
- func WithETag(etag string) Option
- func WithHTTPClient(client *http.Client) Option
- func WithLastModified(t time.Time) Option
- func WithMaxSize(maxSize int64) Option
- func WithTimeout(timeout time.Duration) Option
- func WithUserAgent(ua string) Option
- type Options
- type ParseError
- type Response
Constants ¶
const DefaultAccept = "application/atom+xml, application/rss+xml, application/feed+json, " +
"application/xml;q=0.9, application/json;q=0.9, text/xml;q=0.8, */*;q=0.5"
DefaultAccept is the Accept header sent when WithAccept is not supplied.
The feed types come first and unweighted; the generic types that a feed is also legitimately served as follow, ranked below them. The trailing */* is what keeps a server that would otherwise answer 406 serving feeds — the header is there to break a tie between representations, not to refuse any.
const DefaultMaxSize int64 = 10 << 20
DefaultMaxSize is the largest input read when WithMaxSize is not supplied.
Ten mebibytes is far above any feed a publisher intends to serve and far below what it costs to hold one. The bound exists because the size of a feed is decided by whoever serves it: without one, a response that never ends is read until the process dies.
const DefaultTimeout = 30 * time.Second
DefaultTimeout is the request timeout applied when neither WithTimeout nor WithHTTPClient is supplied.
const DefaultUserAgent = "simplest-feed-parser/1.0"
DefaultUserAgent is the User-Agent sent when WithUserAgent is not supplied.
Variables ¶
var ( // ErrUnknownFormat classifies content whose format could not be determined // — neither by inspecting it nor from any declared media type. ErrUnknownFormat = errors.New("unknown feed format") // ErrMalformedXML classifies a document one of the XML parsers rejected as // syntactically invalid. The underlying [xml.SyntaxError], which carries // the line number, remains reachable with errors.As. ErrMalformedXML = errors.New("malformed XML structure") // ErrMissingRequired classifies a document that omits an element its own // specification requires a reader to reject it for. Only Atom and JSON Feed // are validated this way; see the README's "Validation and Strictness". ErrMissingRequired = errors.New("missing required element") // ErrTooLarge classifies input that exceeded the configured maximum size. // See [WithMaxSize]. ErrTooLarge = errors.New("feed exceeds the maximum size") // ErrNotModified classifies a 304 answer to a conditional request. It is an // expected outcome of polling with [WithETag] or [WithLastModified], not a // failure — [Fetch] reports it as [Response.NotModified] and no error at // all. [ParseFromURL], having no feed to return, reports it as this. ErrNotModified = errors.New("not modified") )
Sentinel errors classifying the conditions a caller is likely to branch on.
They are never returned bare. Each is carried as the cause of one of the typed errors above, so both questions a caller asks are answerable about the same error value: errors.As names where the failure happened, and errors.Is names what went wrong.
feed, err := feedparser.ParseFromURL(ctx, url)
switch {
case errors.Is(err, feedparser.ErrTooLarge): // over the size limit
case errors.Is(err, feedparser.ErrUnknownFormat): // not a feed we know
}
Functions ¶
func ParseFromSource ¶
ParseFromSource parses a feed from the provided reader.
The whole input is read into memory before parsing begins, because the format is detected from the content and every parser then reads that content from the start. How much may be read is bounded by WithMaxSize; input past the bound fails the parse with ErrTooLarge rather than being buffered.
ctx cancels the read and the parse, not merely the fetch that preceded them. A cancelled or expired context surfaces as context.Canceled or context.DeadlineExceeded itself rather than as one of this package's typed errors: the caller withdrawing its own request is not a defect in the feed, and reporting it as a ParseError would say that it was.
func ParseFromURL ¶
ParseFromURL fetches a feed from the given URL and parses it.
It is Fetch for callers who need only the feed. Because it has nowhere to report one, a 304 answer to a conditional request surfaces as an error matching ErrNotModified; use Fetch to poll a feed properly.
Types ¶
type DetectionError ¶
type DetectionError struct {
Reason string
// Err is the cause, kept so that [errors.Is] and [errors.As] can reach it.
// Reason renders that cause for a human; Err is what a program branches on.
Err error
}
DetectionError indicates that the feed format could not be determined.
func (*DetectionError) Error ¶
func (e *DetectionError) Error() string
func (*DetectionError) Unwrap ¶ added in v0.0.5
func (e *DetectionError) Unwrap() error
Unwrap returns the cause, or nil when the error carries none.
type NetworkError ¶
type NetworkError struct {
URL string
Reason string
// StatusCode is the HTTP status the server answered with, or zero when the
// request failed before a response was read.
StatusCode int
// Err is the cause, kept so that [errors.Is] and [errors.As] can reach it.
// A request cancelled by its context unwraps to [context.Canceled], and one
// that ran out of time to [context.DeadlineExceeded].
Err error
}
NetworkError indicates an HTTP or network failure.
func (*NetworkError) Error ¶
func (e *NetworkError) Error() string
func (*NetworkError) Unwrap ¶ added in v0.0.5
func (e *NetworkError) Unwrap() error
Unwrap returns the cause, or nil when the error carries none.
type Option ¶
type Option func(*Options)
Option is a function that modifies Options.
func WithAccept ¶ added in v0.0.5
WithAccept sets the Accept header sent with a fetch, replacing DefaultAccept. An empty value sends no Accept header at all.
Reach for it when a server answers the default badly — some reject a request whose Accept they do not recognise rather than ignoring it.
func WithContentType ¶ added in v0.0.5
WithContentType declares the media type the input arrived with, for input whose type is known from somewhere other than the content — a Content-Type header, a file extension, a database column.
It is a fallback, not an override. The format is read from the content first, because a server's idea of what it is serving is wrong often enough that trusting it would break feeds that parse today: feeds are routinely served as text/plain, text/html, or application/octet-stream. The declared type is consulted only when the content names no format at all, and when a parse rests on it that is recorded on Feed.Warnings.
Fetch sets this from the response for you.
func WithETag ¶ added in v0.0.5
WithETag sends an If-None-Match header carrying the entity tag a previous fetch returned as Response.ETag.
If the feed has not changed, the server answers 304 and sends no body: Fetch reports that as Response.NotModified, and ParseFromURL, having no feed to return, as an error matching ErrNotModified.
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client for network requests.
func WithLastModified ¶ added in v0.0.5
WithLastModified sends an If-Modified-Since header carrying the time a previous fetch returned as Response.LastModified. The zero time sends no header. See WithETag for what a server answers.
func WithMaxSize ¶ added in v0.0.5
WithMaxSize bounds how many bytes are read from the input, for both ParseFromURL and ParseFromSource. Input beyond the bound is not read, and the parse fails with an error matching ErrTooLarge rather than a feed built from a truncated document.
A size of zero or less removes the bound. Do that only for input you produced yourself — a file on disk, a buffer you filled. It is not a setting to reach for because a publisher's feed grew: raise the bound to a size you are willing to hold in memory instead.
func WithTimeout ¶
WithTimeout sets the timeout for HTTP requests. It has no effect when WithHTTPClient is also supplied, because that client carries its own.
func WithUserAgent ¶
WithUserAgent sets a custom User-Agent header for HTTP requests.
type Options ¶
type Options struct {
// HTTPClient, when non-nil, is used verbatim and Timeout is ignored —
// a caller supplying their own client owns its timeout policy.
HTTPClient *http.Client
UserAgent string
Timeout time.Duration
// MaxSize bounds how many bytes are read from the input. Zero or less
// means unbounded. See [WithMaxSize].
MaxSize int64
// Accept is the Accept header sent with a fetch. Empty sends none.
Accept string
// ETag and LastModified are the validators for a conditional request. Both
// are empty or zero unless the caller supplied what a previous fetch
// returned. See [WithETag] and [WithLastModified].
ETag string
LastModified time.Time
// ContentType is the media type the input was declared as, consulted only
// when the content itself names no format. See [WithContentType].
ContentType string
}
Options holds configuration for feed parsing operations.
type ParseError ¶
type ParseError struct {
// Format names the feed format the content was parsed as. It is empty when
// the failure happened before a format was settled on.
Format string
Reason string
// Err is the cause, kept so that [errors.Is] and [errors.As] can reach it.
Err error
}
ParseError indicates that the feed content is malformed or invalid.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
func (*ParseError) Unwrap ¶ added in v0.0.5
func (e *ParseError) Unwrap() error
Unwrap returns the cause, or nil when the error carries none.
type Response ¶ added in v0.0.5
type Response struct {
// Feed is the parsed feed, or nil when NotModified is set.
Feed *model.Feed
// URL is the URL the feed was finally read from, which differs from the
// one requested when the request was redirected. Relative links in the
// document resolve against this, not against the URL asked for.
URL string
// StatusCode is the HTTP status of the response.
StatusCode int
// ETag is the entity tag the server gave this version of the feed, empty
// if it gave none. Hand it back with [WithETag] on the next poll.
ETag string
// LastModified is when the server said the feed last changed, zero if it
// said nothing or said something unparseable. Hand it back with
// [WithLastModified] on the next poll.
LastModified time.Time
// NotModified reports that the server answered 304 to a conditional
// request: the feed has not changed since the validators sent with it.
// This is the successful outcome of a poll, not a failure — Feed is nil
// and the error is nil, and the caller keeps what it already had.
NotModified bool
}
Response is what one fetch returned: the feed, and the transport facts a caller needs that a feed has nowhere to record.
The validators are the reason this type exists. A poller that re-downloads every feed on every pass wastes its own bandwidth and the publisher's, and the way not to is to send back what the server last said — see Fetch.
func Fetch ¶ added in v0.0.5
Fetch retrieves a feed over HTTP and parses it, reporting what the transport said alongside the feed.
It is ParseFromURL with room for the answers that do not fit in a model.Feed: the validators for a conditional request, the URL after redirects, and a 304 that means the caller's copy is current rather than that anything went wrong.
resp, err := feedparser.Fetch(ctx, url,
feedparser.WithETag(prev.ETag),
feedparser.WithLastModified(prev.LastModified))
if err != nil {
return err
}
if resp.NotModified {
return nil // what we have is current
}
save(resp.Feed, resp.ETag, resp.LastModified)
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
atom
Package atom parses Atom 1.0 (RFC 4287) documents and adapts them to the canonical feed model.
|
Package atom parses Atom 1.0 (RFC 4287) documents and adapts them to the canonical feed model. |
|
builder
Package builder validates a populated canonical feed before it is returned.
|
Package builder validates a populated canonical feed before it is returned. |
|
detect
Package detect identifies which feed format an input stream carries by inspecting its root element, so that the caller can select a parser without fully parsing the document first.
|
Package detect identifies which feed format an input stream carries by inspecting its root element, so that the caller can select a parser without fully parsing the document first. |
|
jsonfeed
Package jsonfeed parses JSON Feed 1.0 and 1.1 documents and adapts them to the canonical feed model.
|
Package jsonfeed parses JSON Feed 1.0 and 1.1 documents and adapts them to the canonical feed model. |
|
rss090
Package rss090 parses RSS 0.90 (RDF-based) documents and adapts them to the canonical feed model.
|
Package rss090 parses RSS 0.90 (RDF-based) documents and adapts them to the canonical feed model. |
|
rss091
Package rss091 parses RSS 0.91 documents and adapts them to the canonical feed model.
|
Package rss091 parses RSS 0.91 documents and adapts them to the canonical feed model. |
|
rss092
Package rss092 parses RSS 0.92 documents and adapts them to the canonical feed model.
|
Package rss092 parses RSS 0.92 documents and adapts them to the canonical feed model. |
|
rss10
Package rss10 parses RSS 1.0 (RDF Site Summary) documents, including the Dublin Core and syndication modules, and adapts them to the canonical model.
|
Package rss10 parses RSS 1.0 (RDF Site Summary) documents, including the Dublin Core and syndication modules, and adapts them to the canonical model. |
|
rss20
Package rss20 parses RSS 2.0 documents and adapts them to the canonical feed model.
|
Package rss20 parses RSS 2.0 documents and adapts them to the canonical feed model. |
|
util
Package util holds the date and text helpers shared by every format parser.
|
Package util holds the date and text helpers shared by every format parser. |
|
warn
Package warn collects the deviations a parse recovered from.
|
Package warn collects the deviations a parse recovered from. |
|
xmlutil
Package xmlutil holds the XML decoding behaviour shared by the format parsers and by format detection, so that decoder configuration cannot drift from one format to the next.
|
Package xmlutil holds the XML decoding behaviour shared by the format parsers and by format detection, so that decoder configuration cannot drift from one format to the next. |
|
Package model defines the canonical, format-agnostic feed model that every supported input format is adapted into, plus the per-format branches that carry data with no cross-format equivalent.
|
Package model defines the canonical, format-agnostic feed model that every supported input format is adapted into, plus the per-format branches that carry data with no cross-format equivalent. |