Teste os hiperlinks de um documento PDF

Teste os hiperlinks de um documento PDF

Existe alguma maneira de testar os hiperlinks de um pdfarquivo? Suponha que eu tenha um pdfarquivo 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 texarquivos? 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, pdfestá 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_checkera 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 requestspacote 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 

informação relacionada