#python #web-scraping #beautifulsoup #scrapy
#python #очистка веб-страниц #beautifulsoup #scrapy
Вопрос:
Я создаю программу, которая, учитывая стартовую страницу и целевую страницу в Википедии, переходит с начальной страницы на целевую страницу по гиперссылкам на каждой странице. Например, если у нас есть начальная страница A и целевая страница B, и A ссылается на C, который ссылается на B, мы можем попасть из A в B через A -> C -> B.
Я пробовал использовать beautiful soup, но я новичок в очистке веб-страниц. На данный момент я извлек html со страницы и отсортировал ссылки. Код, который у меня пока есть, таков:
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
html = urlopen("https://en.wikipedia.org/wiki/Web_Bot")
bs = BeautifulSoup(html, "lxml")
links = bs.find("div", {"id": "bodyContent"}).findAll("a",
href=re.compile("^(/wiki/)((?!:).)*$"))
for link in links:
if "href" in link.attrs:
print(link.attrs["href"])
Мой вопрос: как я могу перейти с одной страницы на другую по ссылкам на странице?
Комментарии:
1. Извините, что избегаю точного ответа на вопрос, но, возможно, вы можете выполнить свою задачу проще, используя существующее приложение для поиска веб-страниц, например
httrack
, для загрузки всех связанных страниц, а затем просто выполните итерацию кода синтаксического анализа по загруженным файлам?2. Просто повторяйте этот процесс для каждой ссылки, пока не дойдете до места назначения. Это может быть сделано либо с помощью бесконечного цикла, либо рекурсии.
Ответ №1:
В общем, то, чего вы пытаетесь достичь, не является тривиальной задачей. И есть несколько отдельных проблем, которые требуют решения.
Проблема 1: отслеживание всех ссылок, которые вы уже посетили, и тех, которые вам еще предстоит посетить
Проблема 2: знать, когда остановиться. Если веб-сайт, который вы просматриваете, довольно мал, то вы можете надеяться, что через некоторое время вы просто найдете все возможные ссылки и ваш обход будет завершен.
Проблема 3: Поиск пути, основанного на знании того, какая страница на какую связана. Теперь о возможных решениях:
Проблема 1. Решение A: Используйте очередь и набор. На каждом шаге помещайте ссылку на текущую страницу в набор посещенных ссылок, получайте ссылки со страницы, проверяйте, есть ли какие-либо из них уже в наборе посещенных ссылок, добавляйте новые ссылки в очередь. Затем выберите следующую ссылку для посещения из очереди. Довольно простой подход, особенно если ваш поисковый робот не требует времени. Это будет делать все последовательно. Одна страница за другой.
Проблема 1. Решение B: потратьте 1-2 часа на чтение о Scrapy и попробуйте реализовать обход с помощью Scrapy. Он будет выполнять многопоточность за вас, а также предоставит инструменты для поиска ссылок (аналогично тому, как вы делаете это в BeautifulSoup). Преимущество: уже реализовано множество функций, таких как экспорт в CSV, JSON для дальнейшей обработки, ведение журнала, очистка статистики и так далее.
Проблема 2. Решение A.Это зависит. Если вы ищете какой-либо конкретный путь, вы можете остановиться, как только достигнете целевой страницы, а затем вы можете восстановить путь от A до B.
Проблема 2. Решение B.В случае, если вы ищете кратчайший путь или возможность найти путь между A и B для любых заданных A и B, вы могли бы ограничить сканирование количеством переходов. Допустим, вы начинаете со страницы A, на ней есть ссылки на B1, B2 и B3. Вы посещаете их и присваиваете им порядковый номер 1. На этих страницах B есть ссылки на C1, C2, C3, C4, C5 — вы посещаете эти страницы и присваиваете им порядковый номер 2. Вы продолжаете, пока не достигнете порядкового номера X, что означает, что эти страницы находятся на расстоянии X переходов от вашей начальной страницы. Это гарантирует, что вы ограничите время обхода.
Проблема 3. Решение A.Когда вы переходите на страницу B1, B2, B3 со страницы A, вы «прикрепляете синтаксический анализ» с надписью «A». Это означает, что на эти страницы можно перейти со страницы A. Каждая новая страница, которую вы посещаете, также должна содержать информацию о том, откуда ее можно посетить. Затем вы используете алгоритмы DFS или BFS для поиска пути в этом наборе связанных страниц.
Проблема 3. Решение B.Вместо сохранения ссылки только на предыдущую страницу вы сохраняете список. Если вы посещаете B из A, ваша ссылка на B будет иметь «A» в качестве пути к ней. Но если вы перейдете на C из B, вы добавите B к существующему пути, и C будет содержать «A-> B» и так далее. Это означает, что в конечном итоге для каждой ссылки у вас будет путь от A до этой ссылки. Хорошо работает, если вас интересуют какие-либо пути от A к любой другой странице.
Проблема 3. Решение C.Для каждой страницы, когда вы извлекаете из нее все ссылки, вы создаете карту, где ваша страница является ключом, а список ссылок, который она содержит, является значением. Это несколько противоположно подходу A. Вместо дочерних элементов, имеющих ссылки на родительскую страницу, у вас есть родительская страница, перечисляющая ее дочерние элементы. В этом случае вы также можете использовать алгоритмы DFS или WFS для поиска пути между любыми двумя страницами.
Комментарии:
1. Я читал о scrapy и просматривал учебные пособия, стараясь сделать это как можно проще, просто перемещаясь между 2-3 страницами. Спасибо за подробный ответ, я рассмотрю каждую из этих проблем.
2. Чтобы найти правильное решение, я предлагаю вам ответить на эти вопросы: — вы хотите найти какой-либо (не обязательно кратчайший) путь между страницей A и страницей B? — вы хотите найти кратчайший путь между A и B? — вы хотите иметь возможность находить кратчайший путь между любыми двумя страницами?
Ответ №2:
Очистка «огромного» веб-сайта, такого как Википедия, требует «огромных» ресурсов. Я лично не верю, что это задача, которая может быть выполнена человеком с ограниченными ресурсами, или что на этот вопрос можно дать окончательный ответ, даже учитывая ноу-хау, в пределах словарного запаса ответа stack overflow. При этом следующий подход в моем ответе может работать на небольших сайтах с парой сотен страниц.
Подход:
-
Определите исходную и целевую страницы.
-
Начните сканирование с исходной страницы и рекурсивно просматривайте каждую ссылку, пока на конечной странице не останется ссылки, которую мы не просматривали ранее.
-
Сохраняйте каждую просмотренную страницу в словаре, скажем,
master_link_dict
сkey:value
парами в качествеcrawled page url
:links in that page
-
Не сканируйте страницы, которые мы просматривали раньше. Мы можем проверить, есть ли URL-адрес уже в
dictionary.keys()
перед обходом страницы. -
Когда мы находим страницу с
target url
присутствующим на ней, мы печатаем трассировку и завершаем работу. Цель ограничивается поиском пути отsource url
кtarget url
Код:
import requests
from bs4 import BeautifulSoup
import re
import pprint
source_page='/wiki/Web_Bot'
target_page='/wiki/Computer_Sciences_Corporation'
master_link_dict={}
#initialize trail with target
trail_reverse=[target_page]
def get_links(url):
html=requests.get('https://en.wikipedia.org' url)
soup = BeautifulSoup(html.text, "html.parser")
links = soup.find("div", {"id": "bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$"))
hrefs=[x['href'] for x in links]
return hrefs
def recursive_crawl(url):
#don't crawl again if the page has already been crawled
if url in master_link_dict.keys():
return
#get all urls in the current page
url_list=get_links(url)
#store as page:[list of urls] in the master dict
master_link_dict[url]=url_list
#if target page is found print trail
if target_page in url_list:
find_trail(url)
#crawl all urls of curret page
for item in url_list:
recursive_crawl(item)
def find_trail(url):
#append current url to trail reverse
trail_reverse.append(url)
#if current url is the source url print trail and exit
if url is source_page:
print('->'.join(trail_reverse[::-1]))
exit()
#if current url is in a page, get trail of that page
for page,url_list in master_link_dict.items():
if url in url_list:
find_trail(page)
recursive_crawl(source_page)
Вывод:
/wiki/Web_Bot->/wiki/Internet_bot->/wiki/Automated_bot->/wiki/Computer_science->/wiki/Computer_Sciences_Corporation
Примечания и отказ от ответственности:
-
Ответ, конечно, довольно упрощенный и не учитывает многие крайние случаи. Например. Что, если между двумя страницами A и B нет пути?
-
Я ответил в меру своих возможностей, но могут быть подходы получше.
-
Я не случайно выбрал
target url
. Я искал множество URL-адресов, которые находятся на расстоянии 3-6 страниц для тестирования. URL в коде является одним из них.