File Coverage

blib/lib/Finance/Quote/Bourso.pm
Criterion Covered Total %
statement 29 82 35.3
branch 0 22 0.0
condition n/a
subroutine 11 13 84.6
pod 0 4 0.0
total 40 121 33.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3             # Copyright (C) 1998, Dj Padzensky <djpadz@padz.net>
4             # Copyright (C) 1998, 1999 Linas Vepstas <linas@linas.org>
5             # Copyright (C) 2000, Yannick LE NY <y-le-ny@ifrance.com>
6             # Copyright (C) 2000, Paul Fenwick <pjf@cpan.org>
7             # Copyright (C) 2000, Brent Neal <brentn@users.sourceforge.net>
8             # Copyright (C) 2001, Rob Sessink <rob_ses@users.sourceforge.net>
9             # Copyright (C) 2005, Morten Cools <morten@cools.no>
10             # Copyright (C) 2006, Dominique Corbex <domcox@sourceforge.net>
11             # Copyright (C) 2008, Bernard Fuentes <bernard.fuentes@gmail.com>
12             # Copyright (C) 2009, Erik Colson <eco@ecocode.net>
13             # Copyright (C) 2018, Jean-Marie Pacquet <jmpacquet@sourceforge.net>
14             #
15             # This program is free software; you can redistribute it and/or modify
16             # it under the terms of the GNU General Public License as published by
17             # the Free Software Foundation; either version 2 of the License, or
18             # (at your option) any later version.
19             #
20             # This program is distributed in the hope that it will be useful,
21             # but WITHOUT ANY WARRANTY; without even the implied warranty of
22             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23             # GNU General Public License for more details.
24             #
25             # You should have received a copy of the GNU General Public License
26             # along with this program; if not, write to the Free Software
27             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28             # 02110-1301, USA
29             #
30             #
31             # This code derived from Padzensky's work on package Finance::YahooQuote,
32             # but extends its capabilites to encompas a greater number of data sources.
33             #
34             #
35             # Changelog
36             #
37             # 2018-04-08 Jean-Marie Pacquet
38             #
39             # * (1.49) Major site change (html 5)
40             #
41             # 2014-01-12 Arnaud Gardelein
42             #
43             # * changes on website
44             #
45             # 2009-04-12 Erik Colson
46             #
47             # * Major site change.
48             #
49             # 2008-11-09 Bernard Fuentes
50             #
51             # * changes on website
52             #
53             # 2006-12-26 Dominique Corbex <domcox@sourceforge.net>
54             #
55             # * (1.4) changes on web site
56             #
57             # 2006-09-02 Dominique Corbex <domcox@sourceforge.net>
58             #
59             # * (1.3) changes on web site
60             #
61             # 2006-06-28 Dominique Corbex <domcox@sourceforge.net>
62             #
63             # * (1.2) changes on web site
64             #
65             # 2006-02-22 Dominique Corbex <domcox@sourceforge.net>
66             #
67             # * (1.0) iniial release
68             #
69              
70             require 5.005;
71              
72 5     5   3245 use strict;
  5         14  
  5         282  
73              
74             package Finance::Quote::Bourso;
75              
76 5     5   36 use vars qw( $Bourso_URL );
  5         13  
  5         250  
77              
78 5     5   30 use constant DEBUG => $ENV{DEBUG};
  5         19  
  5         341  
79 5     5   33 use if DEBUG, 'Smart::Comments';
  5         13  
  5         58  
80              
81 5     5   181 use HTTP::Request::Common;
  5         9  
  5         322  
82 5     5   30 use HTML::TreeBuilder;
  5         10  
  5         30  
83 5     5   131 use Encode qw(decode);
  5         15  
  5         205  
84 5     5   30 use JSON qw( decode_json );
  5         10  
  5         44  
85 5     5   569 use utf8;
  5         10  
  5         35  
86              
87             our $VERSION = '1.58_01'; # TRIAL VERSION
88              
89             my $Bourso_URL = 'https://www.boursorama.com/cours/';
90              
91             sub methods {
92             return (
93 5     5 0 27 europe => \&bourso,
94             france => \&bourso,
95             bourso => \&bourso
96             );
97             }
98              
99             {
100             my @labels =
101             qw/name last date isodate p_change open high low close volume currency method exchange/;
102              
103             sub labels {
104             return (
105 5     5 0 19 europe => \@labels,
106             france => \@labels,
107             bourso => \@labels
108             );
109             }
110             }
111              
112             sub bourso_to_number {
113 0     0 0   my $x = shift(@_);
114 0           $x =~ s/\s//g; # remove spaces etc in number
115 0           return $x;
116             }
117              
118             sub bourso {
119 0     0 0   my $quoter = shift;
120 0           my @stocks = @_;
121 0           my $ua = $quoter->user_agent();
122 0           my %info;
123              
124             ### UA max_redirect: $ua->max_redirect
125              
126 0           foreach my $stock (@stocks) {
127 0           eval {
128 0           my $query = $Bourso_URL . $stock;
129 0           my $reply = $ua->request(GET $query);
130              
131             ### Search: $query, $reply->code
132              
133 0           my $body = decode('UTF-8', $reply->content);
134 0           my $root = HTML::TreeBuilder->new_from_content($body);
135              
136 0           my $div = $root->look_down(_tag => 'div',
137             class => qr/^c-faceplate/);
138              
139 0           my $name = $div->look_down(_tag => 'a', class => qr/^c-faceplate__company-link/)->as_text();
140 0           $name =~ s/^\s+|\s+$//g;
141 0           utf8::encode($name);
142              
143 0           my $currency = $div->look_down(_tag => 'span', class => qr/^c-faceplate__price-currency/)->as_text();
144 0           $currency =~ s/^\s+|\s+$//g;
145              
146 0           my ($date, $last, $symbol, $low, $high, $close, $exchange, $volume, $net);
147              
148 0 0         if ($div->attr('data-ist-init')) {
149 0           my $json = JSON::decode_json($div->attr('data-ist-init'));
150 0           $date = $json->{'tradeDate'};
151 0           $last = $json->{'last'};
152 0           $symbol = $json->{'symbol'};
153 0           $low = $json->{'low'};
154 0           $high = $json->{'high'};
155 0           $close = $json->{'previousClose'};
156 0           $exchange = $json->{'exchangeCode'};
157 0           $volume = $json->{'totalVolume'};
158 0           $net = $json->{'variation'};
159             }
160             else {
161             # date captures more than the date, but the regular expression below extracts just the date
162 0           $date = $div->look_down(_tag => 'div', class => qr/^c-faceplate__real-time/)->as_text();
163 0           $last = $div->look_down(_tag => 'span', class => qr/^c-instrument c-instrument--last/)->as_text();
164 0           $symbol = $stock;
165             }
166              
167 0           $info{$stock, 'symbol'} = $symbol;
168 0           $info{$stock, 'name'} = $name;
169 0           $info{$stock, 'currency'} = $currency;
170 0           ($info{$stock, 'last'} = $last) =~ s/[^0-9.]//g;
171 0 0         ($info{$stock, 'high'} = $high) =~ s/[^0-9.]//g if $high;
172 0 0         ($info{$stock, 'low'} = $low) =~ s/[^0-9.]//g if $low;
173 0 0         ($info{$stock, 'close'} = $close) =~ s/[^0-9.]//g if $close;
174 0 0         $info{$stock, 'exchange'} = $exchange if $exchange;
175 0 0         ($info{$stock, 'volume'} = $volume) =~ s/[^0-9.]//g if $volume;
176 0 0         ($info{$stock, 'net'} = $net) =~ s/[^0-9.]//g if $net;
177              
178             # 2020-07-17 17:03:45
179 0 0         $quoter->store_date(\%info, $stock, {isodate => $1}) if $date =~ m|([0-9]{4}-[0-9]{2}-[0-9]{2})|;
180              
181             # dd/mm/yyyy
182 0 0         $quoter->store_date(\%info, $stock, {eurodate => $1}) if $date =~ m|([0-9]{2}/[0-9]{2}/[0-9]{4})|;
183              
184 0           $info{$stock, 'method' } = 'bourso';
185 0           $info{$stock, 'success'} = 1;
186             };
187 0 0         if ($@) {
188 0           $info{$stock, 'success'} = 0;
189 0           $info{$stock, 'errormsg'} = 'Failed to retrieve quote';
190             }
191             }
192              
193 0 0         return wantarray() ? %info : \%info;
194 0           return \%info;
195             }
196              
197             1;
198              
199             =head1 NAME
200              
201             Finance::Quote::Bourso - Obtain quotes from Boursorama.
202              
203             =head1 SYNOPSIS
204              
205             use Finance::Quote;
206              
207             $q = Finance::Quote->new;
208              
209             %info = Finance::Quote->fetch("bourso","ml"); # Only query Bourso
210              
211             =head1 DESCRIPTION
212              
213             This module fetches information from the "Paris Stock Exchange",
214             https://www.boursorama.com. All stocks are available.
215              
216             This module is loaded by default on a Finance::Quote object. It's
217             also possible to load it explicitly by placing "bourso" in the argument
218             list to Finance::Quote->new().
219              
220             Information obtained by this module may be covered by www.boursorama.com
221             terms and conditions See https://www.boursorama.com/ for details.
222              
223             =head1 LABELS RETURNED
224              
225             The following labels will be returned by Finance::Quote::Bourso : name, last,
226             symbol, date, isodate, method, currency. For some symbols, additional
227             information is available: exchange, high, low, close, net, volume.
228              
229             =head1 SEE ALSO
230              
231             Boursorama (french web site), https://www.boursorama.com
232              
233             =cut