I use the code below to iterate all div's on a page with id = news using PHPScraper. Is it possible to only take the first div it find so that the array only contains one entry? I was thinking of maybe (if possible) only take one in the foreach loop like you can do in c# (myList.Take(1))
$dom = file_get_html('http://localhost/test.html');
//collect all news entries into an array
$myArray = array();
if(!empty($dom)) {
$divClass = $title = '';
foreach($dom->find("div[id*=news]") as $divClass) {
You can use break to stop the loop from continuing after you've added the first div.
Something like this:
foreach($dom->find("div[id*=news]") as $divClass) {
$myArray[] = $divClass; // Just assuming you're doing something like this
break;
}
Side note: The code $divClass = $title = ''; before the loop doesn't serve any purpose in your posted code. The variable $divClass will be completely overwritten on each iteration of your foreach.
I'm guessing you're using PHP Simple HTML DOM Parser.
To grab only one element, you can simply pass 0 as the second argument of find:
$firstDiv = $dom->find('div[id*=news]', 0);
foreach($dom->find("div[id*=news]") as $divClass) {
/// work here
break;
}
break; statement is used to stop loop from further processing. So if you use it directly then loop would only execute once.
Related
How can I use a loop for with simplexml_load_file to get all data?
$meteo = simplexml_load_file('hxxp://dzmeteo.com/weather.xml');
for($i=1;$i<$jours;$i++) {
$d1_icon_d = $meteo->dayf->day[$i]->part[0]->icon;
$d1_icon_n = $meteo->dayf->day[$i]->part[1]->icon;
echo $d1_icon_d;
$i++;
}
You are quite close:
$meteo = simplexml_load_file('hxxp://dzmeteo.com/weather.xml');
foreach ($meteo->dayf->day as $day) {
$d1_icon_d = $day->part[0]->icon;
$d1_icon_n = $day->part[1]->icon;
echo $d1_icon_d;
}
Any time you want to access the content on an entire array use foreach. It provides for a reliable way to ensure you have actually seen all the elements of the array and makes your code readable to yourself and others.
Remove $i++; at the end of the loop. The for loop will increment the index for you so as of now you are getting rows 1,3,5,7 and so on instead of 1,2,3,4,5,6. Also I'm not sure if this is deliberate but typically indexes start at 0 and yours starts at 1.
my question has to do with PHP and XML. I would like to echo out some attributes, but echo ones that repeat only once.
Say this was the XML I was dealing with, and it was called beatles.xml:
<XML_DATA item=“TheBeatles”>
<Beatles>
<Beatle Firstname=“George” Lastname=“Harrison” Instrument=“Guitar”>Harrison, George</Beatle>
<Beatle Firstname=“John” Lastname=“Lennon” Instrument=“Guitar”>Lennon, John</Beatle>
<Beatle Firstname=“Paul” Lastname=“McCartney” Instrument=“Bass”>McCartney, Paul</Beatle>
<Beatle Firstname=“Ringo” Lastname=“Starr” Instrument=“Drums”>Starr, Ringo</Beatle>
</Beatles>
</XML_DATA>
This is the PHP I have so far:
$xml = simplexml_load_file("http://www.example.com/beatles.xml");
$beatles = $xml->Beatles->Beatle;
foreach($beatles as $beatle) {
echo $beatle->attributes()->Instrument.',';
}
I would expect this to echo out Guitar,Guitar,Bass,Drums, but I would like Guitar to only display once. How would I prevent repeat attribute values from echoing out?
Inside the foreach loop, cast the instrument name as a string and push it into an array. Once the loop finishes execution, you will have an array containing all the instrument names (with duplicates, of course). You can now use array_unique() to filter out the duplicate values from the array:
$instruments = array();
foreach($beatles as $beatle) {
$instruments[] = (string) $beatle->attributes()->Instrument;
}
$instruments = array_unique($instruments);
Demo.
$xml = simplexml_load_file("http://www.example.com/beatles.xml");
$beatles = $xml->Beatles->Beatle;
$result = array();
foreach($beatles as $beatle) {
if (!array_key_exists($beatle->attributes()->Instrument, $result)) {
$result[] = $beatle->attributes()->Instrument;
// echo $beatle->attributes()->Instrument.',';
}
}
then Loop through the $result array with foreach
Either use xpath.
I have a really strange problem with range();
According to docs :
Create an array containing a range of elements
But when I do :
foreach (range(900,950,1) as $art_id){
//ob_start();
//do stuff
//do a lot more stuff
echo $art_id;
//ob_get_clean(); }
or even
$arts_id = range (900, 920);
foreach ($arts_id as $art_id){
//ob_start();
//do stuff
//do a lot more stuff
echo $art_id;
//ob_get_clean(); }
The output is strangly repeating itself in a series like
"900,900,901,900,901,902,900,901,9002,903,900..."
meaning it is comming back to the first ID after each loop.
(1st iteration -> 900
2nd iteration -> 900,901
3rd iteration -> 900,901,902
...)
When I just put a manual array it works perfectly in order and no duplicates :
$arts_id = array(900,901,902,903,904,905,906,907,908,909,910...);
What Am I doing wrong (again ?? )
EDIT I
here is the whole script :
http://pastebin.com/ZHm3ub6n
It is actually a slightly modified version of the slashdot scraping example included in the simplehtmldom script . Nothing special.
It is executed inside WP but OUTSIDE the loop ..
It must be in the rest of your code, because this works fine.
please share more of the script.
It looks like the foreach is nested within a similair foreach,
$arts_id = range (900, 920);
foreach ($arts_id as $art_id){
foreach (range (900,$art_id) as $art_id2){
echo $art_id2."<br/>";
}
}
This produces an output you've described
EDIT
Personally i'd add the the function scraping_slashdot a reset of the variable $ret just in case.
for example:
$ret = array();
Currently the echo of $output is within the loop, which creates an output like the following:
Article 1
Article 1, Article 2
Article 1, Article 2, Article 3
etc.
place echo $output outside the loop, or $ouptut = ''; inside the loop.
I would like to find all <tr> starting from the second, but i don't know how to get it right..
$items = $html->find('tr');
That piece of code gets all trs but i want everyone except the first one because that one contains <th>.
Just cut off the first element.
$items = array_slice($html->find('tr'), 1)
When you get your list with $html->find('tr'); make a loop that don't care of the first "index/row".
if Simple html dom work like Jquery, try to use like this:
$items = $html->find('tr:not(:has(th)');
As PoulsQ suggests you CAN do it like
$firstTr = true;
foreach($html->find('tr') as $tr) {
if(!$firstTr) {
// YOUR LOGIC FOR A TR HERE
}
else {
$firstTr = false;
}
}
But I think it would be nicer code if you query the DOM to ignore the first element.
You can get all trs from $html->find('tr'); from this u can add the condition to ignore the if the next element for object is "th" tag then u can ignore that tr.
Is it possible to have an AND in a foreach loop?
For Example,
foreach ($bookmarks_latest as $bookmark AND $tags_latest as $tags)
You can always use a loop counter to access the same index in the second array as you are accessing in the foreach loop (i hope that makes sense).
For example:-
$i = 0;
foreach($bookmarks_latest as $bookmark){
$result['bookmark'] = $bookmark;
$result['tag'] = $tags_latest[$i];
$i++;
}
That should achieve what you are trying to do, otherwise use the approach sugested by dark_charlie.
In PHP 5 >= 5.3 you can use MultipleIterator.
Short answer: no. You can always put the bookmarks and tags into one array and iterate over it.
Or you could also do this:
reset($bookmarks_latest);
reset($tags_latest);
while ((list(, $bookmark) = each($bookmarks_latest)) && (list(,$tag) = each($tags_latest)) {
// Your code here that uses $bookmark and $tag
}
EDIT:
The requested example for the one-array solution:
class BookmarkWithTag {
public var $bookmark;
public var $tag;
}
// Use the class, fill instances to the array $tagsAndBookmarks
foreach ($tagsAndBookmarks as $bookmarkWithTag) {
$tag = $bookmarkWithTag->tag;
$bookmark = $bookmarkWithTag->bookmark;
}
you can't do that.
but you can
<?php
foreach($keyval as $key => $val) {
// something with $key and $val
}
the above example works really well if you have a hash type array but if you have nested values in the array I recommend you:
or option 2
<?php
foreach ($keyval as $kv) {
list($val1, $val2, $valn) = $kv;
}
No, but there are many ways to do this, e.g:
reset($tags_latest);
foreach ($bookmarks_latest as $bookmark){
$tags = current($tags_latest); next($tags_latest);
// here you can use $bookmark and $tags
}
No. No, it is not.
You'll have to manually write out a loop that uses indexes or internal pointers to traverse both arrays at the same time.
Yes, for completeness:
foreach (array_combine($bookmarks_latest, $tags_latest) as $bookm=>$tag)
That would be the native way to get what you want. But it only works if both input arrays have the exact same length, obviously.
(Using a separate iteration key is the more common approach however.)