Perl:为什么这个web scraper正则表达式工作不一致?

时间:2012-02-08 12:34:08

标签: regex perl web-scraping lwp html-tree

我遇到了与我想要抓的网站有关的另一个问题。

基本上我从页面内容中剥离了大部分我不想要的内容,并感谢here已经设法隔离我想要的日期的一些帮助。尽管一些初始问题与不间断的空间相匹配,但大多数似乎都工作正常。但是,我现在对最终正则表达式有困难,最终正则表达式旨在将每行数据拆分为字段。每一行代表股价指数的价格。每行的字段是:

  1. 由拉丁字母表中的字符组成的任意长度的名称,有时是逗号或符号,没有数字。
  2. 小数点后两位数的数字(索引的绝对值)。
  3. 小数点后两位数的数字(值的变化)。
  4. 小数点后有两位数的数字,后跟百分号(值的百分比变化)。
  5. 以下是拆分前的示例字符串: “渔业,农业和林业243.45-1.91-0.78%采矿360,74-4.15-1.14%建筑465.36-1.01-0.22%食品783.2511.281.46%纺织品和服装412.070.540.13%纸浆和纸张333。 31-0.29-0.09%化学品729.406.010.83%“

    我用来分割这一行的正则表达式是:

    $mystr =~ s/\n(.*?)(\d{1,4}\.\d{2})(\-?\d{1,3}\.\d{2})(.*?%)\n/\n$1 == $2 == $3 == $4\n/ig;

    它有时有效,但有时无效,我无法弄清楚为什么会这样。 (下面示例输出中的加倍等号用于使字段拆分更容易看到。)

    Fishery, Agriculture & Forestry == 243.45 == -1.91 == -0.78%
    Mining360.74-4.15-1.14%
    Construction == 465.36 == -1.01 == -0.22%
    Foods783.2511.281.46%
    

    我认为对于那些看到指数价格出现负面变化的指数来说,减号是一个问题,但有时尽管有减号,它仍有效。

    Q值。为什么下面显示的最终正则表达式无法一致地分割字段?

    示例代码如下。

    #!/usr/bin/perl -w
    use strict;
    use LWP::Simple;
    use HTML::Tree;
    
    my $url_full = "http://www.tse.or.jp/english/market/STATISTICS/e06_past.html";
    
    my $content = get($url_full);
    # get dates:
    (my @dates) = $content =~ /(?<=dateFormat\(')\d{4}\/\d{2}\/\d{2}(?='\))/g;
    foreach my $date (@dates) { # convert to yyyy-mm-dd
        $date =~ s/\//-/ig;
    }
    my $tree = HTML::Tree->new();
    $tree->parse($content);
    my $mystr = $tree->as_text;
    
    $mystr =~ s/\xA0//gi; # remove non-breaking spaces
    # remove first chunk of text:
    $mystr =~
      s/^(TSE.*?)IndustryIndexChange ?/IndustryIndexChange\n$dates[0]\n\n/gi;
    $mystr =~ s/IndustryIndexChange ?/IndustryIndexChange/ig;
    $mystr =~ s/IndustryIndexChange/Industry Index Change\n/ig;
    $mystr =~ s/% /%\n/gi; # percent symbol is market for end of line
    # indicate breaks between days:
    $mystr =~ s/Stock.*?IndustryIndexChange/\nDAY DELIMITER\n/gi;
    $mystr =~ s/Exemption from Liability.*$//g; # remove boilerplate at bottom
    
    # and here's the problem regex...
    # try to split it:
    $mystr =~
      s/\n(.*?)(\d{1,4}\.\d{2})(\-?\d{1,3}\.\d{2})(.*?%)\n/\n$1 == $2 == $3 == $4\n/ig;
    
    print $mystr;
    

3 个答案:

答案 0 :(得分:2)

它似乎正在做其他每一个。

我的猜测是,您的记录之间只有一个\n,但您的模式以\n开头和结尾。因此,第一场比赛的最终\n将消耗第二场比赛找到第二场比赛所需的\n。最终结果是,它获得了所有其他记录。

最好不要在^$(而不是\n\n)中包装您的模式,并使用m标记。 s///

答案 1 :(得分:2)

问题是你在正则表达式的开头和结尾都有\n

考虑这样的事情:

$s = 'abababa';
$s =~ s/aba/axa/g;

$s设置为axabaxa axaxaxa,因为aba只发生了两次非重叠事件。

答案 2 :(得分:0)

我的解释(伪代码) -

one   = [a-zA-Z,& ]+
two   = \d{1,4}.\d\d
three = <<two>>
four  = <<two>>%

regex = (<<one>>)(<<two>>)(<<three>>)(<<four>>)
      = ([a-zA-Z,& ]+)(\d{1,4}.\d\d)(\d{1,4}.\d\d)(\d{1,4}.\d\d%)

但是,您已经以HTML的形式呈现了“结构化”数据。为什么不利用这个呢?

HTML parsing in perl引用了MOJO 对于perl中基于DOM的解析,除非有严重的性能原因, 我强烈推荐这种方法。