
Existe alguma maneira de testar os hiperlinks de um pdf
arquivo? Suponha que eu tenha um pdf
arquivo contendo um grande número de hiperlinks, posso testar automaticamente a resposta que terei ao seguir esses links ( 200
= ok, 404
= não encontrado, etc.).
Eu sei que existem ferramentas para páginas da web (como softwares, comoKLinkStatus, ou como webservices, comoo link de verificação do W3C) mas existe algum equivalente para pdf
? Como eu acho que não existe tal coisa, existe algum equivalente legal para tex
arquivos? Acho que isso pode ser feito com alguns scripts e magia regexp, mas queria saber se existe alguma solução existente.
\documentclass{article}
\usepackage{hyperref}
\begin{document}
\href{http://tex.stackexchange.com/test-404/}{This will give a 404}
\href{http://tex.stackexchange.com/}{This will give a 200}
\end{document}
Responder1
Instale o curl (supondo que você use o apt-get para instalar pacotes):
sudo apt-get install curl
e faça o seguinte para arquivos PDF:
cat document.pdf | grep -ao '[a-zA-Z]\+://[a-zA-Z.-]*[^)]\+' | while read LINE; do curl -o /dev/null --silent --head --write-out '%{http_code}' "$LINE"; echo " $LINE"; done
e faça o seguinte para arquivos tex:
cat myfile.tex | grep -o '{[a-zA-Z]\+://[a-zA-Z.-]*[^}]\+}' | sed s/{// | sed s/}// | while read LINE; do curl -o /dev/null --silent --head --write-out '%{http_code}' "$LINE" echo " $LINE" done
Copie e cole o seguinte em sua linha de comando para um teste imediato:
echo "\documentclass{article}
\usepackage{hyperref}
\begin{document}
\href{http://tex.stackexchange.com/test-404/}{This will give a 404}
\href{http://tex.stackexchange.com/}{This will give a 200}
\end{document}" | grep -o '{[a-zA-Z]\+://[a-zA-Z.-]*[^}]\+}' | sed s/{// | sed s/}// | while read LINE; do
curl -o /dev/null --silent --head --write-out '%{http_code}' "$LINE"
echo " $LINE"
done
Consulte o seguinte para obter detalhes:
https://stackoverflow.com/questions/13611973/how-to-grep-for-a-url-in-a-file https://stackoverflow.com/questions/6136022/script-to-get-the-http-status-code-of-a-list-of-urls
Responder2
Se você estiver aberto a usar Python, isso pode ser suficiente.
Eu uso python com os pacotes pyPdf e urllib2. A lógica é a seguinte: Abra um PDF com pyPdf.PdfFileReader e percorra-o. Na função abaixo, pdf
está o objeto de arquivo PDF aberto.
def get_urls(pdf):
badurls = list()
links = list()
url_checker = URLChecker() # a helper function to test the URL (urllib2)
for pg in range(pdf.getNumPages()):
page = pdf.getPage(pg)
obj = page.getObject()
for a in obj.get('/Annots', []):
u = a.getObject()
lnk = u['/A'].get('/D')
url = u['/A'].get('/URI')
if lnk:
links.append(lnk)
if url:
urls.append(url)
result, reason = url_checker.check(url)
if not result:
badurls.append({'url':url, 'reason': '%r' % reason})
anchors = pdf.getNamedDestinations().keys()
badlinks = [x for x in links if x not in anchors]
return urls, badurls, badlinks
Abre url_checker
a url e se houver erro, ela retorna (False, error_string)
. Caso contrário, ele retorna(True, None)
Assim, no final do processo, você terá uma lista de URLs no PDF, quaisquer URLs inválidos que não puderam ser abertos (contendo o motivo), uma lista de links no PDF e um subconjunto daqueles links que não o fazem. têm âncoras de destino.
Omiti a lógica do url_checker, mas é bem simples. Existem várias maneiras de fazer essa parte - você também pode usar o requests
pacote em vez de urllib2
.
Responder3
Um programa C++ simples usando Qt4 e Poppler resolveria o problema. Eu rapidamente esbocei e testei em alguns PDFs. Acho que seria possível ajustá-lo bastante para torná-lo mais robusto contra links estranhos como mailto: -protocol e outras portas. E é claro que a formatação de saída pode ser melhor, se você precisar analisá-la depois, mas funciona
#include <QTcpSocket>
#include <QUrl>
#include <QByteArray>
#include <QList>
#include <poppler-qt4.h>
#include <QDebug>
using namespace Poppler;
QString urlGetStatus(const QUrl &url) {
QString status;
QTcpSocket socket;
socket.connectToHost(url.host(), 80);
if (socket.waitForConnected()) {
socket.write("HEAD " + url.path().toUtf8() + " HTTP/1.1\r\n"
"Host: " + url.host().toUtf8() + "\r\n"
"\r\n");
if (socket.waitForReadyRead()) {
QByteArray bytes = socket.readAll();
status = QString(bytes).mid(9, 3);
}
}
return status;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
return 1;
}
QString path = QString(argv[1]);
Document *doc = Document::load(path);
if(doc == NULL)
return 1;
QList<QUrl> urlList;
for(int i = 0;i < doc->numPages(); i++) {
Page *p = doc->page(i);
foreach(Link *li,p->links()) {
if(li->linkType() == Link::Browse) {
LinkBrowse *link = static_cast<LinkBrowse*>(li);
urlList.append(QUrl(link->url()));
}
}
}
foreach(QUrl url, urlList) {
QString stat = urlGetStatus(url);
if(stat == "200") {
qDebug() << url.toString() << "returned status 200";
} else {
qDebug() << url.toString() << " maybe not reachable status" << stat;
}
}
return 0;
}
Como sou um amigo cmake usei este CMakeLists.txt para compilar:
cmake_minimum_required(VERSION 2.6)
project(qlinkextract)
set (CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules/")
find_package(Qt4 REQUIRED QtCore QtNetwork)
include(${QT_USE_FILE})
find_package(Poppler REQUIRED)
include_directories(${POPPLER_QT_INCLUDE_DIR})
add_executable(qlinkextract main.cpp)
target_link_libraries(qlinkextract ${QT_LIBRARIES} ${POPPLER_QT_LIBRARIES})
Você precisará de pacotes de desenvolvimento Qt4 e pacotes de desenvolvimento Poppler-Qt4. Se você não tiver um FindPoppler.cmake em seus diretórios CMakeModules, vá e pegue um online.
Para compilá-lo, certifique-se de que no diretório do projeto qlinkextract esteja (precisa ser escrito exatamente como o seguinte)
- CMakeLists.txt (veja acima)
- main.cpp (veja acima)
- cmake/Modules/FindPoppler.cmake (baixe de algum lugar)
- build/ (a pasta é opcional)
em um console, vá para a pasta build e digite
cmake ..
make
se algo estiver faltando, instale os pacotes ausentes
Alguns exemplos de saída:
"http://www.igi-global.com/chapter/ontology-based-multimedia-indexing/42895?camid=4v1" returned status 200
"http://www.igi-global.com/chapter/ontology-based-multimedia-indexing/42895?camid=4v1" returned status 200
"http://www.igi-global.com/e-resources/library-recommendation/?id=1" returned status 200
"http://www.igi-global.com/chapter/towards-low-cost-energy-monitoring/112719?camid=4v1a" returned status 200
"http://www.igi-global.com/article/algebraic-properties-of-rough-set-on-two-universal-sets-based-on-multigranulation/116046?camid=4v1a" returned status 200
"http://www.igi-global.com/article/algebraic-properties-of-rough-set-on-two-universal-sets-based-on-multigranulation/116046?camid=4v1a" returned status 200
"http://www.igi-global.com/article/fuzzy-decision-support-system-for-coronary-artery-disease-diagnosis-based-on-rough-set-theory/111313?camid=4v1a" returned status 200
"http://www.igi-global.com/article/fuzzy-decision-support-system-for-coronary-artery-disease-diagnosis-based-on-rough-set-theory/111313?camid=4v1a" returned status 200
"http://www.igi-global.com/chapter/optimization-model-identification-temperature-intelligent/74536?camid=4v1a" returned status 200
"http://www.igi-global.com/chapter/optimization-model-identification-temperature-intelligent/74536?camid=4v1a" returned status 200