У меня есть веб-страница с кучей ссылок. Я хочу написать script, который сбрасывает все данные, содержащиеся в этих ссылках в локальном файле.
Кто-нибудь сделал это с PHP? В качестве ответа можно было бы найти общие рекомендации и gotchas.
У меня есть веб-страница с кучей ссылок. Я хочу написать script, который сбрасывает все данные, содержащиеся в этих ссылках в локальном файле.
Кто-нибудь сделал это с PHP? В качестве ответа можно было бы найти общие рекомендации и gotchas.
Мех. Не анализировать HTML с регулярными выражениями.
Здесь версия DOM, вдохновленная Tatu's:
<?php
function crawl_page($url, $depth = 5)
{
static $seen = array();
if (isset($seen[$url]) || $depth === 0) {
return;
}
$seen[$url] = true;
$dom = new DOMDocument('1.0');
@$dom->loadHTMLFile($url);
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $element) {
$href = $element->getAttribute('href');
if (0 !== strpos($href, 'http')) {
$path = '/' . ltrim($href, '/');
if (extension_loaded('http')) {
$href = http_build_url($url, array('path' => $path));
} else {
$parts = parse_url($url);
$href = $parts['scheme'] . '://';
if (isset($parts['user']) && isset($parts['pass'])) {
$href .= $parts['user'] . ':' . $parts['pass'] . '@';
}
$href .= $parts['host'];
if (isset($parts['port'])) {
$href .= ':' . $parts['port'];
}
$href .= dirname($parts['path'], 1).$path;
}
}
crawl_page($href, $depth - 1);
}
echo "URL:",$url,PHP_EOL,"CONTENT:",PHP_EOL,$dom->saveHTML(),PHP_EOL,PHP_EOL;
}
crawl_page("http://hobodave.com", 2);
Изменить: Я исправил некоторые ошибки из версии Tatu (теперь работает с относительными URL-адресами).
Изменить: Я добавил новый бит функциональности, который не позволяет ему дважды следовать одному URL.
Изменить: повторить вывод в STDOUT, чтобы вы могли перенаправить его в любой файл, который вы хотите
Изменить: Исправлена ошибка, отмеченная Джорджем в его ответе. Относительные URL-адреса больше не будут добавляться к концу URL-адреса, но перезаписывают его. Спасибо Джорджу за это. Обратите внимание, что ответ Джорджа не учитывает ни одного из: https, user, pass или port. Если у вас есть http, загруженное расширение PECL, это просто делается с помощью http_build_url. В противном случае мне придется вручную склеивать, используя parse_url. Еще раз спасибо Джордж.
Здесь моя реализация основана на приведенном выше примере/ответе.
CRAWL CLASS:
class crawler
{
protected $_url;
protected $_depth;
protected $_host;
protected $_useHttpAuth = false;
protected $_user;
protected $_pass;
protected $_seen = array();
protected $_filter = array();
public function __construct($url, $depth = 5)
{
$this->_url = $url;
$this->_depth = $depth;
$parse = parse_url($url);
$this->_host = $parse['host'];
}
protected function _processAnchors($content, $url, $depth)
{
$dom = new DOMDocument('1.0');
@$dom->loadHTML($content);
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $element) {
$href = $element->getAttribute('href');
if (0 !== strpos($href, 'http')) {
$path = '/' . ltrim($href, '/');
if (extension_loaded('http')) {
$href = http_build_url($url, array('path' => $path));
} else {
$parts = parse_url($url);
$href = $parts['scheme'] . '://';
if (isset($parts['user']) && isset($parts['pass'])) {
$href .= $parts['user'] . ':' . $parts['pass'] . '@';
}
$href .= $parts['host'];
if (isset($parts['port'])) {
$href .= ':' . $parts['port'];
}
$href .= $path;
}
}
// Crawl only link that belongs to the start domain
$this->crawl_page($href, $depth - 1);
}
}
protected function _getContent($url)
{
$handle = curl_init($url);
if ($this->_useHttpAuth) {
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($handle, CURLOPT_USERPWD, $this->_user . ":" . $this->_pass);
}
// follows 302 redirect, creates problem wiht authentication
// curl_setopt($handle, CURLOPT_FOLLOWLOCATION, TRUE);
// return the content
curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE);
/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);
// response total time
$time = curl_getinfo($handle, CURLINFO_TOTAL_TIME);
/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
return array($response, $httpCode, $time);
}
protected function _printResult($url, $depth, $httpcode, $time)
{
ob_end_flush();
$currentDepth = $this->_depth - $depth;
$count = count($this->_seen);
echo "N::$count,CODE::$httpcode,TIME::$time,DEPTH::$currentDepth URL::$url <br>";
ob_start();
flush();
}
protected function isValid($url, $depth)
{
if (strpos($url, $this->_host) === false
|| $depth === 0
|| isset($this->_seen[$url])
) {
return false;
}
foreach ($this->_filter as $excludePath) {
if (strpos($url, $excludePath) !== false) {
return false;
}
}
return true;
}
public function crawl_page($url, $depth)
{
if (!$this->isValid($url, $depth)) {
return;
}
// add to the seen URL
$this->_seen[$url] = true;
// get Content and Return Code
list($content, $httpcode, $time) = $this->_getContent($url);
// print Result for current Page
$this->_printResult($url, $depth, $httpcode, $time);
// process subPages
$this->_processAnchors($content, $url, $depth);
}
public function setHttpAuth($user, $pass)
{
$this->_useHttpAuth = true;
$this->_user = $user;
$this->_pass = $pass;
}
public function addFilterPath($path)
{
$this->_filter[] = $path;
}
public function run()
{
$this->crawl_page($this->_url, $this->_depth);
}
}
ПРИМЕНЕНИЕ:
// USAGE
$startURL = 'http://YOUR_URL/';
$depth = 6;
$username = 'YOURUSER';
$password = 'YOURPASS';
$crawler = new crawler($startURL, $depth);
$crawler->setHttpAuth($username, $password);
// Exclude path with the following structure to be processed
$crawler->addFilterPath('customer/account/login/referer');
$crawler->run();
Проверьте PHP-сканер
http://sourceforge.net/projects/php-crawler/
Смотрите, если это поможет.
В нем простейшая форма:
function crawl_page($url, $depth = 5) {
if($depth > 0) {
$html = file_get_contents($url);
preg_match_all('~<a.*?href="(.*?)".*?>~', $html, $matches);
foreach($matches[1] as $newurl) {
crawl_page($newurl, $depth - 1);
}
file_put_contents('results.txt', $newurl."\n\n".$html."\n\n", FILE_APPEND);
}
}
crawl_page('http://www.domain.com/index.php', 5);
Эта функция получит содержимое со страницы, затем сканирует все найденные ссылки и сохраняет содержимое в 'results.txt'. Функции принимают второй параметр, depth, который определяет, как долго должны соблюдаться ссылки. Пропустите 1, если вы хотите анализировать только ссылки с данной страницы.
Зачем использовать PHP для этого, когда вы можете использовать wget, например
wget -r -l 1 http://www.example.com
Для того, чтобы проанализировать содержимое, см. Лучшие методы анализа HTML и используйте функцию поиска examples. Как разобрать HTML, было ответили несколько раз раньше.
С некоторыми небольшими изменениями в hobodave код, вот код, который вы можете использовать для сканирования страниц. Для этого необходимо, чтобы расширение curl было включено на вашем сервере.
<?php
//set_time_limit (0);
function crawl_page($url, $depth = 5){
$seen = array();
if(($depth == 0) or (in_array($url, $seen))){
return;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
$result = curl_exec ($ch);
curl_close ($ch);
if( $result ){
$stripped_file = strip_tags($result, "<a>");
preg_match_all("/<a[\s]+[^>]*?href[\s]?=[\s\"\']+"."(.*?)[\"\']+.*?>"."([^<]+|.*?)?<\/a>/", $stripped_file, $matches, PREG_SET_ORDER );
foreach($matches as $match){
$href = $match[1];
if (0 !== strpos($href, 'http')) {
$path = '/' . ltrim($href, '/');
if (extension_loaded('http')) {
$href = http_build_url($href , array('path' => $path));
} else {
$parts = parse_url($href);
$href = $parts['scheme'] . '://';
if (isset($parts['user']) && isset($parts['pass'])) {
$href .= $parts['user'] . ':' . $parts['pass'] . '@';
}
$href .= $parts['host'];
if (isset($parts['port'])) {
$href .= ':' . $parts['port'];
}
$href .= $path;
}
}
crawl_page($href, $depth - 1);
}
}
echo "Crawled {$href}";
}
crawl_page("http://www.sitename.com/",3);
?>
Я объяснил этот учебник в этом обходчике script
Как уже упоминалось, есть все рамы для искателей, готовые для настройки там, но если то, что вы делаете, так же просто, как вы упомянули, вы можете легко сделать это с нуля.
Скремблирование ссылок: http://www.phpro.org/examples/Get-Links-With-DOM.html
Сбрасывание результатов в файл: http://www.tizag.com/phpT/filewrite.php
Hobodave ты был очень близок. Единственное, что я изменил, - это выражение if, которое проверяет, начинает ли атрибут href найденного тега привязки "http". Вместо того, чтобы просто добавлять переменную $url, которая содержала бы пропущенную страницу, вы должны сначала разбить ее на хост, который можно выполнить с помощью функции parse_url php.
<?php
function crawl_page($url, $depth = 5)
{
static $seen = array();
if (isset($seen[$url]) || $depth === 0) {
return;
}
$seen[$url] = true;
$dom = new DOMDocument('1.0');
@$dom->loadHTMLFile($url);
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $element) {
$href = $element->getAttribute('href');
if (0 !== strpos($href, 'http')) {
/* this is where I changed hobodave code */
$host = "http://".parse_url($url,PHP_URL_HOST);
$href = $host. '/' . ltrim($href, '/');
}
crawl_page($href, $depth - 1);
}
echo "New Page:<br /> ";
echo "URL:",$url,PHP_EOL,"<br />","CONTENT:",PHP_EOL,$dom->saveHTML(),PHP_EOL,PHP_EOL," <br /><br />";
}
crawl_page("http://hobodave.com/", 5);
?>
Вопрос в том, как получить исходный код ajax-вызовов? это не сканируется, например, как сканировать изображения по ссылке вроде этого? http://www.tiendeo.nl/Catalogi/amsterdam/16558&subori=web_sliders&buscar=Boni&sw=1366
PHPCrawl - очень хорошая и продуманная структура искателя.
Я использовал код @hobodave с этой небольшой настройкой, чтобы предотвратить повторное сканирование всех вариантов фрагмента одного URL:
<?php
function crawl_page($url, $depth = 5)
{
$parts = parse_url($url);
if(array_key_exists('fragment', $parts)){
unset($parts['fragment']);
$url = http_build_url($parts);
}
static $seen = array();
...
Затем вы также можете опустить строку $parts = parse_url($url);
в цикле for.
Вы можете попробовать, это может помочь вам.
$search_string = 'american golf News: Fowler beats stellar field in Abu Dhabi';
$html = file_get_contents(url of the site);
$dom = new DOMDocument;
$titalDom = new DOMDocument;
$tmpTitalDom = new DOMDocument;
libxml_use_internal_errors(true);
@$dom->loadHTML($html);
libxml_use_internal_errors(false);
$xpath = new DOMXPath($dom);
$videos = $xpath->query('//div[@class="primary-content"]');
foreach ($videos as $key => $video) {
$newdomaindom = new DOMDocument;
$newnode = $newdomaindom->importNode($video, true);
$newdomaindom->appendChild($newnode);
@$titalDom->loadHTML($newdomaindom->saveHTML());
$xpath1 = new DOMXPath($titalDom);
$titles = $xpath1->query('//div[@class="listingcontainer"]/div[@class="list"]');
if(strcmp(preg_replace('!\s+!',' ', $titles->item(0)->nodeValue),$search_string)){
$tmpNode = $tmpTitalDom->importNode($video, true);
$tmpTitalDom->appendChild($tmpNode);
break;
}
}
echo $tmpTitalDom->saveHTML();