Время прочтения: 4 мин.
Одна из задач, которая стояла в рамках проекта, нацеленного на исследование мер поисковой оптимизации (SEO, search engine optimization) информационных ресурсов дочерних структур организации, предполагала поиск всех ссылок и выявление среди них так называемых «мертвых (битых) ссылок», отсылающих на несуществующий сайт, страницу, файл, что в свою очередь понижает рейтинг информационного ресурса.
В этом посте я хочу поделиться одним из способов извлечения всех ссылок сайта (внутренних и внешних), который поможет при решении подобных задач.
Посмотрим, как можно создать инструмент извлечения ссылок в Python, используя пакет requests и библиотеку BeautifulSoup. Итак,
Установим зависимости:
pip install requests bs4
Импортируем необходимые модули:
import requests
from urllib.parse import urlparse, urljoin
from bs4 import BeautifulSoup
Затем определим две переменные: одну для всех внутренних ссылок (это URL, которые ссылаются на другие страницы того же сайта), другую для внешних ссылок вэб-сайта (это ссылки на другие сайты).
# Инициализировать набор ссылок (уникальные ссылки)
int_url = set()
ext_url = set()
Далее создадим функцию для проверки URL – адресов. Это обеспечит правильную схему в ссылке — протокол, например, http или https и имя домена в URL.
# Проверяем URL
def valid_url(url):
parsed = urlparse(url)
return bool(parsed.netloc) and bool(parsed.scheme)
На следующем шаге создадим функцию, возвращающую все действительные URL-адреса одной конкретной веб-страницы:
# Возвращаем все URL-адреса
def website_links(url):
urls = set()
# извлекаем доменное имя из URL
domain_name = urlparse(url).netloc
# скачиваем HTML-контент вэб-страницы
soup = BeautifulSoup(requests.get(url).content, "html.parser")
Теперь получим все HTML теги, содержащие все ссылки вэб-страницы.
for a_tag in soup.findAll("a"):
href = a_tag.attrs.get("href")
if href == "" or href is None:
# href пустой тег
continue
В итоге получаем атрибут href и проверяем его. Так как не все ссылки абсолютные, возникает необходимость выполнить соединение относительных URL-адресов и имени домена. К примеру, когда найден href — «/search» и URL — «google.com» , то в результате получим «google.com/search».
# присоединить URL, если он относительный (не абсолютная ссылка)
href = urljoin(url, href)
В следующем шаге удаляем параметры HTTP GET из URL-адресов:
parsed_href = urlparse(href)
# удалить параметры URL GET, фрагменты URL и т. д.
href = parsed_href.scheme + "://" + parsed_href.netloc + parsed_href.path
Завершаем функцию.
Если URL-адрес недействителен/URL уже находится в int_url , следует перейти к следующей ссылке.
Если URL является внешней ссылкой, вывести его и добавить в глобальный набор ext_url и перейдти к следующей ссылке.
И наконец, после всех проверок получаем URL, являющийся внутренней ссылкой; выводим ее и добавляем в наборы urls и int_url
if not valid_url(href):
# недействительный URL
continue
if href in int_url:
# уже в наборе
continue
if domain_name not in href:
# внешняя ссылка
if href not in ext_url:
print(f"[!] External link: {href}")
ext_url.add(href)
continue
print(f"[*] Internal link: {href}")
urls.add(href)
int_url.add(href)
return urls
Напоминаю, что эта функция захватывает ссылки одной вэб-страницы.
Теперь создадим функцию, которая сканирует весь веб-сайт. Данная функция получает все ссылки на первой странице сайта, затем рекурсивно вызывается для перехода по всем извлеченным ссылкам. Параметр max_urls позволяет избежать зависания программы на больших сайтах при достижении определенного количества проверенных URL-адресов.
# Количество посещенных URL-адресов
visited_urls = 0
# Просматриваем веб-страницу и извлекаем все ссылки.
def crawl(url, max_urls=50):
# max_urls (int): количество макс. URL для сканирования
global visited_urls
visited_urls += 1
links = website_links(url)
for link in links:
if visited_urls > max_urls:
break
crawl(link, max_urls=max_urls)
Итак, проверим на сайте, к которому имеется разрешение, как все это работает:
if __name__ == "__main__":
crawl("https://newtechaudit.ru")
print("[+] Total External links:", len(ext_url))
print("[+] Total Internal links:", len(int_url))
print("[+] Total:", len(ext_url) + len(int_url))
Вот фрагмент результата работы программы:
Обратите внимание, что многократный запрос к одному и тому же сайту за короткий промежуток времени может привести к тому, что ваш IP-адрес будет заблокирован. Ссылка на оригинал поста.