Gerencie uma lista de referências em CSV e verifique DOIs duplicados ou ausentes
Mantenha uma pequena lista de referências de pesquisa em CSV, normalize o texto DOI para comparação e gere um arquivo de revisão para valores DOI duplicados e ausentes. O workflow preserva o CSV original e não afirma que um DOI é válido apenas porque está presente.
Conteúdo verificado 2026.09.20Inclui arquivos de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English
Para quem éEste guia é voltado a pesquisadores que mantêm listas de referências em planilhas ou arquivos CSV e querem uma verificação simples de DOIs duplicados e ausentes.
Preparação
Python 3.12 e um comando de terminal que inicie essa versão.
Um editor de texto ou programa de planilhas capaz de salvar arquivos CSV em UTF-8.
Uma pasta de trabalho em que o script possa criar uma nova pasta dentro de outputs.
É necessária apenas a biblioteca padrão do Python: csv e pathlib.
01Decida o que a verificação de DOI pode e não pode informar
Este workflow verifica dois problemas práticos: uma reference não tem DOI value, ou duas rows contêm o mesmo DOI após uma simple text normalization. Ele não entra em contato com um DOI registry e, portanto, não prova que um DOI existe, resolve corretamente ou pertence ao article citado.
O script remove textual prefixes comuns, como https://doi.org/ e doi:, remove whitespace nas extremidades e converte o DOI text restante para lowercase para comparação. O original DOI text é preservado no output ao lado do normalized value.
02Crie uma lista de referências sintética
A reference list a seguir é sintética e foi criada especificamente para este artigo. As DOI-like strings são dados de demonstração e não são apresentadas como publicações reais ou resolvíveis. Salve o arquivo como references.csv.
Há 6 reference rows. Cinco contêm DOI text e uma, R004, não tem DOI. R001 e R003 usam textual forms diferentes, mas são normalizadas para o mesmo value. R005 demonstra que capitalization e um prefix doi: também podem ser normalizados para comparação.
03Determine manualmente os problemas esperados
ref_id
Original DOI
Normalized DOI
Issue esperado
R001
10.0000/demo.alpha
10.0000/demo.alpha
DUPLICATE_DOI
R002
10.0000/demo.beta
10.0000/demo.beta
None
R003
https://doi.org/10.0000/demo.alpha
10.0000/demo.alpha
DUPLICATE_DOI
R004
MISSING_DOI
R005
doi:10.0000/DEMO.GAMMA
10.0000/demo.gamma
None
R006
10.0000/demo.delta
10.0000/demo.delta
None
As contagens esperadas são 6 references, 5 rows com DOI text, 1 row com missing DOI e 1 duplicate DOI group contendo 2 rows. Após a normalização, as cinco DOI entries não vazias contêm 4 distinct DOI values.
04Normalize o texto DOI e grave arquivos de revisão
Salve o script a seguir como reference_list_check.py. Ele grava uma normalized copy de toda a reference list e um issues.csv separado contendo apenas as rows que precisam de review. O references.csv original é somente leitura.
python
import csv
from pathlib import Path
SOURCE = Path("references.csv")
OUTPUT_DIR = Path("outputs") / "reference_list_result"
NORMALIZED = OUTPUT_DIR / "normalized_references.csv"
ISSUES = OUTPUT_DIR / "issues.csv"
REQUIRED = {"ref_id", "title", "year", "doi"}
def normalize_doi(value: str) -> str:
text = value.strip()
lowered = text.lower()
prefixes = (
"https://doi.org/",
"http://doi.org/",
"doi:",
)
for prefix in prefixes:
if lowered.startswith(prefix):
text = text[len(prefix):].strip()
break
return text.lower()
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
rows = []
with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream)
if reader.fieldnames is None or not REQUIRED.issubset(reader.fieldnames):
raise ValueError("CSV is missing a required column.")
seen_ids = set()
for row in reader:
ref_id = row["ref_id"].strip()
if not ref_id:
raise ValueError("ref_id must not be blank.")
if ref_id in seen_ids:
raise ValueError(f"Duplicate ref_id: {ref_id}")
seen_ids.add(ref_id)
rows.append({
"ref_id": ref_id,
"title": row["title"],
"year": row["year"],
"doi": row["doi"],
"normalized_doi": normalize_doi(row["doi"]),
})
by_doi = {}
for row in rows:
doi = row["normalized_doi"]
if doi:
by_doi.setdefault(doi, []).append(row["ref_id"])
duplicate_dois = {
doi: ids for doi, ids in by_doi.items() if len(ids) > 1
}
issue_rows = []
for row in rows:
doi = row["normalized_doi"]
if not doi:
issue_rows.append({
"ref_id": row["ref_id"],
"issue": "MISSING_DOI",
"normalized_doi": "",
"related_ref_ids": "",
})
elif doi in duplicate_dois:
issue_rows.append({
"ref_id": row["ref_id"],
"issue": "DUPLICATE_DOI",
"normalized_doi": doi,
"related_ref_ids": ";".join(duplicate_dois[doi]),
})
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
normalized_fields = ["ref_id", "title", "year", "doi", "normalized_doi"]
with NORMALIZED.open("x", encoding="utf-8", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=normalized_fields)
writer.writeheader()
writer.writerows(rows)
issue_fields = ["ref_id", "issue", "normalized_doi", "related_ref_ids"]
with ISSUES.open("x", encoding="utf-8", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=issue_fields)
writer.writeheader()
writer.writerows(issue_rows)
missing_count = sum(row["issue"] == "MISSING_DOI" for row in issue_rows)
print(f"References checked: {len(rows)}.")
print(f"Missing DOI rows: {missing_count}.")
print(f"Duplicate DOI groups: {len(duplicate_dois)}.")
print(f"Issue rows: {len(issue_rows)}.")
print(f"Output folder: {OUTPUT_DIR.as_posix()}")
if __name__ == "__main__":
main()
05Compare o relatório de revisão esperado
issues.csv deve conter três data rows: R001 e R003 pelo normalized DOI duplicado, mais R004 pelo missing DOI.
A saída esperada do console abaixo foi derivada manualmente a partir da synthetic list e do script. Não é um execution log capturado.
text
References checked: 6.
Missing DOI rows: 1.
Duplicate DOI groups: 1.
Issue rows: 3.
Output folder: outputs/reference_list_result
06Revise as linhas sinalizadas em vez de excluí-las automaticamente
Confirme que R001 e R003 são normalizados para o mesmo DOI text.
Confirme que R004 é mantido na normalized reference list mesmo sem DOI.
Verifique paper title, authors, year e publication details antes de decidir se duplicate-DOI rows devem ser combinadas.
Para missing DOI rows, verifique se a publication realmente tem um DOI antes de adicioná-lo.
Execute o script novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de substituir a review anterior.
Problema
O que verificar
O mesmo paper foi inserido duas vezes com DOI formatting diferente
Normalize DOI URL e prefixes doi: comuns antes da comparação.
DOI em branco tratado como duplicado
Mantenha missing DOI detection separada de duplicate DOI grouping.
Papers diferentes compartilham title semelhante
Não use title similarity sozinha como prova de que os records são duplicates.
Uma string com aparência de DOI está presente
Presença não prova validity; verifique references importantes com uma trusted metadata source.
Um result existente é sobrescrito
Use uma output folder nova para manter a previous review evidence disponível.
07Entenda os limites da deduplicação baseada em DOI
DOI matching é útil quando DOI metadata está presente, mas missing DOI não significa que uma reference seja invalid ou incomplete. Algumas publications ou document types podem não ter DOI, e older records podem exigir outros identifiers ou manual bibliographic checks.
Este script realiza apenas text normalization. Ele não resolve DOI links, consulta Crossref ou outra registration agency, verifica titles ou authors, detecta retractions ou determina se dois DOI values diferentes se referem a related versions de um trabalho.
Para uma research database real, mantenha fields como authors, title, year, journal, volume, pages, DOI e another local reference ID. Trate automatic duplicate detection como uma ajuda para review, e não como uma instrução para excluir bibliographic records.
Foram contadas manualmente 6 synthetic reference rows, 5 rows com DOI e 1 missing DOI row.
R001 e R003 foram normalizados manualmente para o mesmo value, 10.0000/demo.alpha.
R005 foi normalizado manualmente de doi:10.0000/DEMO.GAMMA para 10.0000/demo.gamma.
Foram calculados 4 distinct normalized DOI values entre as 5 DOI rows não vazias.
O expected issue report foi derivado como 2 duplicate-DOI rows mais 1 missing-DOI row.
O script foi inspecionado quanto a duplicate ref_id checks, DOI normalization, tratamento separado de missing e duplicate, output collision protection e preservação do CSV original.
Limites da verificação
O código não foi executado pelo autor desta resposta; nenhum arquivo CSV de saída foi criado.
As synthetic DOI-like strings não foram verificadas quanto a registration ou resolution e não são apresentadas como publicações reais.
DOI validity, bibliographic metadata accuracy, title similarity, retractions e related article versions não foram testados.
As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.
Em vez de coletar apenas um título e uma URL, registre em uma única linha a data de referência, a data de publicação, a data de acesso, as unidades e os termos de uso. Inclui um modelo CSV e uma checklist que não exigem código.
Separe IDs duplicados de respostas em branco em 10 respostas sintéticas. Informe o denominador das respostas válidas e salve em um novo arquivo as contagens e porcentagens por opção, além dos motivos de exclusão.
Documente o significado de cada coluna do conjunto de dados antes da análise, incluindo unidade, tipo de dado e valores permitidos. Um pequeno conjunto de dados sintético mostra como o mesmo dicionário também pode servir para uma validação automática simples.