Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ const metascraper = require('metascraper')([

- [metascraper-audio](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-audio) – Get audio property from HTML markup.
- [metascraper-author](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-author) – Get author property from HTML markup.
- [metascraper-citation](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-citation) – Get title, author, date, and publisher from Highwire Press / Google Scholar `citation_*` tags.
- [metascraper-date](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-date) – Get date property from HTML markup.
- [metascraper-defuddle](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-defuddle) – A Defuddle connector for metascraper.
- [metascraper-description](https://github.com/microlinkhq/metascraper/tree/master/packages/metascraper-description) – Get description property from HTML markup.
Expand Down
4 changes: 4 additions & 0 deletions packages/metascraper-citation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
36 changes: 36 additions & 0 deletions packages/metascraper-citation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<div align="center">
<br>
<img style="width: 500px; margin:3rem 0 1.5rem;" src="https://metascraper.js.org/static/logo-banner.png" alt="metascraper">
<br>
<br>
<p align="center"><strong>metascraper-citation</strong>: Get title, author, date, and publisher from Highwire Press / Google Scholar <code>citation_*</code> meta tags.</p>
<p align="center">See our <a href="https://metascraper.js.org" target='_blank' rel='noopener noreferrer'>website</a> for more information.</p>
<br>
</div>

## Install

```bash
$ npm install metascraper-citation --save
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

Load it **before** the generic property packages so Highwire values win over Open Graph (e.g. a clean `citation_title` instead of `og:title` with a site suffix).

```js
const metascraper = require('metascraper')([
require('metascraper-citation')(),
require('metascraper-author')(),
require('metascraper-date')(),
require('metascraper-publisher')(),
require('metascraper-title')()
])
```

The bundle is a no-op unless the HTML has at least one `citation_*` tag. Missing fields (ScienceDirect often omits `citation_author`) fall through to the generic rules.

## License

**metascraper-citation** © [Microlink](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/metascraper/blob/master/LICENSE.md) License.<br>
Authored and maintained by [Microlink](https://microlink.io) with help from [contributors](https://github.com/microlinkhq/metascraper/contributors).

> [microlink.io](https://microlink.io) · GitHub [microlinkhq](https://github.com/microlinkhq) · X [@microlinkhq](https://x.com/microlinkhq)
49 changes: 49 additions & 0 deletions packages/metascraper-citation/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "metascraper-citation",
"description": "Metascraper rules for Highwire Press / Google Scholar citation_* meta tags.",
"homepage": "https://github.com/microlinkhq/metascraper/packages/metascraper-citation",
Comment thread
Kikobeats marked this conversation as resolved.
"version": "5.55.2",
"types": "src/index.d.ts",
"main": "src/index.js",
"author": {
"email": "hello@microlink.io",
"name": "microlink.io",
"url": "https://microlink.io"
},
"repository": {
"directory": "packages/metascraper-citation",
"type": "git",
"url": "git+https://github.com/microlinkhq/metascraper.git"
},
"bugs": {
"url": "https://github.com/microlinkhq/metascraper/issues"
},
"keywords": [
"citation",
"extract",
"google-scholar",
"highwire",
"html",
"metadata",
"metascraper",
"scraper",
"scholar"
],
"dependencies": {
"@metascraper/helpers": "workspace:*"
},
"devDependencies": {
"ava": "8",
"cheerio": "latest"
},
"engines": {
"node": ">= 22"
},
"files": [
"src"
],
"scripts": {
"test": "NODE_PATH=.. TZ=UTC ava --timeout 15s"
},
"license": "MIT"
}
3 changes: 3 additions & 0 deletions packages/metascraper-citation/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function rules(): import('metascraper').Rules;

export = rules;
49 changes: 49 additions & 0 deletions packages/metascraper-citation/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

const {
author,
date,
memoizeOne,
publisher,
title,
toRule
} = require('@metascraper/helpers')

const toAuthor = toRule(author)
const toDate = toRule(date)
const toPublisher = toRule(publisher)
const toTitle = toRule(title)

const test = memoizeOne(
$ => $('meta[name^="citation_" i]').length > 0,
memoizeOne.EqualityFirstArgument
)

module.exports = () => {
const rules = {
title: [toTitle($ => $('meta[name="citation_title" i]').attr('content'))],
author: [
toAuthor($ => $('meta[name="citation_author" i]').attr('content'))
],
date: [
toDate($ =>
$('meta[name="citation_publication_date" i]').attr('content')
),
toDate($ => $('meta[name="citation_online_date" i]').attr('content')),
toDate($ => $('meta[name="citation_date" i]').attr('content'))
],
publisher: [
toPublisher($ => $('meta[name="citation_publisher" i]').attr('content')),
toPublisher($ =>
$('meta[name="citation_journal_title" i]').attr('content')
)
]
}

rules.test = ({ htmlDom }) => test(htmlDom)
rules.pkgName = 'metascraper-citation'

return rules
}

module.exports.test = test
117 changes: 117 additions & 0 deletions packages/metascraper-citation/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use strict'

const { load } = require('cheerio')
const test = require('ava').default

const { test: hasCitation } = require('..')

const metascraper = require('metascraper')([
require('..')(),
require('metascraper-author')(),
require('metascraper-date')(),
require('metascraper-publisher')(),
require('metascraper-title')(),
require('metascraper-url')()
])

const sciencedirect = `
<meta property="og:title" content="Genetic monitoring of the critically endangered leatherback turtle (Dermochelys coriacea) in the South West Atlantic">
<title>Genetic monitoring of the critically endangered leatherback turtle (Dermochelys coriacea) in the South West Atlantic - ScienceDirect</title>
<meta name="citation_title" content="Genetic monitoring of the critically endangered leatherback turtle (Dermochelys coriacea) in the South West Atlantic">
<meta name="citation_doi" content="10.1016/j.rsma.2022.102530">
<meta name="citation_publisher" content="Elsevier">
<meta name="citation_journal_title" content="Regional Studies in Marine Science">
<meta name="citation_publication_date" content="2022/09/01">
<meta name="citation_online_date" content="2022/07/08">
`

const plos = `
<meta name="citation_title" content="Genome-Wide Association Study of 14,000 Cases of Seven Common Diseases">
<meta name="citation_author" content="Alice Smith">
<meta name="citation_author" content="Bob Jones">
<meta name="citation_publication_date" content="2006/12/20">
<meta name="citation_publisher" content="Public Library of Science">
<meta name="citation_journal_title" content="PLOS ONE">
`

test('test() is true when citation tags are present', t => {
t.true(hasCitation(load(sciencedirect)))
t.true(hasCitation(load(plos)))
})

test('test() is false when citation tags are absent', t => {
t.false(hasCitation(load('<title>Example</title>')))
})

test('citation_title wins over og:title and title', async t => {
const metadata = await metascraper({
html: sciencedirect,
url: 'https://www.sciencedirect.com/science/article/abs/pii/S2352485522001888'
})
t.is(
metadata.title,
'Genetic monitoring of the critically endangered leatherback turtle (Dermochelys coriacea) in the South West Atlantic'
)
})

test('first citation_author only', async t => {
const metadata = await metascraper({
html: plos,
url: 'https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0000001'
})
t.is(metadata.author, 'Alice Smith')
})

test('no citation_author falls through to meta[name=author]', async t => {
const html = `
${sciencedirect}
<meta name="author" content="Jane Doe">
`
const metadata = await metascraper({
html,
url: 'https://www.sciencedirect.com/science/article/abs/pii/S2352485522001888'
})
t.is(metadata.author, 'Jane Doe')
})

test('citation_publication_date is parsed', async t => {
const metadata = await metascraper({
html: sciencedirect,
url: 'https://www.sciencedirect.com/science/article/abs/pii/S2352485522001888'
})
t.is(metadata.date, '2022-09-01T12:00:00.000Z')
})

test('citation_publisher wins over citation_journal_title', async t => {
const metadata = await metascraper({
html: sciencedirect,
url: 'https://www.sciencedirect.com/science/article/abs/pii/S2352485522001888'
})
t.is(metadata.publisher, 'Elsevier')
})

test('citation_journal_title is used when publisher is absent', async t => {
const html = `
<meta name="citation_title" content="A paper">
<meta name="citation_journal_title" content="Regional Studies in Marine Science">
`
const metadata = await metascraper({
html,
url: 'https://www.sciencedirect.com/science/article/abs/pii/S2352485522001888'
})
t.is(metadata.publisher, 'Regional Studies in Marine Science')
})

test('pages without citation tags use generic rules', async t => {
const html = `
<title>Example page - Site</title>
<meta property="og:title" content="Example page">
<meta name="author" content="Nina Totenberg">
`
const metadata = await metascraper({
html,
url: 'https://www.npr.org/2022/06/24/example'
})
t.is(metadata.title, 'Example page')
t.is(metadata.author, 'Nina Totenberg')
})