10

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

3 Answers3

10

Есть 2 типа html/xml парсеров:

  1. SAX парсер - парсит в потоковом режиме, на вход подается поток html/xml, в определенных местах срабатывают т.н. хэндлеры, то есть перехватчики, которые говорят "сейчас парсер наткнулся на такой-то элемент". В хэндлер обычно прогер вставляет свой код и делает свое дело
  2. DOM парсер - засовывается весь источник, на выходе получаем дерево - иногда довольно сложное.

jsoup это разновидность DOM парсера, так что весь вопрос в том, чтобы правильно спозиционироваться в дереве полученном после парсинга - или выражаясь языком модели DOM в нодах. Это описываетс документацией jsoup API в пакете org.jsoup.nodes.

Заодно нелишним будет почитать про DOM это сразу направит мозги в нужном направлении.

Удачи.

Barmaley
  • 81,300
  • 2
    случайно, прочитал ваш ответ, и на пол дня залип в чтении SAX парсеров. Не могу понять теперь, почему на практике я их не встречал, ведь по сути это быстрей и безопасней. Очень интересно, скоро узнаем, спасибо за этот ответ. ПОставил бы +50 если мог. – Shwarz Andrei Feb 22 '16 at 16:45
8

Ну, а в чём проблема?

  1. Загрузите документ.
  2. Снавигируйте к элементу, содержащему программы.
  3. Тем же способом найдите нужные элементы данных.

Какие именно HTML-атрибуты вам нужны, устанавливается внимательным чтением исходника сайта, который вы собираетесь распарсить.

На сайте даже есть простейший пример (без навигации, просто все ссылки).

VladD
  • 206,799
5

Я это делаю так. Тоже первый раз :) Пока не разобрался как красиво выводить то, что спарсил :(

    package sm.play.sportlife.ua;

import java.io.IOException; import java.util.ArrayList; import java.util.Calendar;

import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements;

import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Color; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView;

public class MainActivity extends Activity {

private static final String TAG = MainActivity.class.getSimpleName();
public static int dayOfTheWeek = 0;
// благодоря этому классу мы будет разбирать данные на куски
public Elements time, currentday;
// то в чем будем хранить данные
public ArrayList<String> timeList = new ArrayList<String>();
public ArrayList<String> dayEventList = new ArrayList<String>();

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    /** Запрос к нашему отдельному поток на выборку данных */
    new GetDataThread().execute();

    dayOfTheWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1;
    if (dayOfTheWeek == 0) {
        dayOfTheWeek = 7;
    }

    Log.d(TAG, "День недели :" + dayOfTheWeek);
}

/**
 * А вот и внутрений класс который делает запросы в отдельном потоке
 */

public class GetDataThread extends AsyncTask<String, Void, String> {
    private TableRow row;
    private TableLayout inflate;
    private TextView txtcol1, txtcol2;
    private String eventNames;

    // private ProgressDialog prog;

    /**
     * Метод выполняющий запрос в фоне, в версиях выше 4 андроида, запросы в
     * главном потоке выполнять нельзя, поэтому все что вам нужно выполнять
     * - выносите в отдельный поток
     */
    @Override
    protected String doInBackground(String... arg) {
        String myURL = "http://www.sportlife.ua/ru/services/schedule/14875";
        // класс который захватывает страницу
        Document doc;

        try { // определяем откуда будем скачивать данные

            doc = Jsoup
                    .connect(myURL)
                    .userAgent(
                            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36")
                    .get(); // задаем с какого места парсить

            /**
             * Выбираем содержимое рассписания по индексу столбца
             * td:eq(Индекс)
             */

            // Время занятий
            int k = 0;
            String link = "#shedule-content tr:gt(0) " + "td:eq(" + k + ")";
            time = doc.select(link);

            link = "#shedule-content tr:gt(0) " + "td:eq(" + dayOfTheWeek
                    + ")";
            currentday = doc.select(link);

            /**
             * Чистим наши ArrayList для того что бы заполнить и в цикле
             * захватываем все данные какие есть на странице
             */
            timeList.clear();
            dayEventList.clear();

            for (Element times : time) {
                if (times.className().equals("time-col")) {
                    timeList.add(times.text()); // Записываем в ArrayList
                                                // время занятий
                }
            }
            /**
             * Для каждого event currentday из записываем в аррей лист
             * события дня
             */
            for (Element event : currentday) {

                if (event.hasText() == true) {
                    Elements mEvents = Jsoup.parse(event.html()).select(
                            ".event-item-body");
                    /** Может быть несколько занятий на одно и тоже время */
                    int i = 0;
                    do {
                        Element textEvent = mEvents.get(i);
                        String tmpString = textEvent.text();

                        if (eventNames == null) {
                            eventNames = tmpString + "\n";
                        } else {
                            eventNames = eventNames + tmpString + "\n";

                        }

                        i++;

                    } while (i < mEvents.size());
                    /** События заносим в список */
                    dayEventList.add(eventNames);
                    eventNames = "";
                } else
                    // dayEventList.add(titles.text());
                    dayEventList.add("");

            }
        } catch (IOException e)

        {
            e.printStackTrace();

        }

        return null;
    }

    @Override
    protected void onPreExecute() {
        // prog = new ProgressDialog(MainActivity.this);
        // prog.setMessage("Соединяемся...");
        // prog.show();

    }

    @Override
    protected void onPostExecute(String result) {

        /** ФОРМИРУЕМ ТАБЛИЦУ */

        inflate = (TableLayout) MainActivity.this
                .findViewById(R.id.mytable);

        for (int i = 0, j = 0; i < timeList.size()
                || j < dayEventList.size();) {
            row = new TableRow(MainActivity.this);
            txtcol1 = new TextView(MainActivity.this);
            if (timeList.size() > i) {
                if ((timeList.get(i) != null)) {
                    txtcol1.setText(timeList.get(i));
                    txtcol1.setBackgroundResource(R.drawable.shape_rec);
                    // txtcol1.setTextColor(Color.rgb(245, 245, 220));
                    // txtcol1.setBackgroundColor(Color.rgb(0, 0, 0));
                    i++;
                }
            } else {
                txtcol1.setText("");
            }
            row.addView(txtcol1);

            txtcol2 = new TextView(MainActivity.this);
            if ((dayEventList.size() > j)) {
                if (dayEventList.get(j) != null) {
                    txtcol2.setText(dayEventList.get(j));
                    txtcol2.setBackgroundResource(R.drawable.shape_rec);
                    // txtcol2.setMaxLines(20);
                    j++;
                }
            } else {
                txtcol2.setText("");
            }
            this.row.addView(txtcol2);

            inflate.addView(row);

        }

        /** КОНЕЦ ФОРМИРОВАНИЯ ТАБЛИЦЫ */

        // super.onPostExecute(result);
        // prog.dismiss();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

/**
 * A placeholder fragment containing a simple view.
 */
public static class PlaceholderFragment extends Fragment {

    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container,
                false);
        return rootView;
    }
}

}