парсинг таблицы на PHP

12 3
V
На сайте с 22.09.2006
Offline
103
7804

Задача: спарсить данные таблиц с http://finance.yahoo.com/q/op?s=GDX

Нужны 2 массива, Calls и Puts

Завис на разборе таблицы, такое ощущение что концы строк в haystack прерывают работу preg_match_all - может быть такое?

попытка удаления концов строк не имеет результата:

$rep = array("\r\n", "\n", "\r");

echo str_replace($rep,'',$haystack);

тут-то что не так? может конфликт кодировок скрипта/данных влиять?

ловить на пробу пытаюсь выражением

preg_match_all('/\<tr .\>([\d\]+[\.]?[\d]?).\<.\<\/tr\>/', $haystack, $matches);

Если кто предложит готовое решение, с меня пиво!

.
siv1987
На сайте с 02.04.2009
Offline
427
#1

Может быть. Точка не соответствует переводам строк без модификатора шаблона s. Кроме того, что-то никаких квантификаторов у вас не видно, что, здесь только один символ "\<.\<\/tr\>"? И в квадратные скобки точка это обычные символ точки.

J
На сайте с 20.02.2014
Offline
120
jkm
#2

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


$str = file_get_contents('http://finance.yahoo.com/q/op?s=GDX');

preg_match('#<table[^>]+>\s*<caption>\s*Calls.+?</thead>(.+?)</table>#s', $str, $match);

preg_match_all('#<td[^>]*>(.*?)</td>#s', $match[1], $matches);

$cells = array_map('strip_tags', $matches[1]);
$cells = array_map('trim', $cells);

$result = array_chunk($cells, 10);

print_r($result);
S7
На сайте с 15.08.2010
Offline
79
#3
vantala:
готовое решение


$str = file_get_contents('http://finance.yahoo.com/q/op?s=GDX');

$trName = array(); // массив имен столбцов таблиц
$haystack = array(); // массив данных таблиц

if (preg_match_all("~<table[^>]*>\s*<caption>\s*([a-z]+)\s*</caption>(.+?)<tbody>(.+?)</tbody>.*?</table>~is", $str, $matches)) {

/* найдены таблицы */
foreach ($matches[3] as $k => $table) {

// имена столбцов таблиц (если нужно)
if (preg_match_all("~(<div class=['\"]D-ib[^'\"]*['\"]>([^<]*)</div>|<th[^>]*>([a-z\s]+)</th>)~is", $matches[2][$k], $th)) {
foreach ($th[2] as $k2 => $name) {
$thName[$matches[1][$k]][$k2] = $name ? $name : $th[3][$k2];
}
} // если не нужно, блок IF удалить/закомментировать

/* разбор таблиц */
if (preg_match_all("~<tr[^>]*>(.+?)</tr>~is", $table, $tr)) {
foreach ($tr[1] as $k2 => $td) {
if (preg_match_all("~<td[^>]*>.+?>([a-z0-9\.%]+)</[^>]+>.+?</td>~is", $td, $data)) {
// добавляем разобранную в массив строку в общий массив данных
$haystack[$matches[1][$k]][$k2] = $data[1];
}
}
}
}
}
/* результат; время выполнения без file_get_contents = 0.00600 sec */
print_r($haystack);

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

на выходе многомерный массив таблиц-данных вида:


[Calls] => Array
(
[0] => Array
(
[0] => 8.00
[1] => GDX150320C00008000
[2] => 14.50
[3] => 0.00
[4] => 0.00
[5] => 0.00
[6] => 0.00%
[7] => 10
[8] => 5
[9] => 0.00%
)
...
[38] => Array
(
[0] => 38.00
[1] => GDX150320C00038000
[2] => 0.13
[3] => 0.00
[4] => 0.13
[5] => 0.00
[6] => 0.00%
[7] => 3
[8] => 161
[9] => 306.25%
)
)
[Puts] => Array
(
[0] => Array
(
[0] => 10.00
[1] => GDX150320P00010000
[2] => 0.01
[3] => 0.00
[4] => 0.00
[5] => 0.00
[6] => 0.00%
[7] => 37
[8] => 4
[9] => 50.00%
)
...
[38] => Array
(
[0] => 38.00
[1] => GDX150320P00038000
[2] => 20.10
[3] => 0.00
[4] => 0.00
[5] => 0.00
[6] => 0.00%
[7] => 12
[8] => 0
[9] => 0.00%
)
)
)

Имена столбцов таблиц в переменной $thName = Array
(
[Calls] => Array
(
[0] => Strike
[1] => Contract Name
[2] => Last
[3] => Bid
[4] => Ask
[5] => Change
[6] => %Change
[7] => Volume
[8] => Open Interest
[9] => Implied Volatility
)

[Puts] => Array
(
[0] => Strike
[1] => Contract Name
[2] => Last
[3] => Bid
[4] => Ask
[5] => Change
[6] => %Change
[7] => Volume
[8] => Open Interest
[9] => Implied Volatility
)

)
vantala:
Нужны 2 массива, Calls и Puts

Берем данные нужной таблиц как $haystack['Calls'], $haystack['Puts']

или 2 массива

$Calls = $haystack['Calls'];

$Puts= $haystack['Puts'];

vantala, обращайтесь ели нужно.. рыба к пиву есть..

V
На сайте с 22.09.2006
Offline
103
#4
senks777:
vantala, обращайтесь ели нужно.. рыба к пиву есть..

Спасибо, это идеальный вариант, похоже... номер WMR киньте в личку 🍻

---------- Добавлено 16.03.2015 в 15:02 ----------

jkm:
Могу предложить такой вариант разбора таблицы. Собираем содержимое всех ячеек. Полученный одномерный массив делим на части по количеству столбцов в таблице.

Интересное решение, возьму на вооружение, спасибо! Номер WMR киньте в личку 🍻

W
На сайте с 09.04.2013
Offline
46
#5

Предлагаю свое решение:

<?php

require_once "simple_html_dom.php";
$html = file_get_html('http://finance.yahoo.com/q/op?s=GDX');

$Calls=array();
$Puts=array();

foreach($html->find('#optionsCallsTable tr[data-row]') as $el){

list($Strike,
$ContractName,
$Last,
$Bid,
$Ask,
$Change,
$ChangePercent,
$Volume,
$OpenInterest,
$ImpliedVolatility)=preg_split('/[\s,]+/', trim($el->plaintext));

array_push($Calls,array("Strike" => $Strike,
"ContractName" => $ContractName,
"Last" => $Last,
"Bid" => $Bid,
"Ask" => $Ask,
"Change" => $Change,
"ChangePercent" => $ChangePercent,
"Volume" => $Volume,
"OpenInterest" => $OpenInterest,
"ImpliedVolatility" => $ImpliedVolatility
));

}

foreach($html->find('#optionsPutsTable tr[data-row]') as $el){

list($Strike,
$ContractName,
$Last,
$Bid,
$Ask,
$Change,
$ChangePercent,
$Volume,
$OpenInterest,
$ImpliedVolatility)=preg_split('/[\s,]+/', trim($el->plaintext));

array_push($Puts,array("Strike" => $Strike,
"ContractName" => $ContractName,
"Last" => $Last,
"Bid" => $Bid,
"Ask" => $Ask,
"Change" => $Change,
"ChangePercent" => $ChangePercent,
"Volume" => $Volume,
"OpenInterest" => $OpenInterest,
"ImpliedVolatility" => $ImpliedVolatility
));

}

print_r($Calls);
print_r($Puts);

Пример вывода:

Array

(
[0] => Array
(
[Strike] => 8.00
[ContractName] => GDX150320C00008000
[Last] => 14.50
[Bid] => 9.65
[Ask] => 10.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 10
[OpenInterest] => 5
[ImpliedVolatility] => 348.44%
)

[1] => Array
(
[Strike] => 11.00
[ContractName] => GDX150320C00011000
[Last] => 8.05
[Bid] => 6.50
[Ask] => 7.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 1
[OpenInterest] => 1
[ImpliedVolatility] => 225.00%
)

[2] => Array
(
[Strike] => 12.00
[ContractName] => GDX150320C00012000
[Last] => 8.20
[Bid] => 5.65
[Ask] => 6.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 1
[OpenInterest] => 11
[ImpliedVolatility] => 190.63%
)

[3] => Array
(
[Strike] => 13.00
[ContractName] => GDX150320C00013000
[Last] => 4.53
[Bid] => 4.70
[Ask] => 5.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 3
[OpenInterest] => 160
[ImpliedVolatility] => 157.81%
)

[4] => Array
(
[Strike] => 14.00
[ContractName] => GDX150320C00014000
[Last] => 3.47
[Bid] => 3.70
[Ask] => 4.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 150
[OpenInterest] => 398
[ImpliedVolatility] => 128.13%
)

[5] => Array
(
[Strike] => 15.00
[ContractName] => GDX150320C00015000
[Last] => 3.07
[Bid] => 2.74
[Ask] => 3.20
[Change] => 0.00
[ChangePercent] => 0.00%
[Volume] => 1
[OpenInterest] => 409
[ImpliedVolatility] => 99.22%
)

Готовое решение выложил на гитхаб

siv1987
На сайте с 02.04.2009
Offline
427
#6

Раз пошла пьянка с решениями, то выложу и я свой вариант


$html = file_get_contents('http://finance.yahoo.com/q/op?s=GDX');
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);

$table = $doc->getElementsByTagName('table');
$result = array();

foreach(array(1,2) as $n){
$tab = $table->item($n);
$ths = $tab->getElementsByTagName('thead')->item(0);
$ths = $ths->getElementsByTagName('th');

$type = $tab->getElementsByTagName('caption')->item(0)->textContent;
$type = trim($type);
$cell = array();

foreach($ths as $th){
$attr = $th->getAttribute('class');
$column = preg_match('/column-(\S+)/', $attr, $m) ? $m[1] : '';
$cell[] = $column;
}

$tbody = $tab->getElementsByTagName('tbody')->item(0);
$trs = $tbody->getElementsByTagName('tr');

foreach($trs as $tr){
$tds = $tr->getElementsByTagName('td');
$row = array();

foreach($tds as $td){
$row[] = trim($td->textContent);
}

$result[ $type ][] = array_combine($cell, $row);
}
}

print_r($result);

Result:


Array
(
[Calls] => Array
(
[0] => Array
(
[strike] => 8.00
[contractName] => GDX150320C00008000
[last] => 14.50
[bid] => 9.65
[ask] => 10.30
[change] => 0.00
[percentChange] => 0.00%
[volume] => 10
[openInterest] => 5
[impliedVolatility] => 453.13%
)

[1] => Array
(
[strike] => 11.00
[contractName] => GDX150320C00011000
[last] => 7.15
[bid] => 6.50
[ask] => 7.30
[change] => 0.00
[percentChange] => 0.00%
[volume] => 1
[openInterest] => 1
[impliedVolatility] => 297.66%
)
....
[Puts] => Array
(
[0] => Array
(
[strike] => 10.00
[contractName] => GDX150320P00010000
[last] => 0.01
[bid] => 0.00
[ask] => 0.02
[change] => 0.00
[percentChange] => 0.00%
[volume] => 37
[openInterest] => 4
[impliedVolatility] => 212.50%
)

[1] => Array
(
[strike] => 11.00
[contractName] => GDX150320P00011000
[last] => 0.01
[bid] => 0.00
[ask] => 0.02
[change] => 0.00
[percentChange] => 0.00%
[volume] => 3
[openInterest] => 0
[impliedVolatility] => 181.25%
)

[2] => Array
(
[strike] => 12.00
[contractName] => GDX150320P00012000
[last] => 0.03
[bid] => 0.00
[ask] => 0.02
[change] => 0.00
[percentChange] => 0.00%
[volume] => 2
[openInterest] => 2
[impliedVolatility] => 153.13%
)
totamon
На сайте с 12.05.2007
Offline
437
#7

да тут настоящий конкурс уже образовался, или курсы по парсингу) запишу в блокнотик решения на всякий случай, спасибо!

Домены и хостинг https://8fn.ru/regru | Дедик от 3000р https://8fn.ru/73 | VPS в Москве https://8fn.ru/72 | Лучшие ВПС, ТП огонь, все страны! https://8fn.ru/inferno | ХОСТИНГ №1 РОССИИ https://8fn.ru/beget
V
На сайте с 22.09.2006
Offline
103
#8

Ладно, конкурс так конкурс...

Первые два варианта по праву первенства уже присоединились к пьянке.

По остальным вариантам будет проведен тест на производительность.

Если предложенное решение быстрее ранее предлагавшихся, так и быть, проставляюсь еще и победителю 🍻

Окончание приема 19 марта 2015 23:59 МСК, оглашение итогов 20 марта.

Не ожидал такого отклика, спасибо всем откликнувшимся!

siv1987
На сайте с 02.04.2009
Offline
427
#9
vantala:
По остальным вариантам будет проведен тест на производительность.

Тест на производительность понятие субъективное. Естественно регулярные выражения в скорости выигрывают перед парсингом DOM модели. За то поддерживать такой код легче, более понятен, чем парсинг на регулярках.

W
На сайте с 09.04.2013
Offline
46
#10
siv1987:
Тест на производительность понятие субъективное.

Полностью согласет с siv1987 сам делал упор на читабельность кода.Как насчет пригласить арбитра-программиста? Не только скорость ,но и логика ☝ Может SeVlad? Сам я болею за senks777 ,если он оформит свое решение в более читабельный вид.Думаю многим будет полезно.

12 3

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий