Проектирование веб-искателя
Я столкнулся с вопросом интервью "Если бы вы разрабатывали веб-искателя, как бы вы избегали попадания в бесконечные циклы?", и я пытаюсь ответить на него.
Как все начинается с самого начала.
Скажем, Google начал с некоторых страниц-концентраторов сказать, что сотни из них (как эти страницы концентратора были найдены в первую очередь, это другой вопрос).
Поскольку Google следует за ссылками со страницы и т.д., Продолжает ли она делать хеш-таблицу, чтобы убедиться, что она не соответствует ранее посещенным страницам.
Что делать, если на той же странице есть 2 имени (URL), которые говорят в наши дни, когда у нас есть URL-адреса и т.д.
Я привел Google в качестве примера. Хотя Google не утечка, как работают его алгоритмы поиска веб-страниц и ранжирования страниц, но любые догадки?
Ответы
Ответ 1
Если вы хотите получить подробный ответ, взгляните на раздел 3.8 этого документа, в котором описан тест на просмотр URL современного скребка:
В процессе извлечения ссылок любой веб-сканер встретит несколько ссылок на один и тот же документ. Чтобы не загружать и не обрабатывать документ несколько раз, перед каждой извлеченной ссылкой необходимо выполнить проверку URL-адреса перед добавлением его к границе URL-адреса. (В качестве альтернативы можно было бы вместо этого выполнить проверку URL-адресов при удалении URL-адреса с границы, но такой подход привел бы к гораздо большей границе.)
Чтобы выполнить проверку URL-адресов, мы храним все URL-адреса, видимые Mercator, в канонической форме в большой таблице, называемой набором URL-адресов. Опять же, слишком много записей, чтобы все они поместились в памяти, поэтому, как и набор отпечатков документов, набор URL хранится в основном на диске.
Чтобы сэкономить место, мы не храним текстовое представление каждого URL-адреса в наборе URL-адресов, а используем контрольную сумму фиксированного размера. В отличие от отпечатков пальцев, представленных в наборе отпечатков документов в документах с проверкой содержимого, поток URL-адресов, проверенных по набору URL-адресов, имеет нетривиальную локальность. Поэтому, чтобы уменьшить количество операций с файлом резервного диска, мы храним в памяти кеш популярных URL-адресов. Интуиция для этого кэша заключается в том, что ссылки на некоторые URL-адреса довольно распространены, поэтому кэширование популярных в памяти приведет к высокой частоте обращений в памяти.
Фактически, используя кэш в памяти из 2 18 записей и политику замены тактовых импульсов, подобную LRU, мы достигаем общего коэффициента попадания в кэш в памяти 66,2% и коэффициента попадания в таблицу 9,5%. недавно добавленные URL, чистая частота попаданий 75,7%. Более того, из 24,3% запросов, которые отсутствуют как в кеше популярных URL-адресов, так и в таблице недавно добавленных URL-адресов, около 1 = 3 производят попадания в буфер в нашей реализации файла произвольного доступа, которая также находится в пользовательском пространстве. Конечным результатом всей этой буферизации является то, что каждый тест членства, который мы выполняем на наборе URL, приводит в среднем к 0,16 запросам поиска и 0,17 вызовам ядра чтения (некоторая часть которых обслуживается из буферов файловой системы ядра). Таким образом, каждый тест на членство в наборе URL-адресов вызывает в шесть раз больше вызовов ядра, чем тест на членство в наборе отпечатков документов. Эти сбережения обусловлены исключительно количеством URL-адресов (т.е. Повторением популярных URL-адресов), присущих потоку URL-адресов, обнаруженному во время сканирования.
По сути, они хешируют все URL-адреса с помощью функции хеширования, которая гарантирует уникальные хеш-значения для каждого URL-адреса, и благодаря расположению URL-адресов становится очень легко найти URL-адреса. Google даже открыл свою функцию хеширования: CityHash
ПРЕДУПРЕЖДЕНИЕ!
Они также могут говорить о ловушках для ботов !!! Ловушка для бота - это раздел страницы, который продолжает генерировать новые ссылки с уникальными URL-адресами, и вы по сути попадете в "бесконечный цикл", перейдя по ссылкам, которые обслуживаются этой страницей. Это не совсем цикл, потому что цикл будет результатом посещения одного и того же URL-адреса, но представляет собой бесконечную цепочку URL-адресов, которые следует избегать сканирования.
Обновление 13.12.2012 - послезавтра должен был закончиться мир :)
Комментарий Fr0zenFyr: если для выбора страниц используется алгоритм AOPIC, то довольно легко избежать ловушек типа бесконечного цикла. Вот краткое изложение того, как работает AOPIC:
- Получить набор из N начальных страниц.
- Выделите сумму кредита X каждой странице, чтобы каждая страница имела кредит X/N (т.е. Равную сумму кредита) до начала сканирования.
- Выберите страницу P, где P имеет наибольшую сумму кредита (или, если все страницы имеют одинаковую сумму кредита, сканируйте случайную страницу).
- Сканирование страницы P (допустим, что при сканировании у P было 100 кредитов).
- Извлеките все ссылки со страницы P (допустим, их 10).
- Установите кредиты P на 0.
- Возьмите 10% "налога" и разместите его на лямбда-странице.
- Выделите равное количество кредитов каждой ссылке, найденной на странице P, из исходного кредита P - налога: так (100 (P кредитов) - 10 (10% налога))/10 (ссылок) = 9 кредитов на каждую ссылку.
- Повторите с шага 3.
Поскольку страница Lambda непрерывно собирает налог, в конечном итоге это будет страница с наибольшей суммой кредита, и нам придется "сканировать" ее. Я говорю "сканировать" в кавычках, потому что мы на самом деле не делаем HTTP-запрос для Lambda-страницы, мы просто берем его кредиты и распределяем их в равной степени по всем страницам в нашей базе данных.
Так как ловушки для ботов дают только внутренние ссылки, и они редко получают кредит извне, они будут постоянно пропускать кредиты (от налогообложения) на страницу Lambda. Лямбда-страница будет равномерно распределять кредиты по всем страницам в базе данных, и при каждом цикле страница ловушки ботов будет терять все больше и больше кредитов, пока у нее не будет так мало кредитов, что она почти никогда не будет сканироваться снова. Это не случится с хорошими страницами, потому что они часто получают кредиты от обратных ссылок, найденных на других страницах. Это также приводит к динамическому рангу страниц, и вы заметите, что каждый раз, когда вы делаете снимок своей базы данных, упорядочиваете страницы по количеству кредитов, которые они имеют, тогда они, скорее всего, будут упорядочены примерно в соответствии с их истинным рангом страницы.,
Это только позволяет избежать ловушек для ботов типа бесконечного цикла, но есть много других ловушек для ботов, на которые следует обратить внимание, и есть способы их обойти.
Ответ 2
В то время как все здесь уже предлагали создать свой веб-искатель, вот как Google оценивает страницы.
Google предоставляет каждой странице рейтинг на основе количества обратных ссылок (количество ссылок на других сайтах указывает на определенный сайт/страницу). Это называется оценкой релевантности. Это основано на том факте, что если на странице есть ссылки на многие другие страницы, это, вероятно, важная страница.
Каждый сайт/страница рассматривается как node на графике. Ссылки на другие страницы - это направленные края. Степень вершины определяется как количество входящих ребер. Узлы с большим количеством входящих ребер оцениваются выше.
Вот как определяется PageRank. Предположим, что страница Pj имеет Lj ссылки. Если одно из этих ссылок относится к странице Pi, то Pj передаст 1/Lj его значение для Pi. Рейтинг важности Pi является тогда суммой всех вкладов, сделанных страницами, связанными с ним. Итак, если мы обозначим множество страниц, связанных с Pi через Bi, то мы получим следующую формулу:
Importance(Pi)= sum( Importance(Pj)/Lj ) for all links from Pi to Bi
Ранги помещаются в матрицу, называемую гиперссылкой: H [i, j]
Строка в этой матрице равна либо 0, либо 1/Lj, если существует связь от Pi до Bi. Другим свойством этой матрицы является то, что если мы суммируем все строки в столбце, получим 1.
Теперь нам нужно умножить эту матрицу на вектор Eigen с именем я (с собственным значением 1), так что:
I = H*I
Теперь мы начинаем итерацию: IH, IIH, IIIH.... я ^ k * H до тех пор, пока решение не сходится. т.е. мы получаем почти одинаковые числа в матрице на шаге k и k + 1.
Теперь все, что осталось в векторе I, - важность каждой страницы.
Для простого примера домашней работы класса см. http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html
Что касается решения дублированной проблемы в вашем интервью, сделайте контрольную сумму на всей странице и используйте либо это, либо bash контрольной суммы в качестве вашего ключа на карте, чтобы отслеживать посещаемые страницы.
Ответ 3
В зависимости от того, насколько глубоко должен был быть их вопрос. Если бы они просто пытались избежать следования тем же ссылкам туда и обратно, то хэширования этого URL было бы достаточно.
Как насчет контента, имеющего буквально тысячи URL-адресов, которые ведут к одному и тому же контенту? Как и параметр QueryString, который ничем не влияет, но может иметь бесконечное количество итераций. Я предполагаю, что вы также можете использовать содержимое страницы и сравнить URL-адрес, чтобы узнать, похожи ли они на то, что поймать контент, который идентифицируется несколькими URL-адресами. См. Например, Bot Traps, упомянутые в сообщении @Lirik.
Ответ 4
Для хранения результатов вам понадобится какая-то хэш-таблица, вам просто нужно проверить ее перед загрузкой каждой страницы.
Ответ 5
Проблема заключается не в сканировании дублированных URL-адресов, которые разрешаются индексом с использованием хэша, полученного из URL-адресов. Проблема заключается в сканировании DUPLICATED CONTENT. Каждый URL-адрес "Crawler Trap" отличается (год, день, SessionID...).
Не существует "идеального" решения... но вы можете использовать некоторые из этих стратегий:
• Сохраняйте поле своего уровня, который находится на веб-сайте. Для каждой страницы получения URL-адресов со страницы увеличьте уровень. Это будет похоже на дерево. Вы можете прекратить сканирование на определенном уровне, например 10 (думаю, Google использует это).
• Вы можете попытаться создать своего рода HASH, который можно сравнить, чтобы найти похожие документы, поскольку вы не можете сравнивать с каждым документом в своей базе данных. Есть SimHash из google, но я не смог найти какую-либо реализацию для использования. Тогда я создал свою собственную. Мой хэш считает низкочастотные и высокочастотные символы внутри html-кода и генерирует 20-байтовый хэш, который сравнивается с небольшим кэшем последних просканированных страниц внутри AVLTree с поиском NearNeighbors с некоторым допуском (около 2). Вы не можете использовать ссылку на расположение символов в этом хеше. После "распознавания" ловушки вы можете записать шаблон url дублированного контента и начать игнорировать страницы с этим.
• Как и Google, вы можете создать рейтинг на каждом веб-сайте и "доверять" больше, чем другим.
Ответ 6
Искатель хранит пул URL-адресов, содержащий все URL-адреса, подлежащие обходу. Чтобы избежать "бесконечного цикла", основная идея - проверить наличие каждого URL перед добавлением в пул.
Однако это непросто реализовать, когда система масштабируется до определенного уровня. Наивный подход заключается в сохранении всех URL-адресов в hashset и проверке наличия каждого нового URL-адреса. Это не будет работать, если в память будет слишком много URL-адресов.
Здесь есть несколько решений. Например, вместо хранения всех URL-адресов в памяти мы должны хранить их на диске. Чтобы сэкономить место, вместо необработанного URL-адреса следует использовать хеш-URL. Также стоит отметить, что мы должны сохранять каноническую форму URL, а не оригинальную. Поэтому, если URL-адрес сокращен такими службами, как bit.ly, лучше получить конечный URL-адрес. Чтобы ускорить процесс проверки, можно создать слой кеша. Или вы можете видеть это как систему распределенного кеша, которая является отдельной темой.
В сообщении Build the Web Crawler содержится подробный анализ этой проблемы.
Ответ 7
Я также потребовал использовать сканер и не могу найти подходящий для моего требования, поэтому после этого я разработал базовую библиотеку сканера для реализации простых требований. Но дает возможность выполнить практически все принципы работы гусеничного хода. Вы можете проверить DotnetCrawler github repo, который реализует модули Downloader-Processor-Pipeline самостоятельно с реализацией по умолчанию с использованием Entity Framework Core для сохранения данных на Sql Server.
https://github.com/mehmetozkaya/DotnetCrawler
Ответ 8
Веб-сканер - это компьютерная программа, которая использовалась для сбора/сканирования следующих ключевых значений (ссылки HREF, ссылки на изображения, метаданные и т.д.) С заданного URL-адреса веб-сайта. Он спроектирован как интеллектуальный, чтобы переходить по различным ссылкам HREF, которые уже получены из предыдущего URL, поэтому Crawler может переходить с одного веб-сайта на другой. Обычно это называется веб-паук или веб-бот. Этот механизм всегда действует как основа поисковой системы в Интернете.
Пожалуйста, найдите исходный код из моего технического блога - http://www.algonuts.info/how-to-built-a-simple-web-crawler-in-php.html
<?php
class webCrawler
{
public $siteURL;
public $error;
function __construct()
{
$this->siteURL = "";
$this->error = "";
}
function parser()
{
global $hrefTag,$hrefTagCountStart,$hrefTagCountFinal,$hrefTagLengthStart,$hrefTagLengthFinal,$hrefTagPointer;
global $imgTag,$imgTagCountStart,$imgTagCountFinal,$imgTagLengthStart,$imgTagLengthFinal,$imgTagPointer;
global $Url_Extensions,$Document_Extensions,$Image_Extensions,$crawlOptions;
$dotCount = 0;
$slashCount = 0;
$singleSlashCount = 0;
$doubleSlashCount = 0;
$parentDirectoryCount = 0;
$linkBuffer = array();
if(($url = trim($this->siteURL)) != "")
{
$crawlURL = rtrim($url,"/");
if(($directoryURL = dirname($crawlURL)) == "http:")
{ $directoryURL = $crawlURL; }
$urlParser = preg_split("/\//",$crawlURL);
//-- Curl Start --
$curlObject = curl_init($crawlURL);
curl_setopt_array($curlObject,$crawlOptions);
$webPageContent = curl_exec($curlObject);
$errorNumber = curl_errno($curlObject);
curl_close($curlObject);
//-- Curl End --
if($errorNumber == 0)
{
$webPageCounter = 0;
$webPageLength = strlen($webPageContent);
while($webPageCounter < $webPageLength)
{
$character = $webPageContent[$webPageCounter];
if($character == "")
{
$webPageCounter++;
continue;
}
$character = strtolower($character);
//-- Href Filter Start --
if($hrefTagPointer[$hrefTagLengthStart] == $character)
{
$hrefTagLengthStart++;
if($hrefTagLengthStart == $hrefTagLengthFinal)
{
$hrefTagCountStart++;
if($hrefTagCountStart == $hrefTagCountFinal)
{
if($hrefURL != "")
{
if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
{
if($doubleSlashCount >= 1)
{ $hrefURL = "http://".$hrefURL; }
else if($parentDirectoryCount >= 1)
{
$tempData = 0;
$tempString = "";
$tempTotal = count($urlParser) - $parentDirectoryCount;
while($tempData < $tempTotal)
{
$tempString .= $urlParser[$tempData]."/";
$tempData++;
}
$hrefURL = $tempString."".$hrefURL;
}
else if($singleSlashCount >= 1)
{ $hrefURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$hrefURL; }
}
$host = "";
$hrefURL = urldecode($hrefURL);
$hrefURL = rtrim($hrefURL,"/");
if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
{
$dump = parse_url($hrefURL);
if(isset($dump["host"]))
{ $host = trim(strtolower($dump["host"])); }
}
else
{
$hrefURL = $directoryURL."/".$hrefURL;
if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
{
$dump = parse_url($hrefURL);
if(isset($dump["host"]))
{ $host = trim(strtolower($dump["host"])); }
}
}
if($host != "")
{
$extension = pathinfo($hrefURL,PATHINFO_EXTENSION);
if($extension != "")
{
$tempBuffer ="";
$extensionlength = strlen($extension);
for($tempData = 0; $tempData < $extensionlength; $tempData++)
{
if($extension[$tempData] != "?")
{
$tempBuffer = $tempBuffer.$extension[$tempData];
continue;
}
else
{
$extension = trim($tempBuffer);
break;
}
}
if(in_array($extension,$Url_Extensions))
{ $type = "domain"; }
else if(in_array($extension,$Image_Extensions))
{ $type = "image"; }
else if(in_array($extension,$Document_Extensions))
{ $type = "document"; }
else
{ $type = "unknown"; }
}
else
{ $type = "domain"; }
if($hrefURL != "")
{
if($type == "domain" && !in_array($hrefURL,$this->linkBuffer["domain"]))
{ $this->linkBuffer["domain"][] = $hrefURL; }
if($type == "image" && !in_array($hrefURL,$this->linkBuffer["image"]))
{ $this->linkBuffer["image"][] = $hrefURL; }
if($type == "document" && !in_array($hrefURL,$this->linkBuffer["document"]))
{ $this->linkBuffer["document"][] = $hrefURL; }
if($type == "unknown" && !in_array($hrefURL,$this->linkBuffer["unknown"]))
{ $this->linkBuffer["unknown"][] = $hrefURL; }
}
}
}
$hrefTagCountStart = 0;
}
if($hrefTagCountStart == 3)
{
$hrefURL = "";
$dotCount = 0;
$slashCount = 0;
$singleSlashCount = 0;
$doubleSlashCount = 0;
$parentDirectoryCount = 0;
$webPageCounter++;
while($webPageCounter < $webPageLength)
{
$character = $webPageContent[$webPageCounter];
if($character == "")
{
$webPageCounter++;
continue;
}
if($character == "\"" || $character == "'")
{
$webPageCounter++;
while($webPageCounter < $webPageLength)
{
$character = $webPageContent[$webPageCounter];
if($character == "")
{
$webPageCounter++;
continue;
}
if($character == "\"" || $character == "'" || $character == "#")
{
$webPageCounter--;
break;
}
else if($hrefURL != "")
{ $hrefURL .= $character; }
else if($character == "." || $character == "/")
{
if($character == ".")
{
$dotCount++;
$slashCount = 0;
}
else if($character == "/")
{
$slashCount++;
if($dotCount == 2 && $slashCount == 1)
$parentDirectoryCount++;
else if($dotCount == 0 && $slashCount == 1)
$singleSlashCount++;
else if($dotCount == 0 && $slashCount == 2)
$doubleSlashCount++;
$dotCount = 0;
}
}
else
{ $hrefURL .= $character; }
$webPageCounter++;
}
break;
}
$webPageCounter++;
}
}
$hrefTagLengthStart = 0;
$hrefTagLengthFinal = strlen($hrefTag[$hrefTagCountStart]);
$hrefTagPointer =& $hrefTag[$hrefTagCountStart];
}
}
else
{ $hrefTagLengthStart = 0; }
//-- Href Filter End --
//-- Image Filter Start --
if($imgTagPointer[$imgTagLengthStart] == $character)
{
$imgTagLengthStart++;
if($imgTagLengthStart == $imgTagLengthFinal)
{
$imgTagCountStart++;
if($imgTagCountStart == $imgTagCountFinal)
{
if($imgURL != "")
{
if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
{
if($doubleSlashCount >= 1)
{ $imgURL = "http://".$imgURL; }
else if($parentDirectoryCount >= 1)
{
$tempData = 0;
$tempString = "";
$tempTotal = count($urlParser) - $parentDirectoryCount;
while($tempData < $tempTotal)
{
$tempString .= $urlParser[$tempData]."/";
$tempData++;
}
$imgURL = $tempString."".$imgURL;
}
else if($singleSlashCount >= 1)
{ $imgURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$imgURL; }
}
$host = "";
$imgURL = urldecode($imgURL);
$imgURL = rtrim($imgURL,"/");
if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
{
$dump = parse_url($imgURL);
$host = trim(strtolower($dump["host"]));
}
else
{
$imgURL = $directoryURL."/".$imgURL;
if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
{
$dump = parse_url($imgURL);
$host = trim(strtolower($dump["host"]));
}
}
if($host != "")
{
$extension = pathinfo($imgURL,PATHINFO_EXTENSION);
if($extension != "")
{
$tempBuffer ="";
$extensionlength = strlen($extension);
for($tempData = 0; $tempData < $extensionlength; $tempData++)
{
if($extension[$tempData] != "?")
{
$tempBuffer = $tempBuffer.$extension[$tempData];
continue;
}
else
{
$extension = trim($tempBuffer);
break;
}
}
if(in_array($extension,$Url_Extensions))
{ $type = "domain"; }
else if(in_array($extension,$Image_Extensions))
{ $type = "image"; }
else if(in_array($extension,$Document_Extensions))
{ $type = "document"; }
else
{ $type = "unknown"; }
}
else
{ $type = "domain"; }
if($imgURL != "")
{
if($type == "domain" && !in_array($imgURL,$this->linkBuffer["domain"]))
{ $this->linkBuffer["domain"][] = $imgURL; }
if($type == "image" && !in_array($imgURL,$this->linkBuffer["image"]))
{ $this->linkBuffer["image"][] = $imgURL; }
if($type == "document" && !in_array($imgURL,$this->linkBuffer["document"]))
{ $this->linkBuffer["document"][] = $imgURL; }
if($type == "unknown" && !in_array($imgURL,$this->linkBuffer["unknown"]))
{ $this->linkBuffer["unknown"][] = $imgURL; }
}
}
}
$imgTagCountStart = 0;
}
if($imgTagCountStart == 3)
{
$imgURL = "";
$dotCount = 0;
$slashCount = 0;
$singleSlashCount = 0;
$doubleSlashCount = 0;
$parentDirectoryCount = 0;
$webPageCounter++;
while($webPageCounter < $webPageLength)
{
$character = $webPageContent[$webPageCounter];
if($character == "")
{
$webPageCounter++;
continue;
}
if($character == "\"" || $character == "'")
{
$webPageCounter++;
while($webPageCounter < $webPageLength)
{
$character = $webPageContent[$webPageCounter];
if($character == "")
{
$webPageCounter++;
continue;
}
if($character == "\"" || $character == "'" || $character == "#")
{
$webPageCounter--;
break;
}
else if($imgURL != "")
{ $imgURL .= $character; }
else if($character == "." || $character == "/")
{
if($character == ".")
{
$dotCount++;
$slashCount = 0;
}
else if($character == "/")
{
$slashCount++;
if($dotCount == 2 && $slashCount == 1)
$parentDirectoryCount++;
else if($dotCount == 0 && $slashCount == 1)
$singleSlashCount++;
else if($dotCount == 0 && $slashCount == 2)
$doubleSlashCount++;
$dotCount = 0;
}
}
else
{ $imgURL .= $character; }
$webPageCounter++;
}
break;
}
$webPageCounter++;
}
}
$imgTagLengthStart = 0;
$imgTagLengthFinal = strlen($imgTag[$imgTagCountStart]);
$imgTagPointer =& $imgTag[$imgTagCountStart];
}
}
else
{ $imgTagLengthStart = 0; }
//-- Image Filter End --
$webPageCounter++;
}
}
else
{ $this->error = "Unable to proceed, permission denied"; }
}
else
{ $this->error = "Please enter url"; }
if($this->error != "")
{ $this->linkBuffer["error"] = $this->error; }
return $this->linkBuffer;
}
}
?>
Ответ 9
Ну, в основном веб-ориентированный граф, поэтому вы можете построить график из URL-адресов, а затем выполнить обход BFS или DFS во время разметки посещенных узлов, чтобы вы не посещали одну и ту же страницу дважды.
Ответ 10
Это пример веб-искателя. Что можно использовать для сбора mac-адресов для подмены Mac.
#!/usr/bin/env python
import sys
import os
import urlparse
import urllib
from bs4 import BeautifulSoup
def mac_addr_str(f_data):
global fptr
global mac_list
word_array = f_data.split(" ")
for word in word_array:
if len(word) == 17 and ':' in word[2] and ':' in word[5] and ':' in word[8] and ':' in word[11] and ':' in word[14]:
if word not in mac_list:
mac_list.append(word)
fptr.writelines(word +"\n")
print word
url = "http://stackoverflow.com/info/tagged/mac-address"
url_list = [url]
visited = [url]
pwd = os.getcwd();
pwd = pwd + "/internet_mac.txt";
fptr = open(pwd, "a")
mac_list = []
while len(url_list) > 0:
try:
htmltext = urllib.urlopen(url_list[0]).read()
except:
url_list[0]
mac_addr_str(htmltext)
soup = BeautifulSoup(htmltext)
url_list.pop(0)
for tag in soup.findAll('a',href=True):
tag['href'] = urlparse.urljoin(url,tag['href'])
if url in tag['href'] and tag['href'] not in visited:
url_list.append(tag['href'])
visited.append(tag['href'])
Измените URL-адрес, чтобы сканировать больше сайтов...... удачи