--- title: "scholar: Analyse Citation Data from Google Scholar" author: "Guangchuang Yu, James Keirstead and Gregory Jefferis" date: "`r Sys.Date()`" format: html: toc: true theme: cosmo code-fold: false execute: message: false warning: false freeze: auto vignette: > %\VignetteIndexEntry{scholar introduction} %\VignetteEngine{quarto::html} %\usepackage[utf8]{inputenc} --- ```{r} #| label: setup #| echo: false #| results: hide knitr::opts_chunk$set( tidy = FALSE, message = FALSE, warning = FALSE, fig.width = 7, fig.height = 4 ) has_scholar <- yulab.utils::has_internet("https://scholar.google.com") run_examples <- has_scholar && identical(tolower(Sys.getenv("SCHOLAR_RUN_VIGNETTE_EXAMPLES")), "true") ``` ```{r} #| label: libraries #| echo: false #| results: hide library("scholar") library("ggplot2") theme_set(theme_minimal()) ``` # Overview The **scholar** package extracts citation and publication data from Google Scholar profiles. It can be used to: * find Google Scholar profile IDs; * retrieve profile metadata, publication records, and citation histories; * calculate citation metrics such as h-index, g-index, i10-index, i50-index, and i100-index; * inspect article-level metadata and article citation histories; * complete truncated author lists; * compare multiple scholars; * explore coauthor networks; * format publication lists for CVs; * match publication venues against SCImago journal rankings; and * predict future h-index values. Google Scholar does not provide an official public API. Results can change when Google changes page markup, and repeated requests may be rate limited. The examples below use small requests and are skipped automatically when Google Scholar is not reachable. # Scholar IDs Most functions need a Google Scholar profile ID. In a profile URL such as `https://scholar.google.com/citations?user=B7vSqZsAAAAJ`, the ID is `B7vSqZsAAAAJ`. ```{r ids-basic} id <- "B7vSqZsAAAAJ" ``` If you have a URL or an ID with trailing query parameters, `tidy_id()` keeps only the Scholar ID. ```{r tidy-id} tidy_id("https://scholar.google.com/citations?user=B7vSqZsAAAAJ&hl=en") tidy_id("B7vSqZsAAAAJ&hl=en") ``` You can search for one matching ID with `get_scholar_id()`. ```{r get-scholar-id, eval=run_examples} get_scholar_id(first_name = "Richard", last_name = "Feynman") ``` For ambiguous names, `search_scholar_ids()` returns all author-search matches it can find, including IDs, affiliations, verified email text, research interests, and profile URLs. `max_pages` limits pagination so that examples and scripts do not make excessive requests. ```{r search-scholar-ids, eval=run_examples} search_scholar_ids("hao xu", max_pages = 1) ``` # Profile Data `get_profile()` returns a list with name, affiliation, citation metrics, fields, homepage, coauthors, and availability counts. ```{r profile, eval=run_examples} profile <- get_profile(id) profile$name profile[c("total_cites", "h_index", "i10_index")] profile$fields ``` # Publications `get_publications()` returns a data frame of publications from a Scholar profile. It includes the publication title, displayed author list, venue text, volume/page details, citation count, year, the Google Scholar citation ID (`cid`), and the publication ID (`pubid`). ```{r publications, eval=run_examples} pubs <- get_publications(id, pagesize = 20) head(pubs, 3) ``` You can sort the profile page by citation count or by publication year. ```{r publications-sort, eval=run_examples} head(get_publications(id, pagesize = 10, sortby = "year"), 3) ``` Google Scholar sometimes truncates long author lists with `...`. Use `get_publications_all_authors()` when you need the complete author list for those rows. It only calls `get_complete_authors()` for rows that appear to be truncated. ```{r all-authors, eval=FALSE} pubs_full <- get_publications_all_authors(id, pagesize = 20, delay = 1) head(pubs_full, 3) ``` For a single publication ID, call `get_complete_authors()` directly. ```{r complete-authors, eval=run_examples} get_complete_authors(id, pubs$pubid[1], delay = 0) ``` `author_position()` helps analyse where a named author appears in publication author lists. `Position_Normalized` is 0 for first author, 1 for last author, and values between 0 and 1 for middle positions. ```{r author-position, eval=run_examples} author_position(pubs$author[1:5], profile$name) ``` # Citation Histories `get_citation_history()` retrieves the yearly citation bar chart for a scholar. ```{r citation-history, eval=run_examples} ct <- get_citation_history(id) ct ``` ```{r citation-history-plot, eval=run_examples} ggplot(ct, aes(year, cites)) + geom_line() + geom_point() + labs(x = "Year", y = "Citations") ``` `get_article_cite_history()` retrieves the yearly citation history for one publication. Missing years are filled with zero citations. ```{r article-citation-history, eval=run_examples} article_history <- get_article_cite_history(id, pubs$pubid[1]) article_history ``` ```{r article-citation-history-plot, eval=run_examples} ggplot(article_history, aes(year, cites)) + geom_segment(aes(xend = year, yend = 0), linewidth = 1, colour = "grey70") + geom_point(size = 3, colour = "firebrick") + labs(x = "Year", y = "Citations") ``` # Citation Metrics `get_publication_metrics()` calculates metrics from a publication data frame. `get_scholar_metrics()` fetches publications and calculates the same summary in one call. ```{r publication-metrics, eval=run_examples} get_publication_metrics(pubs) get_scholar_metrics(id, pagesize = 20) ``` The package also includes focused helpers used by the h-index prediction model. ```{r publication-helpers, eval=run_examples} get_num_articles(id) get_oldest_article(id) get_num_distinct_journals(id) get_num_top_journals(id) ``` # Publication Details Google Scholar publication detail pages can expose additional fields. These helpers accept a Scholar ID and a publication ID. ```{r publication-detail, eval=run_examples} pubid <- pubs$pubid[1] get_article_scholar_url(id, pubid) get_publication_url(id, pubid) get_publication_date(id, pubid) get_publication_abstract(id, pubid) get_publication_data_extended(id, pubid) ``` Some publications have no linked PDF, abstract, or publication date on Google Scholar. In those cases the corresponding helper may return an empty value or `NA`. # Comparing Scholars `compare_scholars()` compares citation totals from publication records by publication year. ```{r compare-scholars, eval=run_examples} ids <- c("B7vSqZsAAAAJ", "DO5oG40AAAAJ") cs <- compare_scholars(ids) head(cs, 3) ``` ```{r compare-scholars-plot, eval=run_examples} cs <- dplyr::filter(cs, !is.na(year) & year > 1900) ggplot(cs, aes(year, cites, colour = name, group = name)) + geom_line() + labs(x = "Publication year", y = "Citations", colour = NULL) + theme(legend.position = "bottom") ``` `compare_scholar_careers()` compares citation histories after aligning each scholar by career year. ```{r compare-careers, eval=run_examples} csc <- compare_scholar_careers(ids) head(csc, 3) ``` ```{r compare-careers-plot, eval=run_examples} ggplot(csc, aes(career_year, cites, colour = name, group = name)) + geom_line() + geom_point() + labs(x = "Career year", y = "Citations", colour = NULL) + theme(legend.position = "bottom") ``` # Coauthor Networks `get_coauthors()` retrieves the coauthors listed by Google Scholar for a profile and can optionally expand the network by following coauthors' profiles. Keep `n_coauthors` and `n_deep` small for exploratory work; network plots get messy quickly and repeated requests can be rate limited. ```{r coauthors, eval=run_examples} coauthor_network <- get_coauthors("DO5oG40AAAAJ", n_coauthors = 4, n_deep = 0) coauthor_network ``` ```{r coauthors-plot, eval=run_examples} if (nrow(coauthor_network) > 1) { plot_coauthors(coauthor_network) } ``` # Journal Rankings `get_journalrank()` downloads SCImago journal ranking data. With no `journals` argument it returns the full table. With a character vector it matches journal names to SCImago rows using approximate matching. ```{r journalrank, eval=run_examples} jr <- get_journalrank() head(jr, 3) get_journalrank(c("Nature", "Science")) ``` # Predicting Future h-index `predict_h_index()` implements the model from Acuna et al. for predicting future h-index values. The original model was calibrated on neuroscientists, so results for other fields should be treated as illustrative rather than authoritative. ```{r predict-h-index, eval=run_examples} predict_h_index("GAi23ssAAAAJ") ``` You can provide a custom list of top journals for the model's top-journal predictor. ```{r predict-h-index-custom, eval=FALSE} predict_h_index( id, journals = c("Nature", "Science", "Proceedings of the National Academy of Sciences") ) ``` # Formatting Publications for CVs `format_publications()` returns publication strings that can be printed in R Markdown, Quarto, or a CV workflow. The `author.name` argument highlights a chosen author in bold after converting the supplied name to the abbreviated format used by the output. ```{r format-publications-asis, results="asis", eval=run_examples} format_publications("DO5oG40AAAAJ", "Guangchuang Yu") |> head() |> cat(sep = "\n\n") ``` For numbered output, print the returned vector directly. ```{r format-publications-print, eval=run_examples} format_publications("DO5oG40AAAAJ", "Guangchuang Yu") |> head() |> print(quote = FALSE) ``` # Mirrors, Retries, and Caching Use `set_scholar_mirror()` if you need to point the package at a compatible Scholar mirror. ```{r mirror, eval=FALSE} set_scholar_mirror("https://scholar.google.com") ``` All network requests go through `get_scholar_resp()`, which stores Google Scholar session cookies and retries transient failures. `get_publications()` also caches profile publication data with `R.cache` in a temporary cache directory. Use `flush = TRUE` in `get_publications()` or functions that forward to it when you need fresh data. ```{r flush-cache, eval=FALSE} get_publications(id, flush = TRUE) get_scholar_metrics(id, flush = TRUE) ``` For large workflows, prefer fetching each scholar's publications once and then reusing the returned data frame for multiple summaries. ```{r reusable-publications, eval=FALSE} pubs <- get_publications(id) recent <- subset(pubs, !is.na(year) & year >= 2020) nrow(recent) sum(recent$cites, na.rm = TRUE) get_publication_metrics(recent) ```