public static function newFromPage( $url ) {
		// Set up HTTP client and HTML document.
		$tempClient = new Client( [ 'headers' => [ 'User-Agent' => 'addwiki-mediawiki-client' ] ] );
		$pageHtml = $tempClient->get( $url )->getBody();
		$pageDoc = new DOMDocument();

		// Try to load the HTML (turn off errors temporarily; most don't matter, and if they do get
		// in the way of finding the API URL, will be reported in the RsdException below).
		$internalErrors = libxml_use_internal_errors( true );
		$pageDoc->loadHTML( $pageHtml );
		$libXmlErrors = libxml_get_errors();
		libxml_use_internal_errors( $internalErrors );

		// Extract the RSD link.
		$xpath = 'head/link[@type="application/rsd+xml"][@href]';
		$link = ( new DOMXpath( $pageDoc ) )->query( $xpath );
		if ( $link->length === 0 ) {
			// Format libxml errors for display.
			$libXmlErrorStr = array_reduce( $libXmlErrors, function ( $prevErr, $err ) {
				return $prevErr . ', ' . $err->message . ' (line '.$err->line . ')';
			} );
			if ( $libXmlErrorStr ) {
				$libXmlErrorStr = "In addition, libxml had the following errors: $libXmlErrorStr";
			}
			throw new RsdException( "Unable to find RSD URL in page: $url $libXmlErrorStr" );
		}
		$rsdUrl = $link->item( 0 )->attributes->getnamedItem( 'href' )->nodeValue;

		// Then get the RSD XML, and return the API link.
		$rsdXml = new SimpleXMLElement( $tempClient->get( $rsdUrl )->getBody() );
		return self::newFromApiEndpoint( (string)$rsdXml->service->apis->api->attributes()->apiLink );
	}