Verification: a143cc29221c9be0

Parsing xml string in php

Parsing xml string in php

Вместо предисловия

Первоначально статья писалась, когда деревья были большими, коты были котятами, Android был версии 2.3, а библиотека jsoup была версии 1.6.1.

С тех пор утекло много воды. Хорошая новость - библиотека подросла до версии 1.13.1, стала чуть меньше размером, стала быстрее работать (почти в два раза). Плохая новость - мои примеры, связанные с интернетом, перестали работать в Android 4.0, так как теперь явно запретили использовать сетевые операции в основном потоке.

Я оставлю старую версию статьи здесь. Если вы пишете программы под старые устройства, то всё остаётся без изменений. Примеры под новые устройства находятся в закрытой зоне 4 курса.

Общая информация

Рассмотрим примеры работы с библиотекой jsoup. Java-библиотека jsoup предназначена для разбора HTML-страниц (парсинг), позволяя извлечь необходимые данные, используя DOM, CSS и методы в стиле jQuery.

Библиотека поддерживает спецификации HTML5 и позволяет парсить страницы, как это делают современные браузеры.

Библиотеке можно подсунуть для анализа URL, файл или строку.

Официальная страница библиотеки: jsoup Java HTML Parser, with best of DOM, CSS, and jquery

Подключаем библиотеку

В Android Studio пропишите в файле build.gradle строку в блоке зависимостей.


implementation 'org.jsoup:jsoup:1.13.1'

Создаём новый проект JsoupDemo. Добавляем на форму кнопку и TextView.

После установки библиотеки вам нужно получить документ для разбора текста. Это может быть страница на сайте или локальный файл на устройстве. Таким образом вам надо подключиться к нужной странице и получить объект класса Document. При импортировании обращайте внимание на полное название класса org.jsoup.nodes.Document, так как многие пакеты имеют в своём составе одноимённый класс.


Document doc  = Jsoup.connect(URL).get();

Получив документ в своё распоряжение, вы можете извлекать требуемую информацию. Например, вы можете получить все теги meta:


Elements metaElements = doc.select("meta");

Метод select() позволяет получить нужные теги.

Если нужно получить атрибут тега, то используйте метод attr():


String name = metaElement.attr("name");

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

. Тогда код будет следующим.


Elements mainHeaderElements = doc.select("h2.main");

Первый пример для знакомства

Для первого знакомства разберём простой пример. А потом будем его усложнять. Создадим переменную, содержащий html-текст. Далее вызываем библиотеку jsoup и смотрим на результат.


// Kotlin
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.jsoupdemo

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import org.jsoup.Jsoup


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView: TextView = findViewById(R.id.textView)
        val button: Button = findViewById(R.id.button)

        button.setOnClickListener {
            val html = ("
Коты учатся кодить"
                    + "

Коты умеют шкодить.
Они великие программисты." + "

А еще они умеют мяукать.

" + "Подробности здесь" + "") val doc = Jsoup.parse(html) textView.text = doc.html() } } }
// Java package ru.alexanderklimov.jsoupdemo; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class JsoupDemoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final Button button = findViewById(R.id.button); final TextView txtView = findViewById(R.id.textView); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String html = " Коты учатся кодить" + "

Коты умеют шкодить.
Они великие программисты." + "

А еще они умеют мяукать.

" + "Подробности здесь" + ""; Document doc = Jsoup.parse(html); textView.setText(doc.html()); } }); } }

Запустите проект и нажмите на кнопку. На экране отобразится наш текст. Но если вы присмотритесь внимательнее, то заметите некоторые отличия (скорее всего вы и не заметили). Я намеренно сделал две "ошибки". Во-первых, я не закрыл тег , а также не закрыл тег

у первого параграфа. Однако библиотека сама подставила недостающие элементы. Именно так поступают и браузеры, если веб-мастер по невнимательности забывает ставить закрывающие парные теги.

Что мы сделали? Мы передали нужный html-текст библиотеке Jsoup и попросили его осуществить его разбор (метод parse()). В результате мы получаем экземпляр класса Document, из которого с помощью метода html() извлекаем уже обработанный текст, с которым можно работать дальше.

Если у вас всё получилось, то можно перейти к более сложным примерам. Подробная документация по методам и свойствам есть на сайте библиотеки. Вам нужно только пробовать.

Извлекаем заголовок страницы

Заголовок страницы находится в теге . Чтобы получить текст заголовка, воспользуемся методом Document.title():


// Kotlin
textView.text = doc.title()

// Java textView.setText(doc.title()); // вернёт строку "Коты учатся кодить"

Извлекаем ссылки

Теперь попробуем поработать с ссылками. В нашем тексте есть ссылка, которую можно разбить на несколько логических элементов: адрес, на который ведёт ссылка, текст в ссылке и полная ссылка, которая объединяет оба элемента.

Начнём с адреса ссылки:


// Kotlin
val doc = Jsoup.parse(html)
val link = doc.select("a").first()
val linkHref = link.attr("href")
textView.text = linkHref

// Java Document doc = Jsoup.parse(html); Element link = doc.select("a").first(); String linkHref = link.attr("href"); textView.setText(linkHref); //http://developer.alexanderklimov.ru

Чтобы получить текст ссылки:


// Kotlin
val linkInnerH = link.html()
textView.text = linkInnerH

// Java String linkInnerH = link.html(); textView.setText(linkInnerH); //Подробности здесь

И, наконец, общий вариант:


// Kotlin
val linkOuterH = link.outerHtml()
textView.text = linkOuterH

// Java String linkOuterH = link.outerHtml(); tvInfo.setText(linkOuterH);

Разбор текста с сайта

Этот пример не работает на новых устройствах, так как запрещено работать с сетью в общем потоке!

Некоторые несознательные граждане могут меня обвинить в том, что я использовал синтетический пример, специально подготовленный для демонстрации. И хотят видеть пример с использованием ваших тырнетов. Ну что ж, вот вам пример.


Document doc = null;
try {
	doc = Jsoup.connect("http://developer.alexanderklimov.ru/android/").get();
} catch (IOException e) {
	e.printStackTrace();
}
String title = doc.title();
textView.setText(title);

Я подключаюсь к самой известной странице в мире http://developer.alexanderklimov.ru/android/ и получаю его заголовок.

Не забудьте установить разрешение на подключение к Интернету вашей программе. Я сам сначала долго тупил, не понимая, почему моя программа вылетала с ошибкой. Но, посмотрев в честные глаза своего кота, я понял в чем моя ошибка и исправил ее. Коты рулят.

Разбор текста из файла

Последний пример, который мы не разобрали - это разбор текста из файла. В этом случае используется метод Jsoup.parse(File in, String charsetName, String baseUri):


File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

Попробуйте самостоятельно. Удачи в программировании! Да пребудет с вами кот!

Открытие файла

Т.к. PHP написан на языке C (читается как «Си»), в нём как и в Си, работа с файлами разделена на 3 этапа:

  1. Открытие файла
  2. Чтение/Запись
  3. Закрытие файла

В начале файл открывается в нужном режиме чтения, при этом возвращается целое число, идентификатор открытого файла или дескриптор файла. Затем выполняются команды связанные с чтением или записью в файл, при этом эти команды ориентируются уже на полученный дескриптор файла а не на его имя. После выполнения нужных команд, файл лучше всего закрыть. Конечно PHP закроет все файлы автоматически после завершения сценария, однако если ваш файл долго обрабатывается, то может происходить утечка ресурсов.


Для открытия файла в PHP используется функция fopen(), общий вид функции:


fopen( string $filename , string $mode);

Функция принимает 2 базовых параметра это

  • $filename — имя файла (путь к файлу)
  • $mode — режим чтения файла

В зависимости от режима открытия файла вы можете выполнять те или иные операции над файлом, ниже приведена таблица с описанием режимов чтения:

Режим Чтение Запись Файловый указатель Очистка файла Создать, если файла нет Ошибка, если файл есть
r Да Нет В начале Нет Нет Нет
r+ Да Да В начале Нет Нет Нет
w Нет Да В начале Да Да Нет
w+ Да Да В начале Да Да Нет
a Нет Да В конце Нет Да Нет
a+ Да Да В конце Нет Да Нет
x Нет Да В начале Нет Да Да
x+ Да Да В начале Нет Да Да
c Нет Да В начале Нет Да Нет
c+ Да Да В начале Нет Да Нет

Однако это не полный список режимов чтения файла. Дело в том, что в конце любой из строк обозначающих режим открытия файла (r, w, a и т.п.) можно добавить ещё один необязательный символ b или t .

  • b — файл открывается в режиме бинарного чтения/записи
  • t — файл открывается в режиме трансляции символа перевода строки (символы \n для UNIX или \r\n для Windows) и файл воспринимается как текстовый

Рассмотрим пару примеров:


//Открытие файла на чтение
$f = fopen('home/www/file.txt', 'rt');

//Открытие HTTP-соединения на чтение
$f = fopen('https://it-svalka.ru/', 'rt');

//Открытие FTP соединения
$f = fopen('ftp://user:password@example.com/log.txt', 'wt');

Запись и закрытие

Запись в файл осуществляется функцией fwrite() которая принимает 2 аргументам, указатель на файл и строку которую нужно записать в файл. Пример:


//Открытие тестового файла
$file = fopen('test.txt', 'wt');
//Запись строки в файл
fwrite($file, 'Текущая дата и время: ' . date('d.m.y H:i:s'));
//Закрытие файла
fclose($file);

В результате выполнения кода, будет создан файл (если его нет) test.txt и запишется строка, в моём случае:


Текущая дата и время: 10.09.20 14:35:46

Как видите после записи я вызываю функцию fclose() куда передаю дескриптор файла для его закрытия. Давайте рассмотрим несколько практических задач которые могут встретиться вам в реальных проектах.

Логирование данных

Одной из частых задач связанных с чтением/записью фалов, является логирование данных о работе веб-сайта. Логировать можно всё что угодно, ошибки, результаты запросов к БД, активность пользователей и т.п.


//Адрес файла с логами храним в константе
define('LOG_FILE', 'log.txt');

/**
 * Функция записи лога
 * @param $textToLog
 */
function logFile($textToLog){
    //Открытие файла лога
    $file = fopen(LOG_FILE, 'at');
    $sepatrator = '-----------------------------------------------';
    //Запись строки в файл
    fwrite($file, "\n" . $sepatrator . "\n". $textToLog . "\nдата и время записи: " . date('d.m.y H:i:s') . "\n");
    //Закрытие файла
    fclose($file);
}

//Вызов функции
logFile('Пользователь открыл страницу');

В результате обращения к этому скрипту, в файл log.txt попадут следующие данные:


-----------------------------------------------
Пользователь открыл страницу
дата и время записи: 10.09.20 14:52:35

-----------------------------------------------
Пользователь открыл страницу
дата и время записи: 10.09.20 14:52:37

Обратите внимание на двойные кавычки вместо одинарных. Это нужно для того, чтобы PHP корретно интерпретировал сочетание «\n» как символ переноса строки. Функцию logFile() можно улучшать до бесконечности, добавляя различные дополнительные данные из сессии пользователя, данных запроса, данных о сервере и т.п.