Mojo::DOM - Как анализировать наборы данных из объекта dom?

У меня есть данные на странице результатов html, и я хочу итеративно анализировать наборы данных из нее. В общем формате страницы результатов есть основной раздел результатов (div), который содержит кучу подразделов (sub div), которые в свою очередь содержат различные теги с данными результатов.

Faux, pseudo, not-real code

$file = Mojo::File->new('BigData.htm');         # Read in some file
$dom  = Mojo::DOM->new($file->slurp);           # Slurp the dom out of it
                                                # 
$rs = $dom->at('div.resultsSection');           # Find the beginning of the results section
                                                # 
for my $ss ($rs->at('div.subSection') {         # Start looping through the subsections
                                                # 
    $cs = $ss->find('p.coolStuff');             # Find correlating data
    $is = $ss->find('div.importantStuff');      # 
                                                # 
    if(! defined $is) {                         # Make decisions based on data availability
        $is = $ss->find('div.differentClass');  #      and data quality
    }                                           # 
    push (@array, "$cs\t$is\n");                # Reformat it for my purposes
}                                               # 

Ясно, что фальшивый, псевдо, ненастоящий код выше полностью фальшивый во всех смыслах, кроме одного: это логическое представление того, что я пытаюсь сделать. -›at() должен возвращать объект dom, который начинается с первого вхождения заданного тега. -›find() возвращает набор совпадающих тегов. Я понимаю, что с помощью селекторов css (и других методов) я могу ограничить результаты обоих методов уникальными элементами (и я это делаю). Однако на этом мои знания заканчиваются.

Я могу найти все теги одного типа за раз. Но данные сложны, и нет возможности впоследствии сопоставить результаты.

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

Я все делаю неправильно?


person Wolf    schedule 26.03.2021    source источник


Ответы (1)


Я нашел решение, которое работает. Я не знаю, лучшее ли это решение, но оно прямое и простое, что, безусловно, является правильным направлением.

HTML-сегмент ниже начинается с основного контейнера и содержит одну строку результатов поиска: (я должен был включить это в свой исходный вопрос - извините)

<div class="container">
    <div class="row searchResultRow">
        <div class="col-sm-12">
            <div class="row">
                <div class="col-md-12">
                    <p class="searchResultTitle">Some Data Here</p>
                </div>
            </div>
            <div class="row">
                <div class="col-sm-7 col-md-8">
                    <div class="row">
                        <div class="col-md-2">
                            <div class="clearfix"> <img alt="clearfixalt" class="searchResultImg" src='/images/image.png' /> </div>
                            <p> <a class="bodyLink" href="description.html">View Details</a> </p>
                        </div>
                        <div class="col-sm-5 col-md-4">
                            <p> <span class="gridTxtLbl br-responsive-sm">Type</span> <span class="gridDataItem br-responsive-sm">Organic</span> </p>
                        </div>
                        <div class="col-sm-3 col-md-3">
                            <p> <span class="gridTxtLbl br-responsive-sm">Year</span> <span class="gridDataItem br-responsive-sm">1955</span> </p>
                        </div>
                        <div class="col-sm-4 col-md-3"> </div>
                    </div>
                    <div class="row">
                        <div class="col-md-12"> </div>
                    </div>
                </div>
                <div class="col-sm-5 col-md-4">
                    <p class="gridTxtLbl">Origin</p>
                    <div class="">
                        <div class="mapIconDiv">
                            <a href="/Maps/ShowMap" id="res_thx-1138"> 
                                <span class="iconWithText">
                                    <span class="fa fa-home" aria-hidden="true"></span>
                                    <br />Map 
                                </span>
                            </a>
                            <script>
                                $(function() {
                                    $('#res_' + thx-1138).click(function(e) {
                                        e.preventDefault();
                                        var url = $(this).attr('href');
                                        $.ajax({
                                            url: url,
                                            success: function(html) {
                                                $('#mapModal').html(html);
                                                $('#mapModal').modal();
                                                initialize(40.7856211, -76.5780298, 'Secret Location<br/>Lincoln County, NV', '2');
                                            }
                                        });
                                    });
                                });
                            </script>
                        </div>
                        <div class="searchResultAddress">
                            <br />Lincoln County, NV</div>
                    </div>
                </div>
            </div>
        </div>
    </div>

Приведенный ниже код перебирает данные и получает то, что:

use Mojo::UserAgent;
use Mojo::File;
use feature 'say';

$infile = 'searchResults.htm';

unless( -e $infile ) {                                                      # Did I already save this data?
    $ua = Mojo::UserAgent->new;                                             # No? Then go get it
    $tx = $ua->get( 'https://www.someurl.com/BigDataResults.html' );        # URL hardcoded here to simplify this post
    unless( $tx->result->is_success ) { 
        die "Doh!!!  ", $tx->result->code
    }

    $tx->result->save_to( $searchResults.htm );                                
}

$data = Mojo::File->new( $infile )->slurp;
$dom  = Mojo::DOM->new( $data );

$c = $dom->at('div.container');                                             # Return the dom from the beginning of the results data section
                                                                            #    in my case, this "div.class" is unique
for $row ($c->find('div.searchResultRow')->compact->each)                   # Return a collection of each subsection (row)
{                                                                           #    
    $data1 = $row->at('div > div > div > div > p')->text;                   # Use css direct child selectors to navigate paths into nested tag structures
    $data2 = $row->at('div > div > div > div > div > div > script')->text;  # <-- There was some lat/long data in this script I needed to parse out
    $data3 = $row->at('div > div > div > p > span')->text;                  # <-- More data in another nested tag structure
                                                                            #
    # A Lot of massaging and formatting code was here #                     
                                                                            
    push (@array, "$cs\t$is\n");                                            # wrap up the data for later
}

Это реальный код, который работает, хотя я избавился от всего, что загромождало основную логику.

Примечание для тех, кто пытается найти ответ на эту проблему:

  • Хотя прямой дочерний селектор › подобен жесткому кодированию пути и поэтому является хрупким решением, в моем случае преимущество заключается в том, что длинные пути селектора css уникальны.
person Wolf    schedule 28.03.2021