Simple HTML DOM with pagination - php

I want to parse values from website category with paginated posts. Information that I need is inside posts. I tried to use Simple HTML DOM to do that. I got it, but I did not think that is correct. The script works slowly and with a large amount of data I get the error
Maximum execution timeout of 300 seconds
<?php
include('simple_html_dom.php');
$total_pages=600;
$i = 1;
while ($i <= $total_pages):
$html = file_get_html(''.$url.'/'.$from.'');
foreach($html->find('.itemReview h3 a') as $a) {
$post = file_get_html('http://www.website.com/'.$a->href.'');
$author_mail = $post->find('.sellerAreaSecond',0);
$author_mail = $post->plaintext;
$a_mail_array[] = $author_mail;
}
$fp = fopen('file.csv', 'w');
foreach( $a_mail_array as $ddd) {
fputcsv($fp, array($ddd));
}
fclose($fp);
$from++;
endwhile;
?>

As you are requesting your pages and the posts inside them via network, of course this is slow and you run into script timeout with large amounts af data. Try increasing the max execution time in your php.ini file.

One solution would be to increase the time limit in your server settings (php.ini)
A better one would be not to have your server download 100 pages from itself and parse them. Parsing HTML takes tons of time, it has to go through all the code and find your .read_more a and .authoremail. I suspect you're working on plain files for data storage, if that's the case you should switch to a database like MySQL or even SQLite, then you can just query the database - which takes considerably less time. This not only makes your website not crash when then's more content, but also speeds it up.
With SQL, you could just store author's email in a table and then use SELECT authoremail FROM posts and then use foreach(). This also enables you to do things like sorting by date, name etc. on the fly. Just letting your website run slow and inefficient my increasing the time limit is probably not a good idea.

Related

Does the body of my function get read every time?

I'm building a WordPress theme and trying to determine what is causing it to run so slowly. Myfunctions.php is about 500 lines and has functions that interact with the database such as
add_action( 'wp_ajax_nopriv_get_N_more', 'get_N_more' );
add_action( 'wp_ajax_get_N_more', 'get_N_more' );
$N = 9;
function get_N_more ( )
{
global $wpdb, $N;
$thisTable = ($_POST['workType'] === 'projs') ? 'wp_nas_projs' : 'wp_nas_cases';
$q = $wpdb->prepare( "SELECT id,compname,descsmall,sumsmall,imageurl FROM $thisTable ORDER BY postdate DESC LIMIT %d, %d", intval($_POST['numItemsLoaded']), $N);
$nextNRows = $wpdb->get_results($q);
removeEscapes($nextNRows);
$stillSomeLeft = ($_POST['numItemsLoaded'] + $N) < count($wpdb->get_results($wpdb->prepare("SELECT * FROM $thisTable")));
// ^ would like more efficient way of doing above
die(json_encode(array('stillSomeLeft' => $stillSomeLeft, 'workItems' => $nextNRows)));
}
Such functions are not called every time the script is read, however I'm wondering if having them there in the first place could be creating performance slowdowns if PHP has read through them every time.
The short answer is maybe.
If you are calling do_action(...) several times on those actions you have added, then it could be the case that multiple ajax requests are being made and either are timing out or being cancelled as new requests are being issued.
The main reason that you could have a very slow running page is that you are working directly with the database. If these are custom tables then there is a good probability that your indexes are not set correctly and the query is taking forever to return or is timing out.
You should consider using get_posts or the WP_Query for posts or pages as you can then use the native wordpress functions to extract data. If you have made a custom post type then store your data in the post_meta and use wordpress core functions versus a secondary table.
In general if your queries will be returning the same information on nearly every page load you might want to consider storing them to cache so you are not recalculating every page load.
What does all of this mean?
Check your database table indexes. Run the query as it is built by your code in something like mysql workbench.
Open up your browser console or firebug and look at the ajax requests being made when you load your pages.
Check all the places you are calling do_action and adjust accordingly.
When debugging print things to a file or to the screen. This will help tremendously.
PHP must only be reading your functions once during the request, therefore not causing a slowdown.
Look at the definition of your function:
function get_N_more ( )
{
}
It's not guarded in a check to function_exists(). This means that, if this file were parsed anywhere more than once, execution would fail and report an error that your get_N_more function is already declared in this scope.
The complexity and runtime efficiency of the code is never taken into account during parsing of the script, and 500 lines is a relatively trivial amount of code.
tl;dr: This isn't the cause of your slowdown.

Process to big for one request, when splitted in multiple request too the same page i get redirect loop

I have a foreach in cakephp that processes products from a distributor, but the thing is the lists have up to 200products each product can have 3 big pictures with 2 resizes.
So i have in total 1200 big actions to much for one request.
I breaked the foreach at each 10 products, removing them from the array and redirected to the same page. But after a while I get a redirect loop.
Any ideeas on how to avoid this?
If I add another page in this redirect freenzy will it work?
The redirect loop appears only when redirecting in the same page?
The thing is the loop will end, but the browser doesn't know that.
$this->data = $this->Session->read('Parser.data');
$limit = 0;
foreach ($this->data as $key => $data):
$limit++;
if ($limit == 4)
$this->redirect($this->here);
...
$this->Session->delete('Parser.data.' . $key);
endforeach;
$this->redirect(array('controller' => 'parser', 'action' => 'index')); //if $this->data is empty it redirects to upload page
The server work with any number of records from what I have tested, but I have this action along the lines:
$this->getImage(WWW_ROOT . $folder . DS, $new_path, $image['path']);
which looks like this:
protected function getImage($folder = null, $path = null, $from = null) {
if (isset($from) && !empty($from))
file_put_contents($folder . $path, file_get_contents($from));
}
this loads up the server's memory and crashes.
This is why I have to break the foreach a couple of times.
I also tried other functions to get the images as cUrl, but with same results!
Let me copy my answer from another very similar question:
Never use URLs to do these kind of tasks, it is simply plain wrong, insecure and can cause your script to die or the server to become not responding any more.
Lets say you have 10000 users and a script runtime of 30 sec, it is very likely that the script times out before it finished and you end up with just a part of your users being processed at this time. The other scenario with a high or infinite amount of script runtime can lock your server. Depending on the script or DB actions it might cause the server to have a high load and users who use the site while the script is running will encounter a horrible slow to non responding site.
Also you can't really run a loop on a single URL, well you could redirect from one to another that does the limit and offset thing to simulate a loop over the 100000 users. If you don't loop over the records but fetch all 100000 at the same time it's likely your script dies because of running out of memory.
You should create a shell that processes the users in a loop and always just processes batches of for example 10, 50 or 100 users.
When executing your shell I recommend to use it with the "nice" command together to limit the amount of CPU time the shell is allowed to use to prevent the shell from taking 100% CPU usage to keep your site responding.
Look at creating a shell
and setting up a cron in cake.

Memory usage increasing inside loop: are Magento functions the cause?

My platform is PHP 5.2, Apache, Magento EE 1.9 and CentOS.
I have a pretty basic script which is fetching about 60,000 rows of data from an MS-SQL database using PHP's ms_sql() functions. The data is then processed a bit via data from Magento and finally written to a text file.
Really simple stuff...
$result = mssql_query($query);
while($row = mssql_fetch_assoc($result)) {
$member = $row; // Copied so I can modify it
// Do some stuff with each row... e.g.:
$customer = Mage::getModel("customer/customer");
$customer->loadByEmail($member["email"]);
$customerId = $customer->getId();
// Some more stuff like that...
$ordersCollection = Mage::getResourceModel('sales/order_collection');
// ...........
// Some more stuff like that...
$wishList = Mage::getModel('wishlist/wishlist')->loadByCustomer($customer);
// ...........
// Write straight to a file
fwrite($fp, implode("\t", $member) . "\r\n");
// Probably not even necessary
unset($member);
}
The problem is, the memory usage of my script increases with each iteration of the loop (about 10MB for every 300 rows), with a theoretical peak of about 2GB (though it hasn't got there yet).
I've taken great pains to ensure that I'm not leaving any data in memory. No huge arrays are building up, no variables are being added to, everything is either unset() or directly overwritten with each iteration of the loop.
So my question is: could the Magento functions be causing memory leaks?
And if so, how do I stop them from doing so?
Ideally this script should be totally "passive": just grab the query results, modify them a bit (very temporary memory needed for this) then dump them straight to a file and destroy the memory. But this is not happening!
Thanks
Exclude all Mage:: from your code and just dump data to the file without processing. And see what happens to the memory while doing this. Then start adding the Mage:: functions back one by one and see when it breaks.
This way you'll find the culprit. Then you need to start digging into it's implementation and see what could go wrong. You could also consider doing the processing without relying on your Mage:: calls. Just write the plain code to deal with the data in self-contained functions/classes and compare how things turn out if you exclude Mage:: entirely from the process.
Yes — PHP has a long history of non-ideal behavior when it comes to memory managment and code that pushes the edges of it's object oriented model.
You can try an alternate method of querying for your data that wastes less memory, or you can read up on how the Magento core team deals with this same issue.

The bwshare module and PHP scraping

I wrote a script downloading a list of pages from a website. From time to time I receive the following error (the number of seconds is variable):
The bwshare module will refuse your requests for the next 7 seconds.
You have downloaded data too rapidly.
I found when using sleep(2) in the loop, it works much better, however the time delay is too expensive.
What's the best way how to deal with this module? Should I scrape it without any delay and if the response will be similar to the above message simply use sleep for the requested number of seconds?
It all depends on how many pages you can get before the error message.
Try and measure how many pages in average you can get.
4 pages before the bwshare message is the minimum.
If you are getting the error message before reaching 4 page downloads, then il would be faster to sleep(2) after each download.
try this way... it might help u.
$requestTime = 0.1; // s/connection
foreach(/* blah */) {
$start = microtime(true);
// Do your stuff to here.. get_file_content($url) and other processing .........
if($timeTaken = microtime(true)-$start < $requestTime) {
usleep(($requestTime-$timeTaken)*1000000);
}
}
if your problem is solved then try to post your answer so that other people may also be benefited

best way to measure (and refine) performance with PHP?

A site I am working with is starting to get a little sluggish, and I would like to refine it. I think the problem is with the PHP, but I can't be sure. How can I see how long functions are taking to perform?
If you want to test the execution time :
<?php
$startTime = microtime(true);
// Your content to test
$endTime = microtime(true);
$elapsed = $endTime - $startTime;
echo "Execution time : $elapsed seconds";
?>
Try the profiler feature in XDebug or Zend Debugger?
Two things you can do.
place Microtime calls everywhere although its not convenient if you want to test more than one function. So there is a simpler way to do it a better solution if you want to test many functions which i assume you would like to do.
just have a class (click on link to follow tutorial) where you can test how long all your functions take. Rather than place microtime everywhere. you just use this class. which is very convenient
http://codeaid.net/php/calculate-script-execution-time-%28php-class%29
the second thing you can do is to optimize your script is by taking a look at the memory usage.
By observing the memory usage of your scripts, you may be able optimize your code better.
PHP has a garbage collector and a pretty complex memory manager. The amount of memory being used by your script. can go up and down during the execution of a script. To get the current memory usage, we can use the memory_get_usage() function, and to get the highest amount of memory used at any point, we can use the memory_get_peak_usage() function.
view plaincopy to clipboardprint?
echo "Initial: ".memory_get_usage()." bytes \n";
/* prints
Initial: 361400 bytes
*/
// let's use up some memory
for ($i = 0; $i < 100000; $i++) {
$array []= md5($i);
}
// let's remove half of the array
for ($i = 0; $i < 100000; $i++) {
unset($array[$i]);
}
echo "Final: ".memory_get_usage()." bytes \n";
/* prints
Final: 885912 bytes
*/
echo "Peak: ".memory_get_peak_usage()." bytes \n";
/* prints
Peak: 13687072 bytes
*/
http://net.tutsplus.com/tutorials/php/9-useful-php-functions-and-features-you-need-to-know/
PK
You can also make it manually, by recording microtime() value in various places, like this:
<?
$TIMER['start']=microtime(TRUE);
// some code
$query="SELECT ...";
$TIMER['before q']=microtime(TRUE);
$res=mysql_query($query);
$TIMER['after q']=microtime(TRUE);
while ($row = mysql_fetch_array($res)) {
// some code
}
$TIMER['array filled']=microtime(TRUE);
// some code
$TIMER['pagination']=microtime(TRUE);
/and so on
?>
and then visualize it
<?
if ('127.0.0.1' === $_SERVER['REMOTE_ADDR']) {
echo "<table border=1><tr><td>name</td><td>so far</td><td>delta</td><td>per cent</td></tr>";
reset($TIMER);
$start=$prev=current($TIMER);
$total=end($TIMER)-$start;
foreach($TIMER as $name => $value) {
$sofar=round($value-$start,3);
$delta=round($value-$prev,3);
$percent=round($delta/$total*100);
echo "<tr><td>$name</td><td>$sofar</td><td>$delta</td><td>$percent</td></tr>";
$prev=$value;
}
echo "</table>";
}
?>
an IP address check implies that we are doing this profiling on the working site
Though I doubt it's PHP itself. Most likely it's database. So, pay most attention to query execution timing.
however, a "site" term is very broad. It includes also JS, CSS, images and stuff. So, I'd suggest to start form FirebFug's Net page to see what part of whole page takes more time.
Of course, refining can be done only after analysis of profiling results, and cannot be advised here without it.
Your best bet is Xdebug. Im happy as it comes bundled in my PHPed IDE. I can get profiler data at the click of a button.
So maybe you could consider that.
I had similar issues and so I created 2 new tables on the database and two new functions. One was audit_sql and the other was audit_code. Because I used an SQL abstraction class it was easy to time every single SQL call (I used php microtime as some others have suggested). So, I called microtime before and after the SQL call and stored the results on the database.
Similarly with pages. I called microtime at the start and end of each page and if necessary at the start and end of functons, divs - whatever I thought might be a culprit.
The general results were:
SQL calls to MySQL were almost instantaneous and were nto a problem at all. The only thing I would say is that even I was surprised at the number being executed! The site is generated from the database - even the menus, permissions etc. To produce the home page the SQL calls were measured in the 100s.
PHP was not the culprit. This was even more instantaneous that MySQL.
The culprit was.... (big build up!) calls to You Tube and Picassa and other sites like that. I host videos and photo albums on the site (well, I don't actually store them - they are stored on YT etc.) and on the home page are thumbnails that are extracted from You Tube and the like via the You Tube PHP API/Zend Framework. Because this is all http based to the other sites, each one was taking 1, 2 or 3 seconds. This was causing those divs containing these to take between 6 and 12 seconds and the home page up to 17 seconds.
The solution - store all thumbnails on my server. The first time one has to be served from the remote site (YT, Picassa etc.) so do that and then store it on your own site. Future times, you check if you have it and if so serve it always from your server. Cuts the page load time down to 2-3 seconds tops. Granted the first person to view the first home page load after someone has loaded more videos/images will take some time, but not thereafter. People will put a long one-off page load time down to their connection/the internet in general. Too many slow loads of your site and they will stop visiting!
I hope that helps somewhat.

Categories