Search by

belisoful / prado-webextract

belisoful

Fetch web pages safely and reduce them to clean text and metadata, for PRADO.

Package info

github.com/belisoful/prado-webextract

Type:prado4-extension

pkg:composer/belisoful/prado-webextract

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-11-04 05:22 UTC

This package is auto-updated.

Last update: 2026-11-04 05:22:37 UTC


README

Fetch web pages safely and reduce them to clean text and metadata, for PRADO.

Give it a web address and it keeps, in one table row per address:

  • the article as plain text, without the navigation, advertising, and footer around it;
  • the title, summary, author, site name, language, representative image, and publication date;
  • keywords, the canonical address, feeds, and a provider's embed code for a video, when the page offers them.

It is built for addresses the public submits. Private, loopback, and link-local networks are refused, including behind redirects and in IPv6 forms, and every request has limits on size, time, and redirects.

The work is done by established libraries:

Job Library
Fetching, and the private network checks symfony/http-client
The article text fivefilters/readability.php, a port of Firefox's Reader View
Metadata: Open Graph, Twitter cards, JSON-LD, oEmbed embed/embed

Installation

composer require belisoful/prado-webextract

It requires PHP 8.2 or newer and PRADO 4.4.

Configuration

<modules>
	<module id="db" class="TDataSourceConfig">
		<database ConnectionString="mysql:host=localhost;dbname=site" Username="site" Password="..." />
	</module>
	<module id="webextract" class="TWebExtractor" ConnectionID="db" />
</modules>

The table (web_extracts by default) is created on first use.

Property Default
TableName web_extracts
AutoCreateTables true
MaxAttempts 3 tries before an address fails for good
RetryDelay 3600 seconds before the first retry; each later one waits twice as long
LeaseTime 300 seconds a queue worker holds an address
StripParameters tracking parameters query parameters taken off an address (utm_*, fbclid, ...)
ProcessOnEndRequest 0 addresses fetched after each response, for hosts with no cron
Timeout 10 seconds to wait for a server
MaxDuration 30 seconds a whole fetch may take
MaxBytes 2097152 the largest page read
MaxRedirects 5
UserAgent identifies the package
AllowPrivateNetworks false never for addresses the public submits
ReadMetadata true read metadata with Embed, which may ask a provider's oEmbed service

Use

Fetching takes seconds, so queue addresses as they arrive and fetch them from cron:

$extractor = $this->getApplication()->getModule('webextract');
$extractor->queueAll($urls);
<module id="cron" class="Prado\Util\Cron\TCronModule">
	<job Name="webextract" Schedule="* * * * *" Task="webextract->processQueue(10)" />
</module>

Then read what was learned:

foreach ($extractor->findAll($urls) as $url => $extract) {
	if ($extract->getIsExtracted()) {
		echo $extract->getTitle(), ' - ', $extract->getSiteName(), "\n", $extract->getDescription();
	}
}

extract($url) fetches one address immediately for a caller that can wait. An address is fetched once; extract($url, true) fetches it again.

A failure that could go differently later (a timeout, a 503) is retried; one that cannot (a 404, a private address, a page too large) fails at once. PDFs, images, and other files that are not text are marked unsupported without being downloaded.

Events: onExtracting (after reading, before storing, so a handler can change the extract, for example putting a video's transcript in its text), onExtracted, and onExtractFailed.

The text is plain text and safe to show escaped. The image, canonical, icon, author, provider, and feed addresses are kept only when they are http or https ones, but they are still the page's choice. The embed meta entry holds a provider's embed HTML, which is third-party markup: decide whether to trust the provider before showing it.

extract($url, true) fetches an address again. When that fails in a way that could go differently later (a timeout, a 503), the earlier extract is kept, with the failure in its getError().

The table works on SQLite, MySQL, and PostgreSQL, and is created on first use. TableName is a plain SQL name, optionally after a schema name: news.web_extracts.

Development

composer fulltest runs the full check: compile, code style, static analysis, unit tests. composer integration installs the package into a throwaway PRADO application and checks it. See AGENTS.md for the conventions this package holds to, and its Security section before changing how anything is fetched.