使用Python从PubMed获取数据

16

我有一份PubMed文章列表,其中包括PubMed ID。我想创建一个Python脚本或使用Python,接受PubMed ID号作为输入,并从PubMed网站获取摘要。

目前,我已经了解了NCBI Eutilities和Python的importurl库,但不知道如何编写模板。

非常感谢您能提供任何指导。

谢谢!

5个回答

36

使用BiopythonEntrez模块,您可以很容易地获取摘要以及所有其他元数据。这将打印摘要:

from Bio.Entrez import efetch

def print_abstract(pmid):
    handle = efetch(db='pubmed', id=pmid, retmode='text', rettype='abstract')
    print handle.read()

以下是一个函数,它会获取XML并仅返回摘要:

from Bio.Entrez import efetch, read

def fetch_abstract(pmid):
    handle = efetch(db='pubmed', id=pmid, retmode='xml')
    xml_data = read(handle)[0]
    try:
        article = xml_data['MedlineCitation']['Article']
        abstract = article['Abstract']['AbstractText'][0]
        return abstract
    except IndexError:
        return None

顺便说一句,我实际上在一个真正的任务中需要做这种事情,所以我将代码组织成了一个类 -- 请看这个代码片段.


1
看起来这是一个非常不错的模块。我之前并不知道它的存在。不过,我的代码有一个好处就是它可以获得 DOI 值,以便检索到的 URL 尽可能通用。我猜 Entrez 模块可能也有类似的功能,但我还没有深入了解过。 - Bobort
1
我不确定你所说的URL是什么意思... biopython在幕后完成所有查询,因此您无需处理任何URL。 - Karol
1
哦,我明白了,但那与这个问题无关。如果您有DOI,您可以始终构建字符串"http://dx.doi.org/"+doi并使用它。问题是如何从Pubmed获取数据。 - Karol
确实,Karol。然而,我需要获取一篇文章的具体细节,而PubMed提供了一个一致的格式,我可以利用它来获取这些信息。否则,我将不得不弄清楚每个唯一DOI页面上特定信息的位置,这通常与下一个页面不同。 - Bobort
显示剩余2条评论

9

哇,我就在一周前也在做一个类似的项目!

编辑: 我最近更新了代码,利用了BeautifulSoup。我有自己的虚拟环境,但你可以用pip安装它。

基本上,我的程序可以接收PubMed ID、DOI或包含PubMed ID和/或 DOI行的文本文件,并获取有关该文章的信息。可以轻松地调整以满足您自己的需求,以获取摘要,以下是我的代码:

import re
import sys
import traceback
from bs4 import BeautifulSoup
import requests

class PubMedObject(object):
    soup = None
    url = None

    # pmid is a PubMed ID
    # url is the url of the PubMed web page
    # search_term is the string used in the search box on the PubMed website
    def __init__(self, pmid=None, url='', search_term=''):
        if pmid:
            pmid = pmid.strip()
            url = "http://www.ncbi.nlm.nih.gov/pubmed/%s" % pmid
        if search_term:
            url = "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s" % search_term
        page = requests.get(url).text
        self.soup = BeautifulSoup(page, "html.parser")

        # set the url to be the fixed one with the PubMedID instead of the search_term
        if search_term:
            try:
                url = "http://www.ncbi.nlm.nih.gov/pubmed/%s" % self.soup.find("dl",class_="rprtid").find("dd").text
            except AttributeError as e:  # NoneType has no find method
                print("Error on search_term=%s" % search_term)
        self.url = url

    def get_title(self):
        return self.soup.find(class_="abstract").find("h1").text

    #auths is the string that has the list of authors to return
    def get_authors(self):
        result = []
        author_list = [a.text for a in self.soup.find(class_="auths").findAll("a")]
        for author in author_list:
            lname, remainder = author.rsplit(' ', 1)
            #add periods after each letter in the first name
            fname = ".".join(remainder) + "."
            result.append(lname + ', ' + fname)

        return ', '.join(result)

    def get_citation(self):
        return self.soup.find(class_="cit").text

    def get_external_url(self):
        url = None
        doi_string = self.soup.find(text=re.compile("doi:"))
        if doi_string:
            doi = doi_string.split("doi:")[-1].strip().split(" ")[0][:-1]
            if doi:
                url = "http://dx.doi.org/%s" % doi
        else:
            doi_string = self.soup.find(class_="portlet")
            if doi_string:
                doi_string = doi_string.find("a")['href']
                if doi_string:
                    return doi_string

        return url or self.url

    def render(self):
        template_text = ''
        with open('template.html','r') as template_file:
            template_text = template_file.read()

        try:
            template_text = template_text.replace("{{ external_url }}", self.get_external_url())
            template_text = template_text.replace("{{ citation }}", self.get_citation())
            template_text = template_text.replace("{{ title }}", self.get_title())
            template_text = template_text.replace("{{ authors }}", self.get_authors())
            template_text = template_text.replace("{{ error }}", '')
        except AttributeError as e:
            template_text = template_text.replace("{{ external_url }}", '')
            template_text = template_text.replace("{{ citation }}", '')
            template_text = template_text.replace("{{ title }}", '')
            template_text = template_text.replace("{{ authors }}", '')
            template_text = template_text.replace("{{ error }}", '<!-- Error -->')

        return template_text.encode('utf8')

def start_table(f):
    f.write('\t\t\t\t\t\t\t\t\t<div class="resourcesTable">\n');
    f.write('\t\t\t\t\t\t\t\t\t\t<table border="0" cellspacing="0" cellpadding="0">\n');

def end_table(f):
    f.write('\t\t\t\t\t\t\t\t\t\t</table>\n');
    f.write('\t\t\t\t\t\t\t\t\t</div>\n');

def start_accordion(f):
    f.write('\t\t\t\t\t\t\t\t\t<div class="accordion">\n');

def end_accordion(f):
    f.write('\t\t\t\t\t\t\t\t\t</div>\n');

def main(args):
    try:
        # program's main code here
        print("Parsing pmids.txt...")
        with open('result.html', 'w') as sum_file:
            sum_file.write('<!--\n')
        with open('pmids.txt','r') as pmid_file:
        with open('result.html','a') as sum_file:
        for pmid in pmid_file:
            sum_file.write(pmid)
        sum_file.write('\n-->\n')
        with open('pmids.txt','r') as pmid_file:
            h3 = False
            h4 = False
            table_mode = False
            accordion_mode = False
            with open('result.html', 'a') as sum_file:
                for pmid in pmid_file:
                    if pmid[:4] == "####":
                        if h3 and not accordion_mode:
                            start_accordion(sum_file)
                            accordion_mode = True
                        sum_file.write('\t\t\t\t\t\t\t\t\t<h4><a href="#">%s</a></h4>\n' % pmid[4:].strip())
                        h4 = True
                    elif pmid[:3] == "###":
                        if h4:
                            if table_mode:
                                end_table(sum_file)
                                table_mode = False
                            end_accordion(sum_file)
                            h4 = False
                            accordion_mode = False
                        elif h3:
                            end_table(sum_file)
                            table_mode = False
                        sum_file.write('\t\t\t\t\t\t\t\t<h3><a href="#">%s</a></h3>\n' % pmid[3:].strip())
                        h3 = True                        
                    elif pmid.strip():
                        if (h3 or h4) and not table_mode:
                            start_table(sum_file)
                            table_mode = True
                        if pmid[:4] == "http":
                            if pmid[:18] == "http://dx.doi.org/":
                                sum_file.write(PubMedObject(search_term=pmid[18:]).render())
                            else:
                                print("url=%s" % pmid)
                                p = PubMedObject(url=pmid).render()
                                sum_file.write(p)
                                print(p)
                        elif pmid.isdigit():
                            sum_file.write(PubMedObject(pmid).render())
                        else:
                            sum_file.write(PubMedObject(search_term=pmid).render())
                if h3:
                    if h4:
                        end_table(sum_file)
                        end_accordion(sum_file)
                    else:
                        end_table(sum_file)
            pmid_file.close()
        print("Done!")

    except BaseException as e:
        print traceback.format_exc()
        print "Error: %s %s" % (sys.exc_info()[0], e.args)
        return 1
    except:
        # error handling code here
        print "Error: %s" % sys.exc_info()[0]
        return 1  # exit on error
    else:
        raw_input("Press enter to exit.")
        return 0  # exit errorlessly

if __name__ == '__main__':
    sys.exit(main(sys.argv))

现在它会根据下载的信息返回一个HTML文件。这是template.txt的模板内容:
<tr>{{ error }}
    <td valign="top" class="resourcesICO"><a href="{{ external_url }}" target="_blank"><img src="/image/ico_sitelink.gif" width="24" height="24" /></a></td>
    <td><a href="{{ external_url }}">{{ title }}</a><br />
    {{ authors }}<br />
    <em>{{ citation }}</em></td>
</tr>

当您运行该程序时,它会要求您输入DOI或Pubmed ID。如果您没有提供一个,它将读取pmids.txt文件。请随意使用代码。


1
谢谢Bobort,我将调整这段代码,以便仅获取摘要信息。另外,我会将其与另一个脚本集成,该脚本将Pubmed ID映射到结构标题和引文标题。 - Ruchik Yajnik
1
为什么我会被踩?仅仅踩一下回答并离开是多么无助啊! - Bobort
使用Biopython的Entrez工具,没有一种简便的方法来获取摘要吗? - Karol
回答自己,是有的,并且我在另一个回答中提供了一个例子。 - Karol
1
我给了一个负评,因为这是一种屏幕抓取方法,而不是通过JSON或XML API检索数据。这种方法是否有充分的理由? - Sigfried
显示剩余4条评论

3

metapub库是为此而构建的。截至2019年,Metapub已在PubMed数据库的超过三分之一上进行了测试。

from metapub import PubMedFetcher

pmids = [<your list of ids>]
for pmid in pmids:
    article = fetch.article_by_id(pmid)
    print(article.abstract)

如果您想查看每篇文章的完整文本,可以执行以下操作:

from metapub import FindIt

pmids = [<yourlist>]
for pmid in pmids:
    src = FindIt(pmid)
    print(src.doi)
    print(src.url)

我已经测试了这个库对成百上千万篇文章的适用性,以至于Medline XML(即Entrez)解析器大约有99%的鲁棒性。相信我,这些数据很杂乱。

来源:我是作者。


1
Pubmed文章的格式为:http://www.ncbi.nlm.nih.gov/pubmed/?Id
如果您知道ID,则可以获取上述内容,并访问该文章。摘要包含在以下结构中:
<div class="abstr"><h3>Abstract</h3><div class=""><p>α-latrotoxin and snake presynaptic phospholipases A2 neurotoxins target the presynaptic membrane of axon terminals of the neuromuscular junction....</p></div></div>

你需要一个工具来提取它。我建议使用:http://www.crummy.com/software/BeautifulSoup/bs4/doc/ 你仍然需要一个工具来获取html。为此,我会使用phantom.js或广受欢迎的requests模块。
您的工作流程将类似于:
pubmed_ids [1,2,3]
abstracts = []

for id in pubmed_ids: 
 html_for_id = requests.get('http://www.ncbi.nlm.nih.gov/pubmed/{0}'.format(id))
 soup =  BeautifulSoup(html_for_id)
 abstract = soup.find('selector for abstract')
 abstracts.append(abstract)

1
对整个文本有何评论? - Areza
我们能否使用这种方法和DOI提取完整的文本? - BND

0

似乎“pattern”模块可以轻松完成这个任务:

from pattern import web
import requests

id = 27523945
url = "http://www.ncbi.nlm.nih.gov/pubmed/{0}".format(id)
page = requests.get(url).text.encode('ascii', 'ignore')
dom = web.Element(page)
print(dom.by_tag("abstracttext")[0].content)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接