Verification: a143cc29221c9be0

Open xml file with php

Open xml file with php

Предварительная проверка

Внимание!
Прежде чем приступить к созданию файлов в формате *.xml, обязательно, проверьте наличие базовых метаданных (контактная информация журнала, его ISSN, автор, название статьи, дата статьи, страницы статьи, номер и название выпуска, номер тома, проставленный DOI и пр. Отсутствие данных приведет к ошибке, и файл не будет загружен в базу данных CrossRef!

Особое внимание следует уделить п. 1.5 «Издатель» ! Он не является обязательным по умолчанию, поэтому многие оставляют его пустым. Для того, чтобы его заполнить, идем:

Управляющий журнала —> Установка —> Детали (Название журнала, ISSN, контакты, спонсоры и поисковые машины).

xmldoi_01

Заполняем п. 1.5 «Издатель».

xmldoi_02

После этого рекомендуем еще раз проверить заполнение всех важных полей в настройках журнала.

Внимание!
Очень важный момент: вам нужно создавать / генерировать / осуществлять экспорт файлов в одной языковой версии Open Journal Systems!

Экспорт XML-файлы в Open Journal Systems

Для того, чтобы получить файлы в формате *.xml для CrossRef идем: Управляющий журнала —> Импорт/экспорт данных —>

xmldoi_03

Выбрать из списка «Экспорт в формате CrossRef XML: Экспорт метаданных статей в формате CrossRef XML».

xmldoi_04

Вы можете выбрать или экспорт всего выпуска или экспорт отдельных статей…

xmldoi_05

В списке будут отображены только те статьи, которым присвоен DOI. Можно выбрать несколько статей, данные которых будут объединены в одном XML-файле, или экспортировать каждую статью в отдельный файл.

xmldoi_06

Система предложит сохранить вам XML-файл на компьютер. Выбираем нужную папку и сохраняем.

Поскольку OJS все экспортируемые файлы по умолчанию предлагает называть «crossref», мы советуем сразу переименовывать их. Удобнее всего давать название, которое соответствует DOI статьи, тогда вы избежите путаницы как в компьютере, так и в админ-панели Crossref.

xmldoi_07

В компьютере ваш файл будет выглядеть следующим образом. Если вы хотите увидеть его содержимое (какие данные системе удалось собрать), можете воспользоваться или блокнотом (советуем использовать Notepad++ поскольку в нем есть подсветка синтаксиса) или любым Интернет-браузером (Chrome, Opera, FireFox и пр.).

xmldoi_08

Проверка файлов в Metadata Quality Check

Ниже мы приводим образцы правильных XML-файлов (нажмите на название, чтобы файл открылся в браузере):

  • submission
  • submission2

После создания файлов *.xml их нужно проверить в специальном сервисе CrossRef, который называется Metadata Quality Check. Сервис работает в режиме online. Регистрация или авторизация не требуется. Переходим по ссылке, выбираем файл с компьютера и нажимаем «upload».

xmldoi_09

Мы видим, что CrossCheck не обнаружил ошибок в метаданных (no errors), но проверка нашла всего 1 DOI, а это неправильно (см. скриншот). Правильно — 2 DOI, один из которых принадлежит статье, а другой — выпуску, данные о котором также содержатся в XML-файле! Причиной данной ошибки послужило то, что мы забыли присвоить отдельный идентификатор выпуску, файл статьи из которого мы сгенерировали и загрузили на проверку. В таком случае следует присвоить DOI выпуску, создать новый файл для статьи и проверить еще раз.

xmldoi_10ab

После того, как мы прописали DOI для выпуска и повторно создали файл, CrossCheck увидел 2 DOI.

xmldoi_10ac

Если в файле будет ошибка, сервис предупредит об этом. В данном случае мы видим ошибку в поле «registrant». Если мы откроем файл в блокноте или браузере, то увидим, что это поле остается пустым. Значит, мы забыли его заполнить в Open Journal Systems. Это поле, которые упоминалось нами выше в п. 1.5 «Издатель».

xmldoi_11

Предотвращение ошибки «submitted version is less or equal to previously submitted version» (DOI match)

Внимание!
Вы должны загружать статьи в базу данных CrossRef в том же порядке, в котором вы добавляли их в Open Journal Systems! Это позволит избежать еще одной ошибки: Record not processed because submitted version: *** is less or equal to previously submitted version (DOI match).

На следующем этапе, при регистрации XML-файлов в CrossRef, вы можете столкнуться с такой ошибкой Record not processed because submitted version: 1443949912 is less or equal to previously submitted version (DOI match). Значение «1443949912» может быть другим.

Проблема этой ошибки содержится одном из значений *.xml файла статьи:


http://mysite/index.php/MS/article/view/194

а именно в порядковом номере добавленной в OJS статьи: 194, в данном случае.

Дело в том, что при добавлении статей в OJS каждая из них получает свой порядковый номер, например:

http://mysite/index.php/MS/article/view/193            статья 1 (в выпуске)
http://mysite/index.php/MS/article/view/194            статья 2 (в выпуске)
http://mysite/index.php/MS/article/view/195            статья 3 (в выпуске)
http://mysite/index.php/MS/article/view/196            статья 4 (в выпуске)
и так далее...

В таком порядке загрузки файлов статей ошибки не будет.

Но, если вы при добавлении статьи в OJS совершите ошибку, удалите статью, добавите другую по счету, третью, а потом, например, вернетесь к первой, то порядок номеров статей, которые им дает система, не будет соответствовать реальному порядку статей в выпуске, например:

http://mysite/index.php/MS/article/view/193            статья 1 (в выпуске)
http://mysite/index.php/MS/article/view/195            статья 2 (в выпуске)
http://mysite/index.php/MS/article/view/194            статья 3 (в выпуске)
http://mysite/index.php/MS/article/view/196            статья 4 (в выпуске)

CrossRef увидит, что вы загрузили вначале статью «195», а затем пытаетесь загрузить «194» и выдаст ошибку.

В целом, расхождения «системной» и «реальной» последовательности статей на сайте это весьма частое явление. Ничего страшного в этом нет! Главное, при экспорте файлов .хml в CrossRef (о чем пойдет речь дальше) придерживаться именно «системной» последовательности. Проще всего переименовать ваши файлы (например, в «crossref») согласно «системному порядку» по возрастанию и загрузить в Crossref:

http://mysite/index.php/MS/article/view/193         статья 1 (в выпуске)          crossref_01.xml
http://mysite/index.php/MS/article/view/195         статья 2 (в выпуске)          crossref_03.xml
http://mysite/index.php/MS/article/view/194         статья 3 (в выпуске)          crossref_02.xml
http://mysite/index.php/MS/article/view/196         статья 4 (в выпуске)          crossref_04.xml

Тогда ошибки не будет.

Регистрация файлов *.xml в CrossRef

Когда возможные ошибки предупреждены и все файлы проверены, их необходимо загрузить в базу данных CrossRef. Для этого, вам нужно перейти в ваш личный кабинет и авторизироваться. Напомним, что логин и пароль вы должны получить после заключения контракта с CrossRef в отдельном письме.

rcossref_003

Переходим по ссылке и авторизируемся…

xmldoi_12

Интерфейс на английском. Нажимаем «Submissions».

xmldoi_13

Ставим галочку на слове «Metadata». Далее выбираем файл и нажимаем «upload».

xmldoi_14

Перед загрузкой можем посмотреть название файла…

xmldoi_15

Чтобы проверить статус загруженного вами файла, нужно перейти в меню «Administration» и нажать «search». Поля (фильтры) можно не заполнять.

xmldoi_16

В списке вы увидите недавно загруженные файлы, дату загрузки, начала и окончания обработки CrossRef. Если напротив файла не стоит буква «E» (error), значит ошибок в файле нет — DOI будет работать без проблем!

xmldoi_17

Система также предоставляет данные о характере ошибок в XML-файлах.

xmldoi_18

Ошибка «ISSN has already been assigned, issn is assigned to another title»

Если вы получили при загрузки файла *.xml подобную ошибку, что это значит?

Скорее всего, Вы случайно экспортировали *.xml файл из OJS на другом языке и загрузили его в CrossRef.

Дело в том, что система запоминает название журнала и ISSN при первом добавлении *.xml файла.

К примеру, первую статью Вы экспортировали из украинской версии сайта и загрузили в CrossRef, а вторую из английской версии. В таком случае возникнет подобная ошибка, поскольку система уже запомнила название Вашего журнала на украинском языке и при добавлении другого скажет, что ISSN занят таким-то другим названием.

Как этого избежать? Быть внимательнее при экспорте статей и последующей загрузки в CrossRef.

1. Веб-скрейпинг на PHP с использованием Guzzle, XML и XPath

Guzzle — это HTTP-клиент для PHP, позволяющий легко отправлять HTTP-запросы. Он предоставляет простой интерфейс для написания строк запросов. XML — язык разметки для представления документов в удобочитаемом для человека и компьютера формате. XPath — это язык запросов для навигации и выбора XML-узлов.

Посмотрим, как можно использовать эти инструменты для скрейпинга сайта. Начнём с установки Guzzle через Composer, выполним в терминале команду:

composer require guzzlehttp/guzzle

После установки Guzzle создадим новый PHP-файл guzzle_requests.php, в который будем добавлять код. Для примера спарсим сайт Books to Scrape, другие сайты вы сможете скрейпить действуя аналогично. Books to Scrape выглядит так:

Нам нужно извлечь названия книг и отобразить их в терминале. Первым делом нужно разобраться в HTML-макете. В этом случае его можно посмотреть, кликнув правой кнопкой над списком книг и выбрав пункт Inspect. Скриншот исходного кода страницы:

Внутри элемента находится список. Следующий дочерний элемент — li. Нам нужно название книги, оно записано внутри а, внутри h3, внутри article, и наконец внутри элемента li. Чтобы инициализировать Guzzle, XML и Xpath добавьте в guzzle_requests.php такой код:

get(‘https://books.toscrape.com/');
$htmlString = (string) $response->getBody();
//add this line to suppress any warnings
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new DOMXPath($doc);

Этот код скачивает страницу в виде строки, затем парсит её с помощью XML и присваивает переменной $xpath. Теперь нужно указать целевой текст внутри тега a. Добавим в файл

$titles = $xpath->evaluate(‘//ol[@class="row"]//li//article//h3/a’);
$extractedTitles = [];
foreach ($titles as $title) {
$extractedTitles[] = $title->textContent.PHP_EOL;
echo $title->textContent.PHP_EOL;
}

В этом коде //ol[@class="row"] получает весь список. У каждого элемента списка есть тег a, который нас интересует для извлечения названий книг. Только тег h3 содержит a, это облегчает работу с ним. Для извлечения текстовой информации и её отображения в терминале воспользуемся циклом foreach. На этом этапе вы можете что-нибудь сделать с извлечёнными данными, например, присвоить их переменной массива, записать в файл или сохранить в БД. Вы можете выполнить файл с помощью PHP в терминале, запустив приведённую ниже команду.

php guzzle_requests.php

На экране должно появиться вот что:

Работает. А что если теперь нам нужно получить стоимость книг?

Цена находится в теге p внутри тега div. В коде страницы несколько тегов p и несколько div. Чтобы найти нужные, мы воспользуемся селекторами классов CSS, которые, к счастью, уникальны для каждого тега. Вот код получения тега цен и конкатенации его со строкой заголовка:

$titles = $xpath->evaluate(‘//ol[@class=”row”]//li//article//h3/a’);
$prices = $xpath->evaluate(‘//ol[@class=”row”]//li//article//div[@class=”product_price”]//p[@class=”price_color”]’);
foreach ($titles as $key => $title) {
echo $title->textContent . ‘ @ ‘. $prices[$key]->textContent.PHP_EOL;
}

Если исполнить код в терминале, то получим:

Ваш код должен выглядеть так:

get(‘https://books.toscrape.com/');
$htmlString = (string) $response->getBody();
//add this line to suppress any warnings
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new DOMXPath($doc);
$titles = $xpath->evaluate(‘//ol[@class=”row”]//li//article//h3/a’);
$prices = $xpath->evaluate(‘//ol[@class=”row”]//li//article//div[@class=”product_price”]//p[@class=”price_color”]’);
foreach ($titles as $key => $title) {
echo $title->textContent . ‘ @ ‘. $prices[$key]->textContent.PHP_EOL;
}

Конечно, это лишь базовый веб-скрейпер, его можно улучшить. Перейдём к следующей библиотеке.

2. Веб-скрейпинг на PHP с использованием Goutte

Goutte — ещё один прекрасный HTTP-клиент для PHP, созданный специально для веб-скрейпинга. Его разработал автор фреймворка Symfony, он предоставляет хороший API для извлечения данных из HTML/XML-ответов сайтов. Вот некоторые компоненты клиента для упрощений скрейпинга:

  • BrowserKit для симуляции поведения браузера.
  • CssSelector для перевода CSS-запросов в XPath-запросы.
  • DomCrawler позволяет использовать DOMDocument и XPath.
  • Symfony HTTP-клиент — новый компонент от команды Symfony.

Установим Goutte через Сomposer, выполнив в терминале:

composer require fabpot/goutte

После установки создадим новый файл goutte_requests.php для кода. Ниже мы обсудим, что мы сделали с помощью библиотеки Guzzle в предыдущей главе. Извлечём с помощью Goutte названия книг с сайта Books to Scrape. Также покажем, как можно добавлять цены в переменную массива и использовать её в коде. Добавьте в файл goutte_requests.php этот код:

request(‘GET’, ‘https://books.toscrape.com/');
$titles = $response->evaluate(‘//ol[@class=”row”]//li//article//h3/a’);
$prices = $response->evaluate(‘//ol[@class=”row”]//li//article//div[@class=”product_price”]//p[@class=”price_color”]’);
// we can store the prices into an array
$priceArray = [];
foreach ($prices as $key => $price) {
$priceArray[] = $price->textContent;
}
// we extract the titles and display to the terminal together with the prices
foreach ($titles as $key => $title) {
echo $title->textContent . ‘ @ ‘. $priceArray[$key] . PHP_EOL;
}

Выполним его с помощью команды в терминале:

 php goutte_requests.php

Результат:

Выше показан один из способов скрейпинга с помощью Goutte. Давайте рассмотрим способ с применением компонента CSSSelector из Goutte. CSS-селектор проще в использовании, чем XPath из предыдущего способа. Создадим ещё один PHP файл — goutte_css_requests.php. Добавим в него код:

request(‘GET’, ‘https://books.toscrape.com/');
// get prices into an array
$prices = [];
$response->filter(‘.row li article div.product_price p.price_color’)->each(function ($node) use (&$prices) {
$prices[] = $node->text();
});
// echo titles and prices
$priceIndex = 0;
$response->filter(‘.row li article h3 a’)->each(function ($node) use ($prices, &$priceIndex) {
echo $node->text() . ‘ @ ‘ . $prices[$priceIndex] .PHP_EOL;
$priceIndex++;
});

CSSSelector сделал код чище и удобочитаемее. Вы могли заметить, что мы использовали оператор &, который обеспечивает передачу в цикл «each» ссылки на переменную, а не просто её значение. Если цикл меняет &$prices, то фактические значения за пределами цикла тоже меняются. Подробнее о присвоении по ссылкам можно почитать в официальной документации PHP. Выполним файл в терминале:

php goutte_css_requests.php

И увидим аналогичный результат:

Наш веб-скрейпер с Goutte работает хорошо. Давайте теперь посмотрим, можно ли кликнуть на ссылку и перейти на другую страницу. На нашем демо-сайте Books to Scrape при клике на название книги загружается страница с описанием:

Посмотрим, можно ли кликнуть на ссылку в списке книг, перейти на страницу с описанием и извлечь его. Изучим код интересующей страницы:

Наш целевой путь будет таким: элемент div clаss="content", затем div id="content_inner", затем один тег article, и наконец тег p. У нас есть несколько тегов p, тег с описанием является четвёртым внутри родительского элемента div class="content". Поскольку массивы начинаются с 0, мы будем получать ноду в третьем индексе. Теперь напишем код. Сначала добавим Composer-пакет для облегчения парсинга HTML5:

composer require masterminds/html5

Модифицируем файл goutte_css_requests.php:

request(‘GET’, ‘https://books.toscrape.com/');
// get prices into an array
$prices = [];
$response->filter(‘.row li article div.product_price p.price_color’)
->each(function ($node) use (&$prices) {
$prices[] = $node->text();
});
// echo title, price, and description
$priceIndex = 0;
$response->filter(‘.row li article h3 a’)
->each(function ($node) use ($prices, &$priceIndex, $httpClient) {
$title = $node->text();
$price = $prices[$priceIndex];
//getting the description
$description = $httpClient->click($node->link())
->filter(‘.content #content_inner article p’)->eq(3)->text();
// display the result
echo “{$title} @ {$price} : {$description}\n\n”;
$priceIndex++;
});

Если исполнить его в терминале, то мы увидим название, цену и описание книги:

С помощью компонента Goutte CSS Selector и возможности кликать на странице вы можете легко бродить по многостраничным сайтам и извлекать сколько угодно информации.

3. Веб-скрейпинг на PHP с использованием Simple HTML DOM

Simple HTML DOM — ещё одна минималистичная библиотека веб-скрейпинга для PHP. Извлекать информацию будем с того же сайта. Прежде чем устанавливать пакет, изменим файл composer.json и добавим нижеуказанные строки сразу после блока require:{}, чтобы избежать ошибок версионирования:

“minimum-stability”: “dev”,
“prefer-stable”: true

Теперь можно устанавливать библиотеку:

composer require simplehtmldom/simplehtmldom

Затем создаём новый PHP-файл — simplehtmldom_requests.php. Мы уже рассматривали макет страницы, так что сразу перейдём к коду. Добавьте в simplehtmldom_requests.php:

load(‘https://books.toscrape.com/');
// echo the title
echo $response->find(‘title’, 0)->plaintext . PHP_EOL . PHP_EOL;
// get the prices into an array
$prices = [];
foreach ($response->find(‘.row li article div.product_price p.price_color’) as $price) {
$prices[] = $price->plaintext;
}
// echo titles and prices
foreach ($response->find(‘.row li article h3 a’) as $key => $title) {
echo “{$title->plaintext} @ {$prices[$key]} \n”;
}

Если выполнить код в терминале, получим:

Больше способов скрейпинга с помощью Simple HTML DOM описано в официальной документации по API.

4. Веб-скрейпинг на PHP с использованием Headless-браузера Symfony Panther

Headless-браузер — это браузер без графического интерфейса. Благодаря этой особенности мы можем использовать терминал для загрузки страниц в среде, аналогичной браузеру и писать код для управления навигацией, как в предыдущих примерах. Зачем это нужно? Сегодня большинство разработчиков используют для развёртывания веб-фреймворки на JavaScript. Эти фреймворки генерируют внутри браузеров HTML-код. В других случаях для динамической загрузки содержимого применяют AJAX. В предыдущих примерах мы использовали статичные HTML-страницы, поэтому и результат скрейпинга был согласованным. А в случае с динамическими страницами, в которых HTML генерируется с помощью JavaScript и AJAX, получившееся DOM-дерево может сильно отличаться, что нарушит работу скрейпера. Решить эту проблему поможет headless-браузер.

В качестве такого браузера можно использовать PHP-библиотеку Symfony Panther. Её применяют и для скрейпинга, и для прогона тестов с использованием настоящих браузеров. Кроме того, библиотека предоставляет такие же методы, как и Goutte, поэтому можно использовать Panther вместо Goutte. Возможности Panther:

  • исполняет JavaScript в веб-страницах;
  • поддерживает удалённое браузерное тестирование;
  • поддерживает асинхронную загрузку элементов посредством ожидания загрузки других элементов, прежде чем выполнить определённую строку кода;
  • поддерживает все реализации Chrome и Firefox;
  • может делать скриншоты;
  • позволяет запускать пользовательский JS-код или XPath-запросы в контексте загруженной страницы.

Мы уже много скрейпили, попробуем кое-что другое. Будем загружать HTML-страницу и делать её скришот. Установим Symfony Panther:

composer require symfony/panther

Создадим новый PHP-файл panther_requests.php. Добавим в него код:

get(‘https://books.toscrape.com/');
// take screenshot and store in current directory
$response->takeScreenshot($saveAs = ‘books_scrape_homepage.jpg’);
// let’s display some book titles
$response->getCrawler()->filter(‘.row li article h3 a’)
->each(function ($node) {
echo $node->text() . PHP_EOL;
});

Чтобы запустить его в системе, нужно установить драйверы для Chrome или Firefox, в зависимости от клиента, используемого в вашем коде. К счастью, Composer может это сделать автоматически. Выполним в терминале команду установки и определения драйверов:

composer require --dev dbrekelmans/bdi && vendor/bin/bdi detect drivers

Теперь можно выполнить в терминале PHP-файл, который сделает скриншот страницы и сохранит его в текущую директорию, а потом покажет список названий книг со страницы.


AJAX XML Пример

В следующем примере показано, как веб страница может получать информацию из файла XML с помощью AJAX:


Объяснение примера

Когда пользователь нажимает на кнопку "Получить информацию о компакт диске" выше, loadDoc() функция выполняется.

Функция loadDoc() создает XMLHttpRequest объект, добавляет функцию, которая будет выполнена, когда ответ сервера готов, и посылает запрос от к серверу.

Когда ответ сервера готов, создается таблица HTML, узлы (элементы) извлекаются из файла XML, и наконец, обновляется "demo" элемент с помощью таблицы HTML, заполненной данными XML:

function loadDoc() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
    myFunction(this);
    }
  };
  xhttp.open("GET", "cd_catalog.xml", true);
  xhttp.send();
}
function myFunction(xml) {
  var i;
  var xmlDoc = xml.responseXML;
  var table="

Artist Title ";
  var x = xmlDoc.getElementsByTagName("CD");
  for (i = 0; i     table += " " +
    x[i].getElementsByTagName("ARTIST")[0].childNodes[0].nodeValue +
    " " +
    x[i].getElementsByTagName("TITLE")[0].childNodes[0].nodeValue +
    " ";
  }
  document.getElementById("demo").innerHTML = table;
}