File Coverage

blib/lib/NewsExtractor/GenericExtractor.pm
Criterion Covered Total %
statement 26 107 24.3
branch 0 84 0.0
condition 0 12 0.0
subroutine 9 13 69.2
pod 0 3 0.0
total 35 219 15.9


line stmt bran cond sub pod time code
1             use v5.18;
2 1     1   12 use utf8;
  1         3  
3 1     1   5  
  1         1  
  1         6  
4             use Moo;
5 1     1   19 extends 'NewsExtractor::TXExtractor';
  1         1  
  1         4  
6              
7             use HTML::ExtractContent;
8 1     1   690 use Mojo::DOM;
  1         18734  
  1         34  
9 1     1   6 use NewsExtractor::Types qw(is_NewspaperName);
  1         2  
  1         23  
10 1     1   4  
  1         2  
  1         9  
11             use Importer 'NewsExtractor::TextUtil' => qw( u normalize_whitespace parse_dateline_ymdhms );
12 1     1   711 use Importer 'NewsExtractor::Constants' => qw( %RE );
  1         2  
  1         9  
13 1     1   29  
  1         2  
  1         4  
14             with 'NewsExtractor::Role::ContentTextExtractor';
15              
16             no Moo;
17 1     1   50  
  1         2  
  1         8  
18             my ($self) = @_;
19              
20 0     0 0   my ($title, $el);
21             my $dom = $self->dom;
22 0           if ($el = $dom->at("#story #news_title, #news_are .newsin_title, .data_midlle_news_box01 dl td:first-child")) {
23 0           $title = $el->text;
24 0 0         } elsif ($el = $dom->at("meta[property='og:title']")) {
    0          
    0          
    0          
25 0           $title = $el->attr("content");
26             } elsif ($el = $dom->at("meta[name='title']")) {
27 0           $title = $el->attr('content');
28             } elsif ($el = $dom->at("title")) {
29 0           $title = $el->text;
30             } else {
31 0           return;
32             }
33 0           $title .= "";
34              
35 0           if (my $site_name = $self->site_name) {
36             $title =~ s/\s* \p{Punct} \s* $site_name \s* \z//x;
37 0 0         }
38 0           if (defined($title)) {
39             my $delim = qr<(?: \p{Punct} | \| | │ )>x;
40 0 0         $title =~ s/ \s* $delim \s* $RE{newspaper_names} \s* \z//x;
41 0           $title =~ s/\A $RE{newspaper_names} \s* $delim \s* //x;
42 0           $title =~ s/\r\n/\n/g;
43 0           $title =~ s/\A\s+//;
44 0           $title =~ s/\s+\z//;
45 0           }
46 0           return $title && normalize_whitespace($title);
47             }
48 0   0        
49             my ($self) = @_;
50             my $dateline;
51             my $guess;
52 0     0 0    
53 0           my $dom = $self->dom;
54             if ($guess = $dom->at("meta[property='article:modified_time'], meta[property='article:published_time'], meta[itemprop=dateModified][content], meta[itemprop=datePublished][content]")) {
55             $dateline = $guess->attr('content');
56 0           }
57 0 0         elsif ($guess = $dom->at("time[itemprop=datePublished][datetime], h1 time[datetime], .func_time time[pubdate], span.time > time.post-published")) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
58 0           $dateline = $guess->attr('datetime');
59             }
60             elsif ($guess = $dom->at(".reporter time, span.viewtime, header.article-desc time, .timeBox .updatetime span, .caption div.label-date, .contents_page span.date, .main-content span.date, .newsin_date, .news .date, .author .date, ul.info > li.date > span:nth-child(2), #newsHeadline span.datetime, article p.date, .post-meta > .icon-clock > span, .article_info_content span.info_time, .content time.page-date, .c_time, .newsContent p.time, div.title > div.time, div.article-meta div.article-date, address.authorInfor time, .entry-meta .date a, .author-links .posts-date, .top_title span.post_time, .node-inner > .submitted > span")) {
61 0           $dateline = $guess->text;
62             }
63             elsif ($guess = $dom->at("div#articles cite")) {
64 0           $guess->at("a")->remove;
65             $dateline = $guess->text;
66             }
67 0           elsif ($guess = $dom->at("article.ndArticle_leftColumn div.ndArticle_creat, ul.info li.date, .cpInfo .cp, .nsa3 .tt27")) {
68 0           ($dateline) = $guess->text =~ m#([0-9]{4}[\-/][0-9]{2}[\-/][0-9]{2} [0-9]{2}:[0-9]{2})#;
69             }
70             elsif ($guess = $dom->at(".news-toolbar .news-toolbar__cell")) {
71 0           ($dateline) = $guess->text =~ m#([0-9]{4}/[0-9]{2}/[0-9]{2})#;
72             }
73             elsif ($guess = $dom->at(".content .writer span:nth-child(2)")) {
74 0           ($dateline) = $guess->text =~ m#([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2})#;
75             }
76             elsif ($guess = $dom->at("div.content-wrapper-right > div > div > div:nth-child(4)")) {
77 0           ($dateline) = $guess->text =~ m#([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})#;
78             }
79             elsif ($guess = $dom->at("span#ctl00_ContentPlaceHolder1_News_Label, #ctl00_ContentPlaceHolder1_UpdatePanel2 font[color=darkred]")) {
80 0           ($dateline) = $guess->text =~ m#([0-9]{4}/[0-9]{1,2}/[0-9]{1,2})#;
81             }
82             elsif ($guess = $dom->at(".news-info dd.date:nth-child(6)")) {
83 0           ($dateline) = $guess->text =~ m#([0-9]{4}年[0-9]{1,2}月[0-9]{1,2}日[0-9]{2}:[0-9]{2})#;
84             }
85             elsif ($guess = $dom->at("article.entry-content div:nth-child(2)")) {
86 0           ($dateline) = $guess->text =~ m#([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})#;
87             }
88             elsif ($guess = $dom->at("span.submitted-by")) {
89 0           # www.thinkingtaiwan.com
90             my ($month, $day, $year) = $guess->text =~ m/([0-9]+)/g;
91             $dateline = u(
92             sprintf(
93 0           '%04d-%02d-%02dT%02d:%02d:%02d%s',
94 0           $year, $month, $day, 23, 59, 59, '+08:00'
95             )
96             );
97             }
98             elsif ($guess = $dom->at('#story #news_author')) {
99             ($dateline) = $guess->all_text =~ m{\A 【記者.+ 】\s* (.+) \z}x;
100             }
101             elsif ($guess = $dom->at('.data_midlle_news_box01 dl dd ul li:first-child')) {
102 0           ($dateline) = $guess->text;
103             my ($year, $mmdd) = $dateline =~ /\A ([0-9]{3}) - (.+) \z /x;
104             $year += 1911;
105 0           $dateline = $year . '-' . $mmdd;
106 0           }
107 0           elsif ($guess = $dom->at('#details_block .left .date, .article_header > .author > span:last-child')) {
108 0           $dateline = normalize_whitespace $guess->text;
109             }
110             elsif ($guess = $dom->at(
111 0           join(','
112             , '.timebox > .publishtime' # howlife.cna.com.tw
113             , 'div.newsInfo > span.time' # n.yam.com
114             ))) {
115             $dateline = parse_dateline_ymdhms($guess->all_text, '+08:00');
116             }
117              
118 0           if ($dateline) {
119             $dateline = normalize_whitespace($dateline);
120              
121 0 0         $dateline =~ s<\A ([0-9]{4}) (\p{Punct}) ([0-9]{1,2}) \2 ([0-9]{1,2}) \z>< sprintf('%04d-%02d-%02d', $1, $3, $4) >ex;
122 0            
123             if ($dateline =~ /^([0-9]{4})[^0-9]/) {
124 0           if ($1 > ((localtime)[5] + 1900)) {
  0            
125             $dateline = undef;
126 0 0         }
127 0 0         }
128 0           }
129              
130             return $dateline;
131             }
132              
133 0           my ($self) = @_;
134              
135             my $dom = $self->dom;
136             my ($ret, $guess);
137 0     0 0    
138             if ( $guess = $dom->at('meta[property="og:article:author"]') ) {
139 0           $ret = $guess->attr('content');
140 0           } elsif ( $guess = $dom->at('meta[name="author"]') ) {
141             $ret = $guess->attr('content');
142 0 0         } elsif ( $guess = $dom->at('.bt_xmic span[itemprop=author], div.tdb_single_author a.tdb-author-name, div.field-item a[href^=/author/], div.content_reporter a[itemprop=author], span[itemprop=author] a, div.author div.intro a div.name, div.article-author > h5 > a, div.article-meta > div.article-author > a, div.authorInfo li.authorName > a, .article .writer > p, .info_author, .news-info dd[itemprop=author], .content_reporter a, .top_title span.reporter_name, .post-heading time span, header .article-meta .article-author, .article_header > .author > span:first-child, .mid-news > .m-left-side > .maintype-wapper > .subtype-sort, .newsCon > .newsInfo > span:first-child, .newsdetail_content > .title > h4 > a[href^="/news/searchresult/news?search_text="], .m-from-author > .m-from-author__name, .post-author-name a[itemprop*=author], a.post-author-avatar .post-author-name > b, div#news_content div.author') ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
143 0           $ret = $guess->text;
144             } elsif ($guess = $dom->at('div#yt_container_placeholder + p')) {
145 0           ($ret) = $guess->text =~ m{\A \s* (.+) \s+ 報導 \s+ / }x;
146             } elsif ($guess = $dom->at('h4.font_color5')) {
147 0           ($ret) = $guess->all_text =~ m{\A \s* 編輯 \s* (.+) \s+ 報導 }x;
148             } elsif ($guess = $dom->at('#story #news_author')) {
149 0           ($ret) = $guess->all_text =~ m{\A 【 (記者 .+) 】}x;
150             } elsif ($guess = $dom->at('#details_block .left .name, .articleMain .article-author a.author-title, .article__credit a[href^="/author/"], span[itemprop=author] span[itemprop=name], .post-header-additional .post-meta-info a.nickname')) {
151 0           $ret = $guess->text;
152             } elsif ($guess = $dom->at('div.single-post-meta a[rel="author"]')) {
153 0           ($ret) = $guess->text =~ m<^工商時報 (.+)\z>x;
154             } elsif ($guess = $dom->at('#PostContent .head-section-content p.meta')) {
155 0           ($ret) = $guess->text =~ m<(記者.+?報導)>x;
156             }
157 0            
158             $ret = undef if ($ret && is_NewspaperName($ret));
159 0            
160             if ( !$ret && (my $content_text = $self->content_text)) {
161             my @patterns = (
162 0 0 0       qr<\b (?:特[約派])? [记記]者 \s* ([\s\p{Letter}、]+?) \s* [/╱/] \s* (?: 特稿 | 專訪 | \p{Letter}+ (?:報導|报导)) \b>xs,
163             qr<\A 【(記者.+?報導)】>x,
164 0 0 0       qr<\A 中評社 .+? \d+ 月 \d+ 日電(記者(.+?))>x,
165 0           qr<\A ( 記者[^/]+/.+?電 )>x,
166             qr<\A 匯流新聞網記者 (\p{Letter}+) /(?:\p{Letter}+)報導 >x,
167             qr<\A 匯流新聞網記者\s*/\s*(\p{Letter}+)綜合報導>x,
168             qr<((中央社[记記]者 \S+ 日 專?[電电] | 大纪元记者\p{Letter}+报导 | 記者.+?報導/.+?))>x,
169             qr< \( ( \p{Letter}+ / \p{Letter}+ 報導 ) \) >x,
170             qr<\A 文:記者(\p{Letter}+) \n>x,
171             qr<( (譯者:.+?/核稿:.+?) )[0-9]+(?:\n|\z)>x,
172             qr< \(記者 (.+?) \) \z >x,
173             qr<^(編譯[^/]+?/.+?報導)$>xsm,
174             qr<(( (?:譯者|編輯):.+) ) (?:[0-9]{7})? \z >x,
175             qr<(記者 (\p{Letter}+) ) \z>x,
176             qr< (記者 (\p{Letter}+) 綜合報導)\s+ ( (責任編輯:\p{Letter}+) ) \z>x,
177             qr< ( (責任編輯:\p{Letter}+) )\z>x,
178             qr< \s (公民記者 .+ 採訪報導) \z>x,
179             qr<\A 【大成報記者 (\p{Letter}+) / .+報導】 >x,
180             qr<\A 記者 (\p{Letter}+) /報導 >x,
181             qr<\A \[ (記者.+報導) \] >x,
182             qr<\A ( (記者.+報導) ) >x,
183             qr<\A 【(本報記者.+報導)】 >x,
184             qr<\b ﹝記者(\p{Letter}+?)/.+?報導﹞ \b>x,
185             qr<\A〔新網記者 ( \p{Letter}+ (?:報導|特稿))〕\b>x,
186             qr<\A(芋傳媒記者(\p{Letter}+)報導)\b>x,
187             qr<\b文\s*/\s*(\p{Letter}+)\s*(中央社編譯)\n>x,
188             qr<\A 香港中通社[0-9]+月[0-9]+日電(記者\s(\p{Letter}+))>x,
189             qr<\A \( (記者\p{Letter}+報導) \)>x,
190             );
191              
192             for my $pat (@patterns) {
193             ($ret) = $content_text =~ m/$pat/;
194             last if $ret;
195             }
196 0            
197 0           unless ($ret) {
198 0 0         my ($guess) = $content_text =~ m{((\p{Letter}+))\z}xsm;
199             if ($guess && $dom->descendant_nodes->first(sub { $_->type eq 'text' && $_->content =~ m<記者${guess}\b> })) {
200             $ret = $guess
201 0 0         }
202 0           }
203 0 0 0 0     }
  0 0          
204 0            
205             if ($ret) {
206             $ret = normalize_whitespace($ret);
207             $ret = "" if is_NewspaperName($ret);
208             }
209 0 0          
210 0           return $ret;
211 0 0         }
212              
213             1;