Тестирование гиперссылок PDF-документа

Тестирование гиперссылок PDF-документа

Есть ли способ проверить гиперссылки файла pdf? Предположим, у меня есть pdfфайл, содержащий большое количество гиперссылок, могу ли я автоматически проверить ответ, который я получу при переходе по этим ссылкам ( 200= ok, 404= not found и т. д.).

Я знаю, что такие инструменты существуют для веб-страниц (в виде программного обеспечения, например)KLinkStatus, или как веб-сервисы, напримерконтрольная ссылка W3C) но есть ли эквивалент для pdf? Поскольку я предполагаю, что такого нет, есть ли какой-нибудь хороший эквивалент для texфайлов? Я предполагаю, что это можно сделать с помощью некоторых скриптов и магии регулярных выражений, но мне было интересно, есть ли какое-либо существующее решение.

\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}

решение1

Установите curl (при условии, что вы используете apt-get для установки пакетов):

sudo apt-get install curl

и сделайте следующее для файлов 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

и сделайте следующее для 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

Скопируйте и вставьте следующее в командную строку для немедленного тестирования:

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

Подробную информацию см. ниже:

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

решение2

Если вы открыты для использования Python, этого может быть достаточно.

Я использую python с пакетами pyPdf и urllib2. Логика выглядит так: открыть pdf с помощью pyPdf.PdfFileReader и выполнить цикл по нему. В функции ниже pdf— это открытый объект файла pdf.

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

Открывает url_checkerURL и если есть ошибка, то возвращает (False, error_string). В противном случае возвращается(True, None)

Таким образом, в конце процесса у вас есть список URL-адресов в PDF-файле, все плохие URL-адреса, которые не удалось открыть (с указанием причины), список ссылок в PDF-файле и подмножество тех ссылок, которые не имеют целевых якорей.

Я опустил логику для url_checker, но она довольно проста. Есть несколько способов сделать эту часть — вы также можете использовать пакет requestsвместо urllib2.

решение3

Простая программа на C++ с использованием Qt4 и Poppler справилась бы с задачей. Я быстро набросал ее и протестировал на нескольких PDF-файлах. Я думаю, что ее можно было бы значительно подправить, чтобы сделать ее более устойчивой к странным ссылкам, таким как mailto: -protocol и другим портам. И, конечно, форматирование вывода может быть лучше, если вам нужно будет разобрать ее впоследствии, но она справляется со своей задачей

#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;
  }

Так как я друг cmake, я использовал этот CMakeLists.txt для компиляции:

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})

Вам понадобятся пакеты разработки Qt4 и пакеты разработки Poppler-Qt4. Если у вас нет FindPoppler.cmake в каталогах CMakeModules, зайдите и скачайте его онлайн.

Чтобы скомпилировать его, убедитесь, что в каталоге проекта есть qlinkextract (должен быть написан точно так, как указано ниже)

  • CMakeLists.txt (см. выше)
  • main.cpp (см. выше)
  • cmake/Modules/FindPoppler.cmake (скачать откуда-нибудь)
  • build/ (папка необязательна)

на консоли перейдите в папку сборки и введите

cmake ..
make

если чего-то не хватает установите недостающие пакеты

Пример вывода:

"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 

Связанный контент