Скачивание и анализ научных статей

Я пытаюсь написать сценарий, который получает исследовательскую статью с веб-сайта, вызывая их API, а затем просматривая его по предложениям с некоторыми условиями.

Статья доступна в формате XML. Я напрямую использую nltk.sent_tokenize() чтобы разбить соответствующую часть XML-документа на список предложений, а затем выполнить поиск “болезни” (количество болезней в моем фреймворке 12000) в каждом предложении с использованием регулярного выражения, и если совпадение найдено, я ищу «биомаркеры» (этот фрейм данных имеет 20000 строк, а тип данных строки относится к списку типов, где каждый список имеет в среднем 2 значения, т.е. существует более 40000 значений, которые должны быть сопоставлены) в том же предложении. И, наконец, результат сохраняется в базе данных, если болезнь и биомаркер встречаются в одном предложении.

В худшем случае шаги, предпринятые для выполнения задачи, будут 900 000(документы, которые необходимо просмотреть) *15(количество предложений в каждой статье) *12.000(номер болезни, по которой необходимо провести поиск) *40 000(номер маркера, который нужно искать).

На данный момент код анализирует 60-70 статей за час, что слишком медленно.

Ниже приведен код, который я пробовал. Я ищу существующие узкие места в своем коде. Мы будем очень благодарны за любые предложения по оптимизации кода.

import timeit
import mysql.connector
from urllib.request import urlopen
import urllib.request
import xml.dom.minidom
import xml.etree.ElementTree as ET
import requests
import xml
import lxml.etree
from lxml import etree
import re
import math
import nltk 
import pandas as pd

start = timeit.default_timer()
print("start_time:", start)
mydb = mysql.connector.connect(host="localhost",user="root",password="",database="biomarker")
mycursor = mydb.cursor() 
mycursor.execute("DROP TABLE  IF EXISTS papers_sentence_com_ppr")
mycursor.execute("create table papers_sentence_com_ppr (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), marker VARCHAR(255),marker_id VARCHAR(255), disease_name VARCHAR(255),AUTHORS  TEXT, sentence  TEXT)")
mycursor.execute("ALTER TABLE biomarker.papers_sentence_com_ppr CONVERT TO CHARACTER SET utf8")
#mycursor.execute("create table papers (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), count VARCHAR(255),disease_name VARCHAR(255))")
df1 = pd.read_sql_query("select name from biomarker.disease2", mydb)

df = pd.read_sql_query("select * from biomarker.table_35", mydb)
print(df1)

biomarker_txt = df.at[2,'CA']
biomarker = biomarker_txt.split(" ")
print(len(biomarker))
#mycursor.execute("create table papers (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), count VARCHAR(255),disease_name VARCHAR(255))")



urla="https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml"
urlb='https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml'


array1 = []
re1=requests.get(urla)
root = ET.fromstring(re1.content)
for hitCount in root.iter('hitCount'):
    hit_count=int(hitCount.text)
result_value=hit_count
hit_count1=hit_count/1000
hit_count=math.ceil(hit_count1)

hit_count=10




counter1=0
counter3=0
y=0
i=1


for x in range(hit_count):
    
    re1=requests.get(urla)
    root1 = ET.fromstring(re1.content)
    
    for id in root1.iter('id'):
        id_text=id.text
       
        array1.append(id.text)
    for nextCursorMark in root1.iter('nextCursorMark'):
       
        counter3=counter3+1
        print(counter3)
        urla=urlb
        urla =urla+"&cursorMark="+nextCursorMark.text







for i in range(result_value):# will run 900.000 times for 900.000 papers
    print("paper no:", i,)
    paper_id=(array1[i]) #array1 contains list of paper ids
    print("paper_id:", paper_id)       
    make_url="https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=ext_id:"+paper_id+'&resultType=core&format=xml'
    re2=requests.get(make_url)
    root2 = ET.fromstring(re2.content)
    for abstractText in root2.iter('abstractText'):
           
            abstract_text_without_tags= re.sub(r"<[^>]*>"," ",abstractText.text )#extract the relevant xml part
            
           
            
            
            nltk_tokens = nltk.sent_tokenize(abstract_text_without_tags)#break the text into sentences
            
            for text in range(len(nltk_tokens)):#depends on the length of text
                    
                    for zz in range(len(df)):#df has 20.000 rows
                        biomarker_txt=((df.at[zz,'CA'])) 
                        biomarker = biomarker_txt.split(" ")# a cell could have more than value which are separated by an space
                        for tt in range(len(biomarker)):
                            if len(biomarker[tt])>2:
                                matches_for_marker = re.findall(rf"b{re.escape(biomarker[tt])}b", nltk_tokens[text])
                                if len(matches_for_marker)!=0:
                                    for y in range(len(df1)):
                                        disease_name=(df1.at[y,'name'])
                                        
                                        regex_for_dis = rf"b{disease_name}b"
                                        
                        
                                        matches_for_dis= re.findall(regex_for_dis, nltk_tokens[text], re.IGNORECASE | re.MULTILINE)
                                        #matches_for_dis = [re.findall(rf"b{(df1.at[y,'name'])}b", nltk_tokens[text], re.IGNORECASE | re.MULTILINE) for y in  range(len(df1))]
                       
                                        
                                        if len(matches_for_dis)!=0:
                                            
                            
                                            for firstPublicationDate in root2.iter('firstPublicationDate'):
                                                firstPublicationDate=firstPublicationDate.text
                                            for authorString in root2.iter('authorString'):
                                                counter1=counter1+1
                                            
                                            mycursor.execute("insert into papers_sentence_com_ppr (id, paper_id,firstPublicationDate, marker,marker_id, disease_name, AUTHORS, sentence) values (%s,%s,%s, %s, %s,%s, %s,%s)", (counter1, paper_id,firstPublicationDate, biomarker[tt],(df.at[zz,'Entry']), (df1.at[y,'name']), authorString.text, nltk_tokens[text]))
                                            #print("***********************************************************DATABASE_ENTRY*************************************************************n")
                                            mydb.commit()






```

1 ответ
1

В настоящее время весь ваш код существует в одной глобальной куче, без функций, подмодулей или классов. Представьте некоторые из них.

Я вижу, что ваш пароль отображается как пустой для базы данных. Несколько вещей:

  • Очень надеюсь что пароль не фактически пусто, и что вы вместо этого пропустили его для проверки. Пожалуйста, не используйте пустой пароль.
  • Этот сценарий должен входить не как root, а как пользователь с ограниченными разрешениями.
  • Сохраните пароль для входа в базу данных в другом месте, в каком-нибудь безопасном кошельке, хранилище кредитов и т. Д.

Оберните ваше соединение и объекты курсора в with контекстные менеджеры.

Вам определенно не следует отбрасывать всю таблицу в этом скрипте. Во всяком случае, может тебе стоит быть truncateзаставить его удалить все свои данные; но определенно не бросай это.

У вас куча неиспользованного импорта; все эти:

from urllib.request import urlopen
import urllib.request
import xml.dom.minidom
import xml
import lxml.etree
from lxml import etree

нужно удалить.

Измените конструкцию URL так, чтобы вместо этого:

https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml

у вас есть четко определенный метод, который принимает сеанс, формирует словарь параметров, проверяет ответ и анализирует ET:

def get_ebi(session: Session, **kwargs: str) -> ET.ElementTree:
    query = (
        '("biomarker" OR "biomarkers" OR "biological marker" OR "biological markers") '
        'AND (LANG:"eng" OR LANG:"en" OR LANG:"us") '
        'AND (HAS_ABSTRACT:y) '
        'AND (SRC:"PPR")'
    )
    with session.get(
        'https://www.ebi.ac.uk/europepmc/webservices/rest/search',
        params={
            'format': 'xml',
            'resultType': 'idlist',
            'pageSize': 1_000,
            'query': query,
            **kwargs,
        },
        stream=True,
    ) as response:
        response.raise_for_status()
        root = ET.parse(response.raw)

    return root


with Session() as sess:
    root = get_ebi(sess)
    for hitCount in root.iter('hitCount'):
        hit_count = int(hitCount.text)
    result_value = hit_count
    hit_count1 = hit_count / 1_000
    hit_count = math.ceil(hit_count1)

Кроме того, ваш код наполнен вещами, которые просто не имеют смысла. Вы перезаписываете переменные:

hit_count=math.ceil(hit_count1)
hit_count=10

У вас есть загадочные, непонятные имена переменных:

array1 = []
counter1=0
counter3=0
y=0
i=1

У вас есть переменные с немного разными именами, которые делают совершенно разные вещи:

for hitCount in root.iter('hitCount'):
    hit_count=int(hitCount.text)

Этот последний пример также неправильно повторяет все hitCount элементы, когда следует обращать внимание только на первое.

Встроенные модули тени:

    for id in root1.iter('id'):

Вы называете переменные, которые прямо противоположны тому, что они содержат; text на самом деле будет содержать целое число:

for text in range(len(nltk_tokens)):

Вы выполняете фиксацию в своем самом внутреннем цикле, когда это, вероятно, должно быть отложено до гораздо более позднего времени, возможно, до конца скрипта.

Вам следует попытаться решить эти проблемы, в частности организовать методы подпрограммы, а затем опубликовать новый вопрос.

  • 1

    Большое спасибо!! Я внесу указанные изменения и, надеюсь, процесс ускорится.

    – sanjeev_gautam

  • @sanjeev_gautam Вы также можете опубликовать новый вопрос с обновленным кодом. Как только вы устраните очевидные недостатки текущего кода, кто-нибудь может найти хороший способ значительно его ускорить.

    – Грейфер

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *