Hämta och behandla bibliometrisk data från SwePub

SwePub är en databas som svenska universitet och högskolor rapporterar in vetenskapliga publikationer i. Den administreras av Kungliga biblioteket och det som är intressant med denna databas är att den innehåller många fler typer av texter än de stora vetenskapliga databaserna (Scopus, Web of Science etc.). Här förekommer till exempel populärvetenskap och rapporter som inte skulle kvalificera sig för de stora databaserna.

En annan aspekt är hur denna databas kan användas för att administrera tilldelning av forskningsmedel. Men det kan vi lämna åt sidan.

En tredje aspekt, som är väldigt intressant, är att SwePub har ett API utan några begränsningar! Jämfört med de kommersiella databaserna är detta fantastiskt bra datakommunalism som bör uppmuntras!

Med lite programmering kan man enkelt hämta och behandla både stora och precisa datamängder. Här följer några snuttar Python(3)-kod som visar vad som kan göras på en eftermiddag.

Uppdatering: Det går att skippa detta steg. Fick precis reda på att man kan få datan på ett mycket enklare sätt via SwePubs (ännu beta-) funktion för uttag av data. Steget nedan innebär alltså att gå över ån efter vatten 😀

Uppdatering 2: Det verkar ändå som att man måste använda denna metod för att komma åt institutionstillhörighet. Se denna post + kommentarer.

För att hämta data anger man bara en söksträng så returnerar API:t datamängden i ett urval av format (grundläge är XML och 10 artiklar åt gången). Så här sökte jag alla de 91622 artiklar som svarade mot en sökning på lärosäte Göteborgs universitet. Först själva URLen:

http://libris.kb.se/xsearch?d=swepub&hitlist&q=lärosäte%3gu&f=ext&spell=true&hist=true&n=200&p=1

Det viktiga här är dels databasen d=swepub samt själva söksträngen q=lärosäte%3gu. Se även hur jag får den att returnera 200 artiklar. Om man vill kan man lägga till &format=json för att få tillbaka json-data. Men då kommer man dels att stöta på några datafel som beror på att SwePub verkar tillåta en del tecken som är förbjudna i json. Dessutom innehåller inte json alla datafält.

Men ett enkelt anrop ger bara 200 artiklar åt gången. Men eftersom API:t är obegränsat är det bara att fråga om och om igen. Med lite hjälp från @skagedal skapades följande skript:

from urllib.request import urlopen

counter = 1

while True:
  url = 'http://libris.kb.se/xsearch?d=swepub&hitlist&q=l%C3%A4ros%C3%A4te%3agu&f=ext&spell=true&hist=true&n=200&start=' + str(counter)
  print ("Fetching: " + url)
  data = urlopen(url).read()
  if not data.find(b'"identifier"') >= 0:
    print("No more records!")
    break
  with open(str(counter) + ".json", "wb") as outputfile:
    print("Writing file...")
    outputfile.write(data)
  counter += 200

Vad som händer här är att skriptet skriver en fil för varje omgång om 200 artiklar. Tio minuter och 240 megabytes senare har man fått hem alla nittio tusen referenser. Att göra motsvarande i de begränsade kommersiella databaserna är en plåga!

Därefter ska xml-datan läsas in. Jag blir lätt förvirrad av xml, men jag fick hjälp av @jimmycallin att reda ut alla turer man måste ta för att komma åt (i det här fallet) Titel, Författare och Institution. Så här ser ett utsnitt av datan ut:

<datafield tag="245" ind1="1" ind2="0">
 <subfield code="a">Variability in quality of life 13 years after traumatic brain injury in childhood</subfield>
</datafield>

<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Emanuelson, Ingrid,</subfield>
 <subfield code="d">1955-,</subfield>
 <subfield code="u">Göteborgs universitet, Institutionen för kliniska vetenskaper, sektionen för kvinnors och barns hälsa, Avdelningen för pediatrik, University of Gothenburg, Institute of Clinical Sciences, Section for the Health of Women and Children, Department of Pediatrics</subfield>
 <subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:chalmers.se)xeming</subfield>
</datafield>

<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Charlotte Smedler, Ann</subfield><subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:chalmers.se)227391</subfield>

</datafield>
<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Smedler, Ann-Charlotte,</subfield>
 <subfield code="d">1948-,</subfield>
 <subfield code="u">Stockholms universitet, Psykologiska institutionen</subfield>
 <subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:su)acsr</subfield>
</datafield>

Det som jag vill hämta här är dels titeln och sedan författare och deras institutionstillhörighet. Detta för att kunna studera samförfattarskap över institutionsgränser (ett tänkbart mått på tvärdisciplinaritet).

Så här kan man komma åt den informationen (och printa den):

from os import listdir
from lxml import etree as ET
#import xml.etree.ElementTree as ET #Use this if you don't have lxml installed

for filename in listdir("GU20151228N91622/"):
    with open("GU20151228N91622/" + filename) as currentFile:
        tree = ET.parse(currentFile)
        root = tree.getroot()

        for child in root[0]:
            for c in child:
                if c.get("tag") == "245":
                    for value in c:
                        if value.get("code") == "a":
                            print("-" * 50)
                            print(value.text)
                elif c.get("tag") == "700":
                    for value in c:
                        if value.get("code") == "a":
                            print(value.text)
                        elif value.get("code") == "u":
                            print(value.text)

Observera att jag i skriptet ovan har lagt alla filer i katalogen GU20151228/. Ur detta får vi sedan följande output:

Diagnosis and treatment of premenstrual dysphoria.
Andersch, Björn,
Göteborgs universitet, Institutionen för kvinnors och barns hälsa, Avdelningen för obstetrik och gynekologi, University of Gothenburg, Institute for the Health of Women and Children, Dept of Obstetrics and Gynaecology
Ho, Hoi-Por,
Göteborgs universitet, Institutionen för fysiologi och farmakologi, Avdelningen för farmakologi, University of Gothenburg, Institute of Physiology and Pharmacology, Dept of Pharmacology
Landén, Mikael,
Göteborgs universitet, Institutionen för klinisk neurovetenskap, Sektionen för psykiatri, University of Gothenburg, Institute of Clinical Neurosciences, Section of Psychiatry

Artikeln ovan är alltså resultatet av tre författare på tre olika institutioner. Intressant. Detta kan givetvis mätas och visualiseras. Men det blir en annan lat eftermiddags hackande.

 

 

 

 

Skrapa blogspot-bloggar

Det är oklart varför, men jag satt och funderade på om det fanns något spännande man kunde göra med bloggtexter ur ett medborgarprogrammeringsperspektiv. Men innan jag hann tänka så mycket på det dök ett praktiskt problem upp. Hur ladda ned allt som skrivits på en blogg?

Jag skrev således ett litet skrapverktyg för Blogspot-bloggar som jag döpte till det fantasifulla namnet blogspotscraper. Skraparen fungerar så att den börjar på det senaste inlägget, sedan arbetar sig skriptet bakåt i tiden rekursivt. För varje blogpost sparar den sedan en html-fil, rensad från de värsta kodsnuttarna så att man nästan bara får texten.

Man borde kunna använda samma approach för WordPressbloggar eftersom även de har en funktion för att läsa “nästa (äldre) inlägg”.

I allt detta glömde jag bort vad jag nu ska göra med tusentals nedsparade blogginlägg. Kanske träna upp en chatterbot.

Slå ihop datamängder från Web of Science och Scopus

Jag är inne i en programmeringstunnel. Häromveckan fick jag en peer-reviewkommentar av en anonym kollega som menade att jag borde kombinera data från Web of Science och Scopus i min artikel för att få ett bredare urval från de båda databaserna, jämfört med bara en av dem.

Problemet är att dessa två (konkurrerande) arkiv inte returnerar meta-data om artiklar i samma format. Från Scopus kan man exportera till csv-filer och från Web of Science liknande tsv-filer. Men det stora problemet ligger i dels att de båda databaserna inte delar något unikt sätt att identifiera enskilda artiklar (som är kompatibelt med varandra).

Det mest intuitiva, att använda DOI-systemet, är problematiskt. Dels eftersom det är ett väldigt dåligt system att programmera mot eftersom det inte följer standarder och dels eftersom det innehåller bisarra teckenkombinationer, och att det vid utelämnande av något tecken helt plötsligt kan börja returnera hundratusentals träffar (eftersom det sorterar till tidskrift efter enskild artikel).

Så mitt problem blev: hur slå samman två datamängder samtidigt som man tar bort överlappande duplikater?

Jag skrev då en liten algoritm som försöker använda artikeltitlar som en unik identifikator för en artikel. Risken finns givetvis att två olika artiklar har samma titel, men jag tänker att den borde vara ganska liten när man arbetar med dataset under hundratusen i allafall. Resultatet blev Pythonskriptet WoScop. Ur beskrivningen:

To find duplicates, this script removes identical titles. Due to formatting inconsistencies in the Scopus and Web of Science databases, a few steps are taken to simplify the titles. The procedure works like this:

  1. Titles are converted to lower case letters only (Python’s lower() method).
  2. Only the first seven words of the title are included for further processing (to avoid long titles in multiple languages).
  3. A regular expression – [^A-Za-z0-9]+ – is applied to only allow letters and numbers (removing all special characters and spaces).
  4. The result is a string like this: aphotogrammetricapproachforassessingpositionalaccuracy, which is used as an unique identifier to avoid adding the same article twice to the final merging of the datasets.

Detta verkade fungera ganska bra på de dataset jag prövade med (se Githubsidan för länkar). Ett fåtal falska positiver (dvs. duplikater) kom med i den sammanslagna datamängden eftersom ovanstående algoritm inte (ännu) kan hantera stavfel i titlarna. Men rena stavfel är ganska ovanliga, det som skapar inkonsistens är i nio fall av tio avvikande anvädning av specialtecknen , . : ; ' " ? !, som det reguljära uttrycket ovan filtrerar bort.

Kommentera gärna om ni har förslag på förbättringar eller ser begränsningar som jag inte uppmärksammat. När man använder programmering för forskning är det viktiga inte att det fungerar så himla bra, utan att det går att beskriva hur det inte fungerar/dess begränsningar.