【问题标题】:parse HTML into objects将 HTML 解析为对象
【发布时间】:2016-02-10 18:13:47
【问题描述】:

我有以下 html,我正在尝试使用 jsoup 将其解析为 Java 中的对象。

我正在尝试遍历元素并将所有“类”提取为对象以生成时间表数据。每个“班级”都有时间、地点、讲师和描述等,但这不是问题。 所有元素都属于tt_details 类。每一天都没有特定的父子关系,但是我可以使用Elements dayNames = content.getElementsByClass("tt_day"); 提取所涉及的天数

每天可以有不同数量的“课程”,如您所见,周一有 3 个“课程”,周二有,因此正常的循环结构不起作用。我怎样才能做到这一点?

<div class='tt_details'>
    <div class='tt_day'>Mon</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>11:00 - 13:00
        <div class='tt_day_small'> (Mon)</div>
    </div>
    <div class='tt_detail'>Internet of Things<br/>E1010 - MAC Lab <br/></div>
    <div class='tt_lecturer'>Loftus, M</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>13:00 - 14:00
        <div class='tt_day_small'> (Mon)</div>
    </div>
    <div class='tt_detail'>Computer Systems & Networking<br/>A0004 - Tiered Lecture Theatre (132) <br/></div>
    <div class='tt_lecturer'>Lang, D</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>16:00 - 18:00
        <div class='tt_day_small'> (Mon)</div>
    </div>
    <div class='tt_detail'>Intro.to Programming L8<br/>D2005 - Computer Laboratory (32) <br/></div>
    <div class='tt_lecturer'>Kinsella,V</div>
</div>
<div class='tt_details'>
    <div class='tt_day'>Tue</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>09:00 - 10:00
        <div class='tt_day_small'> (Tue)</div>
    </div>
    <div class='tt_detail'>Mathematics 2<br/>A0004 - Tiered Lecture Theatre (132) <br/></div>
    <div class='tt_lecturer'>O'Regan,D</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>10:00 - 11:00
        <div class='tt_day_small'> (Tue)</div>
    </div>
    <div class='tt_detail'>Mathematics 2<br/>E0017 - Tiered Classroom (106) <br/></div>
    <div class='tt_lecturer'>O'Regan,D</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>11:00 - 12:00
        <div class='tt_day_small'> (Tue)</div>
    </div>
    <div class='tt_detail'>Intro to Programming<br/>A0006 - Tiered Lecture Theatre (152) <br/></div>
    <div class='tt_lecturer'>Kinsella,V</div>
</div>
<div class='tt_details'>
    <div class='tt_timeslot'>16:00 - 17:00
        <div class='tt_day_small'> (Tue)</div>
    </div>
    <div class='tt_detail'>Computer Systems & Networking<br/>A0006 - Tiered Lecture Theatre (152) <br/></div>
    <div class='tt_lecturer'>Lang, D</div>
</div>

【问题讨论】:

  • 你可以在你的项目中使用 jQuery 吗?这会让事情变得容易得多。
  • html 就像一个 XML,因此您可以使用 xml 注释创建对象并使用 Mashal 和 unMarshal
  • 它是一个为安卓应用提供数据的爬虫,所以我没有使用任何 jQuery

标签: java jsoup


【解决方案1】:

这样的事情可能会有所帮助:

String html = ""
        +"<div class='tt_details'>"
        +"    <div class='tt_day'>Mon</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>11:00 - 13:00"
        +"        <div class='tt_day_small'> (Mon)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Internet of Things<br/>E1010 - MAC Lab <br/></div>"
        +"    <div class='tt_lecturer'>Loftus, M</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>13:00 - 14:00"
        +"        <div class='tt_day_small'> (Mon)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Computer Systems & Networking<br/>A0004 - Tiered Lecture Theatre (132) <br/></div>"
        +"    <div class='tt_lecturer'>Lang, D</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>16:00 - 18:00"
        +"        <div class='tt_day_small'> (Mon)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Intro.to Programming L8<br/>D2005 - Computer Laboratory (32) <br/></div>"
        +"    <div class='tt_lecturer'>Kinsella,V</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_day'>Tue</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>09:00 - 10:00"
        +"        <div class='tt_day_small'> (Tue)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Mathematics 2<br/>A0004 - Tiered Lecture Theatre (132) <br/></div>"
        +"    <div class='tt_lecturer'>O'Regan,D</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>10:00 - 11:00"
        +"        <div class='tt_day_small'> (Tue)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Mathematics 2<br/>E0017 - Tiered Classroom (106) <br/></div>"
        +"    <div class='tt_lecturer'>O'Regan,D</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>11:00 - 12:00"
        +"        <div class='tt_day_small'> (Tue)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Intro to Programming<br/>A0006 - Tiered Lecture Theatre (152) <br/></div>"
        +"    <div class='tt_lecturer'>Kinsella,V</div>"
        +"</div>"
        +"<div class='tt_details'>"
        +"    <div class='tt_timeslot'>16:00 - 17:00"
        +"        <div class='tt_day_small'> (Tue)</div>"
        +"    </div>"
        +"    <div class='tt_detail'>Computer Systems & Networking<br/>A0006 - Tiered Lecture Theatre (152) <br/></div>"
        +"    <div class='tt_lecturer'>Lang, D</div>"
        +"</div>"
        ;
Document doc = Jsoup.parse(html);
Elements courseEls = doc.select("div.tt_details:not(:has(div.tt_day))");
class Course{
    public Course(String day, String time, String lecturer, String subject) {
        super();
        this.day = day;
        this.time = time;
        this.lecturer = lecturer;
        this.subject = subject;
    }
    public String day;
    public String time;
    public String lecturer;
    public String subject;

    public String toString(){
        return day + " : "+ time +" : "+ lecturer + " : "+ subject;
    }
}
Map<String,List<Course>> coursesByDay = new HashMap<>();
for (Element courseEl : courseEls){
    Element timeSlotEl = courseEl.select(".tt_timeslot").first();
    String timeSlotStr = timeSlotEl.ownText();
    String dayStr = timeSlotEl.select(".tt_day_small").first().text().trim().replace("(", "").replace(")", "");
    String detailStr = courseEl.select(".tt_detail").first().text();
    String lecturerStr = courseEl.select(".tt_lecturer").first().text();

    Course course = new Course(dayStr, timeSlotStr, lecturerStr, detailStr);
    List<Course> courses = coursesByDay.get(dayStr);
    if (courses == null){
        courses = new ArrayList<>();
        coursesByDay.put(dayStr, courses);
    }
    courses.add(course);
}

//get all courses on Tue
List<Course> courses = coursesByDay.get("Tue");
for (Course c : courses){
    System.out.println(c);
}

这会创建一张包含每日课程的地图。所以地图键是日期,它包含一个课程对象列表。

对此的一些评论:

  • 我使用自定义对象来保存课程信息
  • 我使用选择器div.tt_details:not(:has(div.tt_day)) 只获取课程 div,而忽略了当天的 div。这是可能的,因为当天的信息在课程 div 中重复出现。
  • CSS 选择器用于获取详细信息。
  • 注意 ownText() 和 text() 之间的区别。这用于仅获取没有日期的时间信息。
  • 地图由其内容动态填充。

【讨论】:

  • 我无法遍历元素集。 for (Element courseEl : courseEls){ 行给出了一个编译错误,说 Unknown class: courselEls
  • courseEls 应该属于 Elements 类,它实现了 List 接口。您应该能够对此进行迭代。是我还是你打错字了?
  • 抱歉,这是由 typeo 引起的
【解决方案2】:

试试这个

static final String[] DETAILS = { "tt_timeslot", "tt_day_small", "tt_detail", "tt_lecturer" };

     Document doc = Jsoup.parse(html);
     String day = null;
     for (Element e : doc.select("div.tt_details")) {
         Elements days = e.select("div.tt_day");
         if (days.size() > 0) {
             day = days.get(0).text();
             System.out.printf("    *** %s ***%n", day);
         } else {
             System.out.printf("        --------%n");
             for (String cls : DETAILS) {
                 Elements elements = e.select("div." + cls);
                 if (elements.size() > 0)
                     System.out.printf("%24s : %s%n", cls, elements.get(0).text());
             }
         }
     }

结果

*** Mon ***
    --------
         tt_timeslot : 11:00 - 13:00 (Mon)
        tt_day_small : (Mon)
           tt_detail : Internet of Things E1010 - MAC Lab
         tt_lecturer : Loftus, M
    --------
         tt_timeslot : 13:00 - 14:00 (Mon)
        tt_day_small : (Mon)
           tt_detail : Computer Systems & Networking A0004 - Tiered Lecture Theatre (132)
         tt_lecturer : Lang, D
    --------
         tt_timeslot : 16:00 - 18:00 (Mon)
        tt_day_small : (Mon)
           tt_detail : Intro.to Programming L8 D2005 - Computer Laboratory (32)
         tt_lecturer : Kinsella,V
*** Tue ***
    --------
         tt_timeslot : 09:00 - 10:00 (Tue)
        tt_day_small : (Tue)
           tt_detail : Mathematics 2 A0004 - Tiered Lecture Theatre (132)
         tt_lecturer : O'Regan,D
    --------
         tt_timeslot : 10:00 - 11:00 (Tue)
        tt_day_small : (Tue)
           tt_detail : Mathematics 2 E0017 - Tiered Classroom (106)
         tt_lecturer : O'Regan,D
    --------
         tt_timeslot : 11:00 - 12:00 (Tue)
        tt_day_small : (Tue)
           tt_detail : Intro to Programming A0006 - Tiered Lecture Theatre (152)
         tt_lecturer : Kinsella,V
    --------
         tt_timeslot : 16:00 - 17:00 (Tue)
        tt_day_small : (Tue)
           tt_detail : Computer Systems & Networking A0006 - Tiered Lecture Theatre (152)
         tt_lecturer : Lang, D

【讨论】:

  • 使用 CSS 获取具有某些类的元素最好使用element.select(".classname") 来完成,因为如果一个元素带有多个类,您就无法确定类名的顺序。使用点运算符,您可以轻松处理此问题。您也可以像这样连接:el.select(".className1.className2")。我仍然喜欢你简单的方法。 +1
【解决方案3】:

如果这是来自在线页面的 HTML 源代码,那么您可以将 selenium 用于此类目的,为此您必须导入 selenium jar。

我的建议——

String datentime = driver.findElement(By.className("tt_timeslot")).getText(); 

如果元素名称相同,则使用唯一的 id 或 css 选择器或 xpath。

【讨论】:

  • 我必须使用的唯一信息是 HTML 中发布的内容,没有更多的 id 或 css 选择器可以使用
  • 您可以使用现有的
    元素作为位置来创建 xpath。例如 - //div/div[@class='tt_day']
猜你喜欢
  • 2016-03-26
  • 2017-02-17
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多