Как создать простой сканер в PHP?
У меня есть веб-страница с кучей ссылок. Я хочу написать script, который сбрасывает все данные, содержащиеся в этих ссылках в локальном файле.
Кто-нибудь сделал это с PHP? В качестве ответа можно было бы найти общие рекомендации и gotchas.
Ответы
Ответ 1
Мех. Не анализировать 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. Еще раз спасибо Джордж.
Ответ 2
Здесь моя реализация основана на приведенном выше примере/ответе.
- Он основан на классе
- использует Curl
- поддержка HTTP Auth
- Пропустить Url, не принадлежащий базовому домену
- Возврат Http-заголовка Код ответа для каждой страницы
- Время возврата для каждой страницы
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();
Ответ 3
Проверьте PHP-сканер
http://sourceforge.net/projects/php-crawler/
Смотрите, если это поможет.
Ответ 4
В нем простейшая форма:
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, если вы хотите анализировать только ссылки с данной страницы.
Ответ 5
Зачем использовать PHP для этого, когда вы можете использовать wget, например
wget -r -l 1 http://www.example.com
Для того, чтобы проанализировать содержимое, см. Лучшие методы анализа HTML и используйте функцию поиска examples. Как разобрать HTML, было ответили несколько раз раньше.
Ответ 6
С некоторыми небольшими изменениями в 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
Ответ 7
Как уже упоминалось, есть все рамы для искателей, готовые для настройки там, но если то, что вы делаете, так же просто, как вы упомянули, вы можете легко сделать это с нуля.
Скремблирование ссылок: http://www.phpro.org/examples/Get-Links-With-DOM.html
Сбрасывание результатов в файл: http://www.tizag.com/phpT/filewrite.php
Ответ 8
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);
?>
Ответ 9
Вопрос в том, как получить исходный код ajax-вызовов? это не сканируется, например, как сканировать изображения по ссылке вроде этого? http://www.tiendeo.nl/Catalogi/amsterdam/16558&subori=web_sliders&buscar=Boni&sw=1366
Ответ 10
PHPCrawl - очень хорошая и продуманная структура искателя.
Ответ 11
Я использовал код @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.
Ответ 12
Вы можете попробовать, это может помочь вам.
$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();