I have two foreach blocks to get some values from a web page (I'm scraping values from an HTML table).
<?php
include('../simple_html_dom.php');
$html = file_get_html('http://www.betexplorer.com/soccer/belgium/jupiler-league/results/');
foreach($html->find('td') as $e) {
echo $e->innertext . '<br>';
}
foreach( $html->find('td[data-odd]') as $td ) {
echo $td->attr['data-odd'].PHP_EOL;
}
?>
and this is my HTML code:
<tr class="strong">
<td class="first-cell tl">
Waasland-Beveren - Anderlecht
</td>
<td class="result">
1:0
</td>
<td class="odds best-betrate" data-odd="5.97"></td>
<td class="odds" data-odd="4.21"></td>
<td class="odds" data-odd="1.51"></td>
<td class="last-cell nobr date">21.02.2016</td>
</tr>
<tr class="">
<td class="first-cell tl">
Waregem - KV Mechelen
</td>
<td class="result">
2:3
</td>
<td class="odds" data-odd="1.83"></td><td class="odds" data-odd="3.71"></td>
<td class="odds best-betrate" data-odd="3.99"></td>
<td class="last-cell nobr date">21.02.2016</td>
</tr>
In this way, in my output, I get before values from the first foreach and, after, values from the second. I'd like to get values together in the right order. For example:
21.02.2016 Waasland-Beveren - Anderlecht 1:0 5.96 4.20 1.51
21.02.2016 Waregem - KV Mechelen 2:3 1.83 3.71 3.98
If they have the exact (count) or (length). Use the normal for after you assign them to two variables.
$td = $html->find('td');
$attr = $html->find('td[data-odd]');
for($i=0; $i < count($td); $i++)
echo $td[$i]->innertext."<br/>".$attr[$i]->attr['data-odd'].PHP_EOL;
Update:
You want to reorder the tds you received from the HTML file, that means you have to think of another logic in how to retrieve them. This updated code is very specific to your case:
$match_dates = $html->find("td[class=last-cell nobr date]"); // we have 1 per match
$titles = $html->find("td[class=first-cell tl]"); // 1 per match
$results = $html->find("td[class=result]"); // 1
$best_bets = $html->find("td[class=odds best-betrate]"); // 1
$odds = $html->find("td[class=odds]"); // 2
// Now to output everything in whatever order you want:
$c=0; $b=0; // two counters
foreach($titles as $match)
echo $match_dates[$c]->innertext." - ".$match->innertext." [".$results[$c]->innertext."] - Best bet rate: ".$best_bets[$c++]->attr['data-odd']." - odds: ".$odds[$b++]->attr['data-odd'].", ".$odds[$b++]->attr['data-odd']."<br/>";
Related
I have a site with a CSV upload, which pushes everything in the CSV to a temp table and then splits into smaller tables.
Currently, I have a display page that displays all of that info in HTML tables. Some sections, however, need to have a formulaic representation. In other words, as seen in the screenshot below, meter volume is divided by tester volume and the sum of that is multiplied by the tester accuracy. That number is divided by 100 to give the corrected accuracy.
Here is the code I have but it's not loading my web page and I think the PHP might be wrong in establishing the variables:
<table style="width:100%; border:none;
border-collapse:collapse;">
<? php
$test1FormA = $row['test1MeterVol'] / $row['test1TesterVol'];
$test1FormB = $test1FormA * $row['test1Accuracy'];
$test1FinalForm = $test1FormB / 100;
?>
<tr>
<td style="border:none; text-align: left;">Meter Volume: </td>
<td><? echo $row['test1MeterVol'];?> </td>
</tr>
<tr>
<td style="border:none; text-align: left;">Tester Volume: </td>
<td><? echo $row['test1TesterVol'];?> </td>
</tr>
<tr>
<td style="border:none; text-align: left;">Tester Accuracy: </td>
<td><? echo $row['test1Accuracy'];?> </td>
</tr>
<tr>
<td style="border:none; text-align: left;">Corrected Accuracy: </td>
<td><? echo $test1FinalForm;?> </td>
</tr>
</table>
There is currently no corrected accuracy in the database, so I wanted to find either a way to do it per line, like above, or a way to do that upon every CSV upload. There are 8 tests, so it would have to do that formula for 8 different fields per 5 records in the CSV upon upload. I currently have the CSV uploading into the temp table with a large insert statement.
Is there a way to just do this in the table row with PHP?
UPDATE: Screenshot of new results
UPDATE: Code for averaging issue
<?php
$sum=0;
for($i=1; $i<=8; $i++){
$testFormA = $row["test".$i."MeterVol"] / $row["test".$i."TesterVol"];
$testFormB = $testFormA * $row["test".$i."Accuracy"];
$testFinalForm = $testFormB / 100;
$sum += $testFinalForm;
echo"$sum";
?>
<td><?php echo round($testFinalForm,3) ?>%</td>
<?php }
$average = $sum / 8;
echo"$average";
?>
<td><?php echo round($average,3)?> </td>
There are many ways to do what you need. But the most important part is that you will need a loop structure to build your table with a pretty code, without repeating everything over and over.
In your case, you can use the following code to build your whole last row for all 8 tests:
<?php for($i=1; $i<=8; $i++){
$testFormA = $row["test".$i."MeterVol"] / $row["test".$i."TesterVol"];
$testFormB = $testFormA * $row["test".$i."Accuracy"];
$testFinalForm = $testFormB / 100;
?>
<td><?php echo $testFinalForm ?></td>
<?php } ?>
Instead of accessing indexes like "test4MeterVol" you use $i to build the index string to access the right value in each loop iteration.
Doing the equations inside the loop will give you different values based on each test.
Based on this code and the way to build indexes to access your data you should be able to build the other rows, which are much simpler.
I have simulated the mysql result from an old SO question as the following array:
<?php
$arr = array(
array(
'title'=>'Test',
'name'=>'ABC',
'cat_desc'=>'ABC_DESC',
'parent'=>0,
'parent_menu'=>1
),
array(
'title'=>'Test2',
'name'=>'DEF',
'cat_desc'=>'DEF_DESC',
'parent'=>0,
'parent_menu'=>2
),
array(
'title'=>'Test2',
'name'=>'GHI',
'cat_desc'=>'GHI_DESC',
'parent'=>1,
'parent_menu'=>0
),
array(
'title'=>null,
'name'=>'JKL',
'cat_desc'=>'JKL_DESC',
'parent'=>2,
'parent_menu'=>0
)
);
//print_r($arr);
?>
Now I wonder if I could print the result in this format:
<table>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td> Menu Title Test </td>
<td> Main Category ABC</td>
</tr>
<tr>
<td>GHI</td>
<td>GHI_DESC</td>
</tr>
<tr>
<td> Menu Title Test2 </td>
<td> Main Category DEF</td>
</tr>
<tr>
<td>JKL</td>
<td>JKL_DESC</td>
</tr>
</table>
I am trying the following php to print but it could not give the expected result:
<table>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<?php
$r = 0;
while(list($key, $val)=each($arr)) {
if($val['parent']==0):
if($r % 1==0): ?>
<tr>
<td> Menu Title <?php echo $val['title'];?> </td>
<td> Main Category <?php echo $val['name'];?></td>
</tr>
<?php endif;
endif;
if($val['parent']!=0){ ?>
<tr>
<td><?php echo $val['name'];?></td>
<td><?php echo $val['cat_desc'];?></td>
</tr>
<?php
}
$r++;
} ?>
</table>
Your help and suggestion is very much welcome.
I find it difficult to figure out what you exactly want from your array structure. But I noticed that array element 0 is paired with element 2, and element 1 is paired with element 3. Therefore my code would be like following. It will give you exactly the table that you wanted.
print "<table border=1>";
$numArray = count($arr) / 2;
for($i=0;$i<$numArray;$i++) {
$element = $arr[$i];
print "<tr><td>Menu Title {$element['title']}</td><td>Main Category {$element['name']}</td></tr>";
$element = $arr[$i+2];
print "<tr><td>{$element['name']}</td><td>{$element['cat_desc']} </td></tr>";
}
print "</table>";
You can use this where I commented what to do and why
<table>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<?php
// Store the array into temporary array
$temp_array = $arr;
foreach ($arr as $akey => $sArr)
{
// Check that prrent is 0
if ( $sArr['parent'] ==0 )
{
// Store parent_menu id
$parent_menu = $sArr['parent_menu'];
?>
<tr>
<td> Menu Title <?php echo $sArr['title'];?> </td>
<td> Main Category <?php echo $sArr['name'];?></td>
</tr>
<?php
// Unset or remove current index from array
unset($temp_array[$akey]);
// Iterate all rows
foreach ($temp_array as $skey => $aval)
{
// Check that it has parent for the current menu
if ( $aval['parent'] == $parent_menu )
{
?>
<tr>
<td><?php echo $aval['name'];?></td>
<td><?php echo $aval['cat_desc'];?></td>
</tr>
<?php
}
}
// End of foreach
}
// End of if
}
// End of foreach
?>
</table>
I find that data from a relational DB doesn't really lend itself to direct use and requires some massaging to lower the code complexity. So, if your aim is to make a dynamic menu with submenus, I would suggest refactoring the data structure into more of a tree structure and then looping over each level.
A better data structure would be:
$menus = array(
1 => array( // original array item index 0
'title'=>'Test',
'name'=>'ABC',
'cat_desc'=>'ABC_DESC',
'parent'=>0,
'parent_menu'=>1
'children'=> array( // new array containing submenus
array( // original array item index 2
'title'=>'Test2',
'name'=>'GHI',
'cat_desc'=>'GHI_DESC',
'parent'=>1,
'parent_menu'=>0
),
),
),
2 => array( // original array item index 1
'title'=>'Test2',
'name'=>'DEF',
'cat_desc'=>'DEF_DESC',
'parent'=>0,
'parent_menu'=>2
'children'=> array( // array containing original item index 2
array(
'title'=>null,
'name'=>'JKL',
'cat_desc'=>'JKL_DESC',
'parent'=>2,
'parent_menu'=>0
),
),
),
);
Rather than just entering the data in this more covenient form, it is good practice to convert it. So, to get from the original data to this structure you can use a simple single pass foreach loop:
$menus = [];
foreach($arr as $item) {
if($item['parent'] == 0) {
$item['children'] = array();
$menus[$item['parent_menu']] = $item;
}
else {
$menus[$item['parent']]['children'][] = $item;
}
}
Once you have the data in a nice convenient form you can use simple nested foreach loops to iterate it and output the table in a clear manner like this:
<table>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<?php foreach($menus as $menu) { ?>
<tr>
<td> Menu Title <?php echo $menu['title'];?> </td>
<td> Main Category <?php echo $menu['name'];?></td>
</tr>
<?php foreach($menu['children'] as $child) { ?>
<tr>
<td><?php echo $child['name'];?></td>
<td><?php echo $child['cat_desc'];?></td>
</tr>
<?php } } ?>
</table>
From what I see, I believe $r % 2 makes a little more sense than $r % 1
In case $r is an integer, $r % 1 will always return a zero. Looks like in your case you use this to split logic for odd and even numbers, which makes sense for your data set.
Make sure the data set doesn't change. Maybe it'd be better use a more reliable approach. Adding indexes to the data set wouldn't harm either :)
I'm working on this code and trying to get it to only pull 4 entries from the repository and it keeps pulling all of my entries.
<?php
while ( $i < 4) {
foreach ($churchEvents->getResults() as $churchEvent){
?>
<tbody>
<tr>
<td> <?php echo $churchEvent->getStructuredText('church-events.event-title')->asText(); ?>
</td>
<td class="cuntd" style="vertical-align: middle;">
<?php echo $churchEvent->getDate('church-events.event-start-date')->asDateTime()->format('l, F j, Y'); ?>
</td>
</tr>
</tbody>
<?php $i++; } } ?>
You have two loops, but there is no reason to have two. The inner one (foreach) already iterates through all the $churchEvents, so the outer one (while) will not make it do less.
Remove the outer while loop and the $i++;, and make the inner loop only loop over the first four elements.
For this, you can use the => syntax in the foreach statement to get the value of $i, and then add an if to exit the loop:
<tbody>
<?php
foreach ($churchEvents->getResults() as $i => $churchEvent){
if ($i >= 4) break;
?>
<tr>
<td> <?= $churchEvent->getStructuredText('church-events.event-title')->asText() ?>
</td>
<td class="cuntd" style="vertical-align: middle;">
<?= $churchEvent->getDate('church-events.event-start-date')->asDateTime()->format('l, F j, Y') ?>
</td>
</tr>
<?php
}
?>
</tbody>
Note that tbody should not be generated more than once, so it should stay out of the loop.
Because while is outside foreach.
while ( $i < 4) {
foreach ($churchEvents->getResults() as $churchEvent){
At the start of foreach try if combine with break.
//you don't need while here anymore
foreach ($churchEvents->getResults() as $churchEvent){
if ($i >= 4) {
break;
}
The method names are distinctive enough to make a guess what library/api you're using: (http://prismicio.github.io/php-kit/namespaces/Prismic.html), right?
The ->getResults() method returns an array. So, there are several options; here are some of them:
Using an SPL LimitIterator:
<?php
// only fetching the first four entires
$iter = new LimitIterator( new ArrayIterator($churchEvents->getResults()), 0, 4);
foreach ($iter as $churchEvent) { ?>
<tbody>
<tr>
<td> <?php echo $churchEvent->getStructuredText('church-events.event-title')->asText(); ?></td>
<td class="cuntd" style="vertical-align: middle;"><?php echo $churchEvent->getDate('church-events.event-start-date')->asDateTime()->format('l, F j, Y'); ?></td>
</tr>
</tbody>
<?php
}
Using array_slice() to get the first four elements and then iterate over those four:
<?php
// copying the first four element of the array
$arr = array_slice($churchEvents->getResults(), 0, 4);
foreach ($arr as $churchEvent) { ?>
<tbody>
<tr>
<td> <?php echo $churchEvent->getStructuredText('church-events.event-title')->asText(); ?></td>
<td class="cuntd" style="vertical-align: middle;"><?php echo $churchEvent->getDate('church-events.event-start-date')->asDateTime()->format('l, F j, Y'); ?></td>
</tr>
</tbody>
<?php
}
(prefered option:) Only request four items/fragements:
I'm not familiar with the api but at the very least there's the method pageSize(integer $pageSize) : \Prismic\SearchForm which sets the resultset size for the pagination. Maybe you can even limit the number of results in the response with some predicates or a limit-clause or something, i.e. only request what you really need.
I am really struggling attempting to scrape a table either via XPath or any sort of 'getElement' method. I have searched around and attempted various different approaches to solve my problem below but have come up short and really appreciate any help.
First, the HTML portion I am trying to scrape is the 2nd table on the document and looks like:
<table class="table2" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr><th colspan="8" align="left">Status Information</th></tr>
<tr><th align="left">Status</th><th align="left">Type</th><th align="left">Address</th><th align="left">LP</th><th align="left">Agent Info</th><th align="left">Agent Email</th><th align="left">Phone</th><th align="center">Email Tmplt</th></tr>
<tr></tr>
<tr>
<td align="left">Active</td>
<td align="left">Resale</td>
<td align="center">*Property Address*</td>
<td align="right">*Price*</td>
<td align="center">*Agent Info*</td>
<td align="center">*Agent Email*</td>
<td align="center">*Agent Phone*</td>
<td align="center"> </td>
</tr>
<tr>
<td align="left">Active</td>
<td align="left">Resale</td>
<td align="center">*Property Address*</td>
<td align="right">*Price*</td>
<td align="center">*Agent Info*</td>
<td align="center">*Agent Email*</td>
<td align="center">*Agent Phone*</td>
<td align="center"> </td>
</tr>
...etc
With additional trs continuing containing 8 tds with the same information as detailed above.
What I need to do is iterate through the trs and internal tds to pick up each piece of information (inside the td) for each entry (inside of the tr).
Here is the code I have been struggling with:
<?php
$payload = array(
'http'=>array(
'method'=>"POST",
'content'=>'key=value'
)
);
stream_context_set_default($payload);
$dom = new DOMDocument();
libxml_use_internal_errors(TRUE);
$dom->loadHTMLFile('website-scraping-from.com');
libxml_clear_errors();
foreach ($dom->getElementsByTagName('tr') as $row){
foreach($dom->$row->getElementsByTagName('td') as $node){
echo $node->textContent . "<br/>";
}
}
?>
This code is not returning nearly what I need and I am having a lot of trouble trying to figure out how to fix it, perhaps XPath is a better route to go to find the table / information I need, but I have come up empty with that method as well. Any information is much appreciated.
If it matters, my end goal is to be able to take the table data and dump it into a database if the first td has a value of "Active".
Can this be of any help?
$table = $dom->getElementsByTagName('table')->item(1);
foreach ($table->getElementsByTagName('tr') as $row){
$cells = $row->getElementsByTagName('td');
if ( $cells->item(0)->nodeValue == 'Active' ) {
foreach($cells as $node){
echo $node->nodeValue . "<br/>";
}
}
}
This will fetch the second table, and display the contents of the rows starting with a first cell "Active".
Edit: Here is a more extensive help:
$arr = array();
$table = $dom->getElementsByTagName('table')->item(1);
foreach ($table->getElementsByTagName('tr') as $row){
$cells = $row->getElementsByTagName('td');
if ( $cells->item(0)->nodeValue == 'Active' ) {
$obj = new stdClass;
$obj->type = $cells->item(1)->nodeValue;
$obj->address = $cells->item(2)->nodeValue;
$obj->price = $cells->item(3)->nodeValue;
$obj->agent = $cells->item(4)->nodeValue;
$obj->email = $cells->item(5)->nodeValue;
$obj->phone = $cells->item(6)->nodeValue;
array_push( $arr, $obj );
}
}
print_r( $arr );
If you believe this is a duplicate please let me now as I'm not totally sure what to search for to check, (I will remove if duplicate)
I have a list of of numbers in a mySql field called sess_times in a table called test_sess, the numbers for the first row are:
25, 38, 40, 50
is it possible to split these into php variable e.g:
$no1 = 25
$no2 = 38
$no3 = 40
$no4 = 50
I'm trying to put the individual numbers in a table, something like below:
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td> 1 </td>
<td><?php echo $no1; ?></td>
</tr>
<tr>
<td> 2 </td>
<td><?php echo $no2; ?></td>
</tr>
<tr>
<td> 3 </td>
<td><?php echo $no3; ?></td>
</tr>
<tr>
<td> 4 </td>
<td><?php echo $no4; ?></td>
</tr>
</table>
Any help with this would be great so thanks in advance for any answers.
HERE'S THE SOLUTION I USED, A COMBINATION OF 2 ANSWERS BELOW:
$data = '25,38,40,50';
$string = explode(",", $data);
$number = 0;
echo '<table cellspacing="0" cellpadding="0" border="0">';
for($i=0; $i<count($string); $i++)
{
$number = $number +1;
echo '
<tr>
<td>' . $number . '</td>
<td>' . $string[$i] . '</td>
</tr>
';
}
You can use PHP's explode function.
When you output everything, save it into a variable.
Then, $string = explode(",", $string); will produce this:
Array ( [0] => 28[1] => 38 [2] => 40 [3] => 50 )
To output this, you can then use $string[number] for each value.
You can explode them
$string="25,38,40,50";
$numbers=explode(",",$string);
print_r($numbers); // You can use them for printing like echo $numbers[0]; or 1 or 2 or 3
And if you don't want the result in an array and still want those 4 variable names you can do this
$string="25,38,40,50";
list($no1,$no2,$no3,$no4)=explode(",",$string);
This would give you the table output and it doesn't require the total amount to be known.
$data = '25,38,40,50';
$arr = explode(',', $data);
echo '<table cellspacing="0" cellpadding="0" border="0">';
for($i=0; $i<count($arr); $i++)
{
echo '
<tr>
<td>' . $i+1 . '</td>
<td>' . $arr[$i] . '</td>
</tr>
';
}
echo '</table>';
Use explode();
ex:
$numbers='25,38,40,50';
$numbers_array= explode(",", $no);
foreach($numbers_array as $key => $number)
{
$var_name='no'.(++$key);
$$var_name=$number;
}
echo $no1.'<br>;
echo $no2.'<br>;
echo $no3.'<br>;
echo $no4.'<br>;