代码之家  ›  专栏  ›  技术社区  ›  user1768233

如何使用Web::Scraper刮取以下内容?

  •  0
  • user1768233  · 技术社区  · 11 年前

    这个问题不同于但与 How to Parse this HTML with Web::Scraper? .

    我必须用 Web::Scraper 其中HTML可以稍微改变。有时可能是

    <div>
      <p>
        <strong>TITLE1</strong>
        <br>
        DESCRIPTION1
      </p>
      <p>
        <strong>TITLE2</strong>
        <br>
        DESCRIPTION2
      </p>
      <p>
        <strong>TITLE3</strong>
        <br>
        DESCRIPTION3
      </p>
    </div>
    

    我正在提取 Web::Scraper 具有以下代码

    my $test = scraper {
        process 'div p', 'test[]' => scraper {
            process 'p strong', 'name' => 'TEXT';
            process '//p/text()', 'desc' => [ 'TEXT', sub { s/^\s+|\s+$//g } ];
        };
    };
    

    但有时它包含以下HTML(请注意,每个标题和描述不再由分隔 <p> ).

    <div>
      <p>
        <strong>TITLE1</strong>
        <br>
        DESCRIPTION1
        <strong>TITLE2</strong>
        <br>
        DESCRIPTION2
        <strong>TITLE3</strong>
        <br>
        DESCRIPTION3
      </p>
    </div>
    

    如何将上面的HTML转换为

    test => [
      { desc => "DESCRIPTION1 ", name => "TITLE1" },
      { desc => "DESCRIPTION2 ", name => "TITLE2" },
      { desc => "DESCRIPTION3 ", name => "TITLE3" },
    ]
    

    我尝试过修改上面的代码,但我无法确定使用什么HTML来“拆分”唯一的标题和描述对。

    2 回复  |  直到 9 年前
        1
  •  1
  •   DBagBaggerWithSwagger    11 年前

    我从未使用过WebScraper,但它的行为似乎很坏或很奇怪。

    对于这两种情况,以下XPath表达式或多或少都应该有效(需要进行小调整):

    //div//strong/text()
    //div//br/following-sibling::text()
    

    当将这些插入 xmllint (libxml2):

    tmp >xmllint --html --shell a.html
    / > cat /
     -------
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
    <html><body>
    <div>
      <p>
        <strong>TITLE1</strong>
        <br>
        DESCRIPTION1
      </p>
      <p>
        <strong>TITLE2</strong>
        <br>
        DESCRIPTION2
      </p>
      <p>
        <strong>TITLE3</strong>
        <br>
        DESCRIPTION3
      </p>
    </div>
    </body></html>
    
    / > xpath //div//strong/text()
    Object is a Node Set :
    Set contains 3 nodes:
    1  TEXT
        content=TITLE1
    2  TEXT
        content=TITLE2
    3  TEXT
        content=TITLE3
    / > xpath //div//br/following-sibling::text()
    Object is a Node Set :
    Set contains 3 nodes:
    1  TEXT
        content=     DESCRIPTION1
    2  TEXT
        content=     DESCRIPTION2
    3  TEXT
        content=     DESCRIPTION3
    
    / > load b.html
    / > cat /
     -------
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
    <html><body><div>
        <p>
        <strong>TITLE1</strong>
        <br>
        DESCRIPTION1
        <strong>TITLE2</strong>
        <br>
        DESCRIPTION2
        <strong>TITLE3</strong>
        <br>
        DESCRIPTION3
        </p>
    </div></body></html>
    
    / > xpath //div//strong/text()
    Object is a Node Set :
    Set contains 3 nodes:
    1  TEXT
        content=TITLE1
    2  TEXT
        content=TITLE2
    3  TEXT
        content=TITLE3
    / > xpath //div//br/following-sibling::text()
    Object is a Node Set :
    Set contains 5 nodes:
    1  TEXT
        content=  DESCRIPTION1
    2  TEXT
        content=
    3  TEXT
        content=  DESCRIPTION2
    4  TEXT
        content=
    5  TEXT
        content=  DESCRIPTION3
    

    当你将这些的不同版本插入WebScraper时,它们就不起作用了。

     process '//div', 'test[]' => scraper {
        process '//strong', 'name' => 'TEXT';
        process '//br/following-sibling::text()', 'desc' => 'TEXT';
      };
    

    结果如下:

    /tmp >for f in a b; do perl bs.pl file:///tmp/$f.html; done
    { test => [{ desc => " DESCRIPTION1 ", name => "TITLE1" }] }
    { test => [{ desc => " DESCRIPTION1 ", name => "TITLE1" }] }
    

    process '//div', 'test[]' => scraper {
      process '//div//strong', 'name' => 'TEXT';
      process '//div//br/following-sibling::text()', 'desc' => 'TEXT';
    };
    

    结果如下:

    /tmp>对于a b中的f;做perl-bs。平面图file:///tmp/f.美元。html;完成
    {test=>[{desc=>“DESCRIPTION1”,name=>“TITLE1”}]}
    {test=>[{desc=>“DESCRIPTION1”,name=>“TITLE1”}]}
    

    即使是最基本的情况:

      process 'div', 'test[]' => scraper {
        process 'strong', 'name' => 'TEXT';
      };
    

    结果如下:

    /tmp >for f in a b; do perl bs.pl file:///tmp/$f.html; done
    { test => [{ name => "TITLE1" }] }
    { test => [{ name => "TITLE1" }] }
    

    即使您告诉它使用libxml2 use Web::Scraper::LibXML -什么都没有!

    为了确保我不会发疯,我使用了Ruby的Nokogiri:

     /tmp >for f in a b; do ruby -rnokogiri -rpp -e'pp Nokogiri::HTML(File.read(ARGV[0])).css("div p strong").map &:text' $f.html; done
    ["TITLE1", "TITLE2", "TITLE3"]
    ["TITLE1", "TITLE2", "TITLE3"]
    

    什么是 丢失的

        2
  •  0
  •   user1768233    11 年前

    我想我解决了。我不确定这是否是最好的方法,但它似乎能处理这两种情况。

             my $test = scraper {
             process '//div', 'test' => scraper {
                process '//div//strong//text()', 'name[]' => 'TEXT';
                process '//p/text()','desc[]' => ['TEXT', sub { s/^\s+|\s+$//g} ];
    
             }
          };
    
    
    
        my $res = $test->scrape(\$html);
    
        #get the names and descriptions 
        my @keys = @{$res->{test}->{name}};
        my @values = @{$res->{test}->{desc}};
    
        #merge two arrays into hash
        my %hash;   
        @hash{@keys} = @values;