Создание агрегаторов научных статей
Всем привет! В этом посте я хочу рассказать про некоторые возможности автоматизации задач, с которыми сталкивается программист-исследователь.
В моей работе часто возникает потребность изучать научные статьи. Это помогает быть в курсе событий, а также исследовать новые методы и области.
Бывает очень полезно фиксировать прочитанную информацию, чтобы не приходилось постоянно возвращаться к одному и тому же материалу, чтобы что-то найти. Обычно я веду заметки, но переносить туда ссылки на статьи, скачивать их, создавать таблицы бывает муторно. Огромную работу проделали создатели гитхаб репозиториев-агрегаторов статей по определённым темам. Например, статьи про клоны кода или статьи про фаззинг. Эти репозитории заставили меня задуматься над автоматизацией создания таких списков статей. В результате я определила последовательность действий, которую я выполняю при исследовании новой темы:
Шаг 1. Поиск статей в известных источниках (например, Semantic Scholar или arxiv)
Шаг 2. Чтение аннотаций к статьям, по которым часто можно понять, подходят ли они по тематике.
Шаг 3. Выделение важных объектов из статей: к ним можно отнести ссылки на гитхаб, графики, картинки (чтобы можно было, например, сослаться на результаты статьи в своей презентации, не испортив качество объекта при вставке) , а также год написания и журнал для понимания уровня статьи и актуальности.
Шаг 4. Подробное изучение выбранных статей.
Сбор информации для первых трёх шагов можно автоматизировать. Остановимся подробнее на них.
Поиск статей
Semantic Scholar и arxiv имеют специальное API для поиска статей по запросу. С помощью него можно узнать базовую информацию про статьи и скачать их при наличии открытого доступа.
Например, исходя из документации для arxiv, для поиска статей по запросу потребуется написать подобный код:
import arxiv
# Construct the default API client.
client = arxiv.Client()
# Search for the 10 most recent articles matching the keyword "quantum."
search = arxiv.Search(
query = "quantum",
max_results = 10,
sort_by = arxiv.SortCriterion.SubmittedDate
)
results = client.results(search)
Для найденных статей можно получить аннотацию, авторов, год публикации, ссылку на скачивание в случае открытого доступа и т.д:
papers = [
{
"title": result.title,
"authors": ", ".join([author.name for author in result.authors]),
"journal": result.journal_ref,
"year": result.published.year,
"abstract": result.summary,
"openAccessPdf": {"url": result.pdf_url},
"paperId": result.entry_id.split("/")[-1], # last part of url after slash
} for result in results
]
Статьи можно скачать с помощью следующего кода:
results = client.results(search)
paper_to_download = next(results)
paper_to_download.download_pdf(dirpath=".", filename="some-article.pdf")
Для Semantic Scholar примеры использования API есть в репозитории.
Аналогичный по содержанию код для Semantic Scholar API может выглядеть следующим образом:
import requests
import json
from datetime import datetime
x_api_key = "your-api-key" # you can get it from official site
class SemanticScholarClient:
def __init__(self):
self.base_url = "https://api.semanticscholar.org/graph/v1"
def search(self, query, max_results=10, sort_by="relevance"):
endpoint = f"{self.base_url}/paper/search"
params = {
"query": query,
"limit": max_results,
"fields": "paperId,title,isOpenAccess,openAccessPdf,abstract,year,authors,citationCount,url,venue",
"sort": sort_by
}
headers = {
"X-API-KEY": x_api_key
}
response = requests.get(endpoint, params=params, headers=headers)
response.raise_for_status()
return response.json()['data']
class SemanticScholarSearch:
def __init__(self, query, max_results=10, sort_by="relevance"):
self.query = query
self.max_results = max_results
self.sort_by = sort_by
# Usage
client = SemanticScholarClient()
# Search for the 10 most recent articles matching the keyword "quantum."
search = SemanticScholarSearch(
query="quantum",
max_results=10,
sort_by="publicationDate:desc" # Sort by publication date, descending
)
results = client.search(search.query, search.max_results, search.sort_by)
papers = [
{
"title": result['title'],
"authors": ", ".join([author['name'] for author in result['authors']]),
"journal": result.get('venue', ''),
"year": result['year'],
"abstract": result.get('abstract', ''),
"openAccessPdf": {"url": result.get('openAccessPdf', {}).get('url', '')} if result.get('openAccessPdf', {}) else None,
"paperId": result['paperId'],
} for result in results if result
]
# Download papers
for paper in papers:
if paper['openAccessPdf']:
print(f"Title: {paper['title']}")
response = requests.get(paper['openAccessPdf']['url'])
response.raise_for_status()
filepath = f"{paper['title']}.pdf"
with open(filepath, 'wb') as f:
f.write(response.content)
Выделение объектов
Для автоматизации выделения объектов из статьи можно использовать библиотеку pdfplumber. В написании кода с использованием этой библиотеки очень помогла статья. С помощью парсинга элементов в PDF и регулярных выражений можно извлекать существующие в статье ссылки на гитхаб:
import pdfplumber
import re
def extract_text_from_pdf(pdf):
text = ""
for page in pdf.pages:
text += page.extract_text() + "\n"
return text.strip()
pdf = pdfplumber.open("some-article.pdf")
text_from_pdf = extract_text_from_pdf(pdf)
github_regex = r"https?://(?:www\.)?github\.com/[\w-]+/[\w.-]+[\w-]"
github_links = re.findall(github_regex, text_from_pdf)
Для извлечения картинок подходит библиотека PyMuPDF. Вот подробный гайд про использование библиотеки. Для сохранения картинок из статьи можно написать следующий код:
import fitz
doc = fitz.Document("some-article.pdf")
for i in tqdm(range(len(doc)), desc="pages"):
cnt = 0 # counter to divide images on one page
for img in tqdm(doc.get_page_images(i), desc="page_images"):
xref = img[0]
image = doc.extract_image(xref)
pix = fitz.Pixmap(doc, xref)
pix.save("img_%s_%s.png" % (i, cnt))
cnt += 1
Не во всех случаях указанным методом удаётся извлечь все изображения из статей, поэтому в этой задаче есть огромное пространство для размышлений и идей по улучшению метода.
Перечисленные этапы реализованы в репозитории Researcher-Helper. На данный момент в нём поддерживается поиск по Semantic Scholar и arxiv. В результате он позволяет сгенерировать по запросу таблицу со статьями в формате html. Буду рада обратной связи, идеям по улучшению и советам в реализации описанных этапов!