(Ideas, proof-reading and editing by me, initial draft by Claude)
Every academic struggles with finding enough hours in the day: often its most apparent in the personal website that's eighteen months out of date. You publish something, you mean to add it, you don't, and eventually the site quietly undersells what you have accomplished to those who come looking.
Google Scholar already maintains the up-to-date version of that page. It knows your papers, your citation counts, your h-index, and it updates them without being asked. So instead of maintaining two copies of the truth, my website simply copies the Scholar one, and then spruces it up with some scripts (like putting my name in bold).
This post walks through the three snippets that do it, in PHP (they slot straight into WordPress via a code-snippets plugin — the setup my site used when I wrote them). The idea transfers to any language: fetch the profile page, pick out the right element, print it.
The underlying idea
A Google Scholar profile is just a web page, and every number on it lives at a predictable address inside the page's HTML — an XPath, which is to a web page what a grid reference is to a map. The statistics table on your profile sits in an element with the id gsc_rsb_st; the total-citations number is the second cell of its first row; the h-index is the second cell of the second row. Fetch the page, ask for the cell, and you have the number.
Everything below is that trick applied in increasing complex ways.
Total citations
Replace [USERNAME] with the id from your own profile's URL (the user=... part) and [LANGUAGE] with your language code (en for English).
<?php
$profile = "https://scholar.google.com/citations?user=[USERNAME]&hl=[LANGUAGE]&oi=ao";
$contents = file_get_contents($profile);
$citations_xpath = '//*[@id="gsc_rsb_st"]/tbody/tr[1]/td[2]';
$dom = new DOMDocument();
@$dom->loadHTML($contents);
$xpath = new DOMXPath($dom);
$citations = $xpath->query($citations_xpath);
echo $citations->item(0)->nodeValue;
?>In WordPress, paste this into a new snippet, choose "only display when inserted into a post or page", and you get a shortcode you can drop anywhere — inside a sentence, on a button, wherever the number should live.
H-index
Identical, one row further down the table:
<?php
$profile = "https://scholar.google.com/citations?user=[USERNAME]&hl=[LANGUAGE]&oi=ao";
$contents = file_get_contents($profile);
$hindex_xpath = '//*[@id="gsc_rsb_st"]/tbody/tr[2]/td[2]';
$dom = new DOMDocument();
@$dom->loadHTML($contents);
$xpath = new DOMXPath($dom);
$hindex = $xpath->query($hindex_xpath);
echo $hindex->item(0)->nodeValue;
?>The whole publication table
The full version: walk the publications table on your profile and rebuild it as a table on your own site — title (linked), authors, venue, citations, year. [SORT] takes pubdate for newest-first or citedby for most-cited-first, and [KEY AUTHOR] bolds your own name in every author list (for me: B Wooding).
<?php
$baseUrl = "https://scholar.google.com";
$profile = "/citations?hl=en&user=[USERNAME]&view_op=list_works&hl=[LANGUAGE]&sortby=[SORT]";
$contents = file_get_contents($baseUrl.$profile);
$citations_xpath = '//*[@id="gsc_a_b"]';
$dom = new DOMDocument();
@$dom->loadHTML($contents);
$xpath = new DOMXPath($dom);
$table = $xpath->query($citations_xpath);
$records = [];
foreach ($table as $row) {
$trs = $row->getElementsByTagName('tr');
foreach ($trs as $tr) {
$td = $tr->getElementsByTagName('td')->item(0);
$title = $td->getElementsByTagName('a')->item(0)->nodeValue;
$titleHref = $td->getElementsByTagName('a')->item(0)->getAttribute('href');
$authors = $td->getElementsByTagName('div')->item(0)->nodeValue;
$conference = $td->getElementsByTagName('div')->item(1)->nodeValue;
$cites = $tr->getElementsByTagName('td')->item(1)->nodeValue;
$year = $tr->getElementsByTagName('td')->item(2)->nodeValue;
$keyAuthor = "[KEY AUTHOR]";
$authors = str_replace($keyAuthor, "<strong>$keyAuthor</strong>", $authors);
$records[] = [
'title' => $title,
'titleHref' => $baseUrl.$titleHref,
'authors' => $authors,
'conference' => $conference,
'cites' => $cites,
'year' => $year,
];
}
}
?>Then render $records however your site likes its tables — mine zebra-stripes the rows and puts authors and venue in small grey text under each title.
Some Caveats
There is no promise the pages will not change, so three warnings from experience:
The grid references move. Those XPaths are correct for Google Scholar's current layout. When Google redesigns the page — every few years, historically — the cells shift and your website starts printing nothing. The fix is usually a two-minute XPath update, but you have to notice…
Google may decline to answer. A plain
file_get_contentsfrom a server that asks too often can get rate-limited or blocked. Be polite: cache the result (your h-index does not change by the hour) and fetch at most daily. If you want a supported route, a paid service like SerpApi does the fetching with an actual API contract — I keep a WordPress-ready version of that approach in a separate repository.Check the terms. You're reading your own public profile, which sits at the gentle end of the scraping spectrum, but automated access to Google properties lives in a grey zone of their terms of service. A personal site fetching one page once a day is a very different animal from bulk harvesting — stay the first animal.

